@openclaw/zalouser 2026.2.21 → 2026.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.2.22
4
+
5
+ ### Changes
6
+
7
+ - Version alignment with core OpenClaw release numbers.
8
+
3
9
  ## 2026.1.17-1
4
10
 
5
11
  - Initial version with full channel plugin support
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/zalouser",
3
- "version": "2026.2.21",
3
+ "version": "2026.2.23",
4
4
  "description": "OpenClaw Zalo Personal Account plugin via zca-cli",
5
5
  "type": "module",
6
6
  "dependencies": {
package/src/monitor.ts CHANGED
@@ -1,10 +1,20 @@
1
1
  import type { ChildProcess } from "node:child_process";
2
- import type { OpenClawConfig, MarkdownTableMode, RuntimeEnv } from "openclaw/plugin-sdk";
2
+ import type {
3
+ MarkdownTableMode,
4
+ OpenClawConfig,
5
+ OutboundReplyPayload,
6
+ RuntimeEnv,
7
+ } from "openclaw/plugin-sdk";
3
8
  import {
4
9
  createReplyPrefixOptions,
10
+ resolveOutboundMediaUrls,
5
11
  mergeAllowlist,
12
+ resolveOpenProviderRuntimeGroupPolicy,
13
+ resolveDefaultGroupPolicy,
6
14
  resolveSenderCommandAuthorization,
15
+ sendMediaWithLeadingCaption,
7
16
  summarizeMapping,
17
+ warnMissingProviderGroupPolicyFallbackOnce,
8
18
  } from "openclaw/plugin-sdk";
9
19
  import { getZalouserRuntime } from "./runtime.js";
10
20
  import { sendMessageZalouser } from "./send.js";
@@ -177,8 +187,18 @@ async function processMessage(
177
187
  const groupName = metadata?.threadName ?? "";
178
188
  const chatId = threadId;
179
189
 
180
- const defaultGroupPolicy = config.channels?.defaults?.groupPolicy;
181
- const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "open";
190
+ const defaultGroupPolicy = resolveDefaultGroupPolicy(config);
191
+ const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
192
+ providerConfigPresent: config.channels?.zalouser !== undefined,
193
+ groupPolicy: account.config.groupPolicy,
194
+ defaultGroupPolicy,
195
+ });
196
+ warnMissingProviderGroupPolicyFallbackOnce({
197
+ providerMissingFallbackApplied,
198
+ providerKey: "zalouser",
199
+ accountId: account.accountId,
200
+ log: (message) => logVerbose(core, runtime, message),
201
+ });
182
202
  const groups = account.config.groups ?? {};
183
203
  if (isGroup) {
184
204
  if (groupPolicy === "disabled") {
@@ -379,7 +399,7 @@ async function processMessage(
379
399
  }
380
400
 
381
401
  async function deliverZalouserReply(params: {
382
- payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string };
402
+ payload: OutboundReplyPayload;
383
403
  profile: string;
384
404
  chatId: string;
385
405
  isGroup: boolean;
@@ -395,29 +415,23 @@ async function deliverZalouserReply(params: {
395
415
  const tableMode = params.tableMode ?? "code";
396
416
  const text = core.channel.text.convertMarkdownTables(payload.text ?? "", tableMode);
397
417
 
398
- const mediaList = payload.mediaUrls?.length
399
- ? payload.mediaUrls
400
- : payload.mediaUrl
401
- ? [payload.mediaUrl]
402
- : [];
403
-
404
- if (mediaList.length > 0) {
405
- let first = true;
406
- for (const mediaUrl of mediaList) {
407
- const caption = first ? text : undefined;
408
- first = false;
409
- try {
410
- logVerbose(core, runtime, `Sending media to ${chatId}`);
411
- await sendMessageZalouser(chatId, caption ?? "", {
412
- profile,
413
- mediaUrl,
414
- isGroup,
415
- });
416
- statusSink?.({ lastOutboundAt: Date.now() });
417
- } catch (err) {
418
- runtime.error(`Zalouser media send failed: ${String(err)}`);
419
- }
420
- }
418
+ const sentMedia = await sendMediaWithLeadingCaption({
419
+ mediaUrls: resolveOutboundMediaUrls(payload),
420
+ caption: text,
421
+ send: async ({ mediaUrl, caption }) => {
422
+ logVerbose(core, runtime, `Sending media to ${chatId}`);
423
+ await sendMessageZalouser(chatId, caption ?? "", {
424
+ profile,
425
+ mediaUrl,
426
+ isGroup,
427
+ });
428
+ statusSink?.({ lastOutboundAt: Date.now() });
429
+ },
430
+ onError: (error) => {
431
+ runtime.error(`Zalouser media send failed: ${String(error)}`);
432
+ },
433
+ });
434
+ if (sentMedia) {
421
435
  return;
422
436
  }
423
437
 
package/src/onboarding.ts CHANGED
@@ -23,6 +23,45 @@ import { runZca, runZcaInteractive, checkZcaInstalled, parseJsonOutput } from ".
23
23
 
24
24
  const channel = "zalouser" as const;
25
25
 
26
+ function setZalouserAccountScopedConfig(
27
+ cfg: OpenClawConfig,
28
+ accountId: string,
29
+ defaultPatch: Record<string, unknown>,
30
+ accountPatch: Record<string, unknown> = defaultPatch,
31
+ ): OpenClawConfig {
32
+ if (accountId === DEFAULT_ACCOUNT_ID) {
33
+ return {
34
+ ...cfg,
35
+ channels: {
36
+ ...cfg.channels,
37
+ zalouser: {
38
+ ...cfg.channels?.zalouser,
39
+ enabled: true,
40
+ ...defaultPatch,
41
+ },
42
+ },
43
+ } as OpenClawConfig;
44
+ }
45
+ return {
46
+ ...cfg,
47
+ channels: {
48
+ ...cfg.channels,
49
+ zalouser: {
50
+ ...cfg.channels?.zalouser,
51
+ enabled: true,
52
+ accounts: {
53
+ ...cfg.channels?.zalouser?.accounts,
54
+ [accountId]: {
55
+ ...cfg.channels?.zalouser?.accounts?.[accountId],
56
+ enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
57
+ ...accountPatch,
58
+ },
59
+ },
60
+ },
61
+ },
62
+ } as OpenClawConfig;
63
+ }
64
+
26
65
  function setZalouserDmPolicy(
27
66
  cfg: OpenClawConfig,
28
67
  dmPolicy: "pairing" | "allowlist" | "open" | "disabled",
@@ -123,40 +162,10 @@ async function promptZalouserAllowFrom(params: {
123
162
  continue;
124
163
  }
125
164
  const unique = mergeAllowFromEntries(existingAllowFrom, results.filter(Boolean) as string[]);
126
- if (accountId === DEFAULT_ACCOUNT_ID) {
127
- return {
128
- ...cfg,
129
- channels: {
130
- ...cfg.channels,
131
- zalouser: {
132
- ...cfg.channels?.zalouser,
133
- enabled: true,
134
- dmPolicy: "allowlist",
135
- allowFrom: unique,
136
- },
137
- },
138
- } as OpenClawConfig;
139
- }
140
-
141
- return {
142
- ...cfg,
143
- channels: {
144
- ...cfg.channels,
145
- zalouser: {
146
- ...cfg.channels?.zalouser,
147
- enabled: true,
148
- accounts: {
149
- ...cfg.channels?.zalouser?.accounts,
150
- [accountId]: {
151
- ...cfg.channels?.zalouser?.accounts?.[accountId],
152
- enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
153
- dmPolicy: "allowlist",
154
- allowFrom: unique,
155
- },
156
- },
157
- },
158
- },
159
- } as OpenClawConfig;
165
+ return setZalouserAccountScopedConfig(cfg, accountId, {
166
+ dmPolicy: "allowlist",
167
+ allowFrom: unique,
168
+ });
160
169
  }
161
170
  }
162
171
 
@@ -165,37 +174,9 @@ function setZalouserGroupPolicy(
165
174
  accountId: string,
166
175
  groupPolicy: "open" | "allowlist" | "disabled",
167
176
  ): OpenClawConfig {
168
- if (accountId === DEFAULT_ACCOUNT_ID) {
169
- return {
170
- ...cfg,
171
- channels: {
172
- ...cfg.channels,
173
- zalouser: {
174
- ...cfg.channels?.zalouser,
175
- enabled: true,
176
- groupPolicy,
177
- },
178
- },
179
- } as OpenClawConfig;
180
- }
181
- return {
182
- ...cfg,
183
- channels: {
184
- ...cfg.channels,
185
- zalouser: {
186
- ...cfg.channels?.zalouser,
187
- enabled: true,
188
- accounts: {
189
- ...cfg.channels?.zalouser?.accounts,
190
- [accountId]: {
191
- ...cfg.channels?.zalouser?.accounts?.[accountId],
192
- enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
193
- groupPolicy,
194
- },
195
- },
196
- },
197
- },
198
- } as OpenClawConfig;
177
+ return setZalouserAccountScopedConfig(cfg, accountId, {
178
+ groupPolicy,
179
+ });
199
180
  }
200
181
 
201
182
  function setZalouserGroupAllowlist(
@@ -204,37 +185,9 @@ function setZalouserGroupAllowlist(
204
185
  groupKeys: string[],
205
186
  ): OpenClawConfig {
206
187
  const groups = Object.fromEntries(groupKeys.map((key) => [key, { allow: true }]));
207
- if (accountId === DEFAULT_ACCOUNT_ID) {
208
- return {
209
- ...cfg,
210
- channels: {
211
- ...cfg.channels,
212
- zalouser: {
213
- ...cfg.channels?.zalouser,
214
- enabled: true,
215
- groups,
216
- },
217
- },
218
- } as OpenClawConfig;
219
- }
220
- return {
221
- ...cfg,
222
- channels: {
223
- ...cfg.channels,
224
- zalouser: {
225
- ...cfg.channels?.zalouser,
226
- enabled: true,
227
- accounts: {
228
- ...cfg.channels?.zalouser?.accounts,
229
- [accountId]: {
230
- ...cfg.channels?.zalouser?.accounts?.[accountId],
231
- enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
232
- groups,
233
- },
234
- },
235
- },
236
- },
237
- } as OpenClawConfig;
188
+ return setZalouserAccountScopedConfig(cfg, accountId, {
189
+ groups,
190
+ });
238
191
  }
239
192
 
240
193
  async function resolveZalouserGroups(params: {
@@ -403,38 +356,12 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
403
356
  }
404
357
 
405
358
  // Enable the channel
406
- if (accountId === DEFAULT_ACCOUNT_ID) {
407
- next = {
408
- ...next,
409
- channels: {
410
- ...next.channels,
411
- zalouser: {
412
- ...next.channels?.zalouser,
413
- enabled: true,
414
- profile: account.profile !== "default" ? account.profile : undefined,
415
- },
416
- },
417
- } as OpenClawConfig;
418
- } else {
419
- next = {
420
- ...next,
421
- channels: {
422
- ...next.channels,
423
- zalouser: {
424
- ...next.channels?.zalouser,
425
- enabled: true,
426
- accounts: {
427
- ...next.channels?.zalouser?.accounts,
428
- [accountId]: {
429
- ...next.channels?.zalouser?.accounts?.[accountId],
430
- enabled: true,
431
- profile: account.profile,
432
- },
433
- },
434
- },
435
- },
436
- } as OpenClawConfig;
437
- }
359
+ next = setZalouserAccountScopedConfig(
360
+ next,
361
+ accountId,
362
+ { profile: account.profile !== "default" ? account.profile : undefined },
363
+ { profile: account.profile, enabled: true },
364
+ );
438
365
 
439
366
  if (forceAllowFrom) {
440
367
  next = await promptZalouserAllowFrom({
@@ -447,7 +374,7 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
447
374
  const accessConfig = await promptChannelAccessConfig({
448
375
  prompter,
449
376
  label: "Zalo groups",
450
- currentPolicy: account.config.groupPolicy ?? "open",
377
+ currentPolicy: account.config.groupPolicy ?? "allowlist",
451
378
  currentEntries: Object.keys(account.config.groups ?? {}),
452
379
  placeholder: "Family, Work, 123456789",
453
380
  updatePrompt: Boolean(account.config.groups),
@@ -0,0 +1,156 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import {
3
+ sendImageZalouser,
4
+ sendLinkZalouser,
5
+ sendMessageZalouser,
6
+ type ZalouserSendResult,
7
+ } from "./send.js";
8
+ import { runZca } from "./zca.js";
9
+
10
+ vi.mock("./zca.js", () => ({
11
+ runZca: vi.fn(),
12
+ }));
13
+
14
+ const mockRunZca = vi.mocked(runZca);
15
+ const originalZcaProfile = process.env.ZCA_PROFILE;
16
+
17
+ function okResult(stdout = "message_id: msg-1") {
18
+ return {
19
+ ok: true,
20
+ stdout,
21
+ stderr: "",
22
+ exitCode: 0,
23
+ };
24
+ }
25
+
26
+ function failResult(stderr = "") {
27
+ return {
28
+ ok: false,
29
+ stdout: "",
30
+ stderr,
31
+ exitCode: 1,
32
+ };
33
+ }
34
+
35
+ describe("zalouser send helpers", () => {
36
+ beforeEach(() => {
37
+ mockRunZca.mockReset();
38
+ delete process.env.ZCA_PROFILE;
39
+ });
40
+
41
+ afterEach(() => {
42
+ if (originalZcaProfile) {
43
+ process.env.ZCA_PROFILE = originalZcaProfile;
44
+ return;
45
+ }
46
+ delete process.env.ZCA_PROFILE;
47
+ });
48
+
49
+ it("returns validation error when thread id is missing", async () => {
50
+ const result = await sendMessageZalouser("", "hello");
51
+ expect(result).toEqual({
52
+ ok: false,
53
+ error: "No threadId provided",
54
+ } satisfies ZalouserSendResult);
55
+ expect(mockRunZca).not.toHaveBeenCalled();
56
+ });
57
+
58
+ it("builds text send command with truncation and group flag", async () => {
59
+ mockRunZca.mockResolvedValueOnce(okResult("message id: mid-123"));
60
+
61
+ const result = await sendMessageZalouser(" thread-1 ", "x".repeat(2200), {
62
+ profile: "profile-a",
63
+ isGroup: true,
64
+ });
65
+
66
+ expect(mockRunZca).toHaveBeenCalledWith(["msg", "send", "thread-1", "x".repeat(2000), "-g"], {
67
+ profile: "profile-a",
68
+ });
69
+ expect(result).toEqual({ ok: true, messageId: "mid-123" });
70
+ });
71
+
72
+ it("routes media sends from sendMessage and keeps text as caption", async () => {
73
+ mockRunZca.mockResolvedValueOnce(okResult());
74
+
75
+ await sendMessageZalouser("thread-2", "media caption", {
76
+ profile: "profile-b",
77
+ mediaUrl: "https://cdn.example.com/video.mp4",
78
+ isGroup: true,
79
+ });
80
+
81
+ expect(mockRunZca).toHaveBeenCalledWith(
82
+ [
83
+ "msg",
84
+ "video",
85
+ "thread-2",
86
+ "-u",
87
+ "https://cdn.example.com/video.mp4",
88
+ "-m",
89
+ "media caption",
90
+ "-g",
91
+ ],
92
+ { profile: "profile-b" },
93
+ );
94
+ });
95
+
96
+ it("maps audio media to voice command", async () => {
97
+ mockRunZca.mockResolvedValueOnce(okResult());
98
+
99
+ await sendMessageZalouser("thread-3", "", {
100
+ profile: "profile-c",
101
+ mediaUrl: "https://cdn.example.com/clip.mp3",
102
+ });
103
+
104
+ expect(mockRunZca).toHaveBeenCalledWith(
105
+ ["msg", "voice", "thread-3", "-u", "https://cdn.example.com/clip.mp3"],
106
+ { profile: "profile-c" },
107
+ );
108
+ });
109
+
110
+ it("builds image command with caption and returns fallback error", async () => {
111
+ mockRunZca.mockResolvedValueOnce(failResult(""));
112
+
113
+ const result = await sendImageZalouser("thread-4", " https://cdn.example.com/img.png ", {
114
+ profile: "profile-d",
115
+ caption: "caption text",
116
+ isGroup: true,
117
+ });
118
+
119
+ expect(mockRunZca).toHaveBeenCalledWith(
120
+ [
121
+ "msg",
122
+ "image",
123
+ "thread-4",
124
+ "-u",
125
+ "https://cdn.example.com/img.png",
126
+ "-m",
127
+ "caption text",
128
+ "-g",
129
+ ],
130
+ { profile: "profile-d" },
131
+ );
132
+ expect(result).toEqual({ ok: false, error: "Failed to send image" });
133
+ });
134
+
135
+ it("uses env profile fallback and builds link command", async () => {
136
+ process.env.ZCA_PROFILE = "env-profile";
137
+ mockRunZca.mockResolvedValueOnce(okResult("abc123"));
138
+
139
+ const result = await sendLinkZalouser("thread-5", " https://openclaw.ai ", { isGroup: true });
140
+
141
+ expect(mockRunZca).toHaveBeenCalledWith(
142
+ ["msg", "link", "thread-5", "https://openclaw.ai", "-g"],
143
+ { profile: "env-profile" },
144
+ );
145
+ expect(result).toEqual({ ok: true, messageId: "abc123" });
146
+ });
147
+
148
+ it("returns caught command errors", async () => {
149
+ mockRunZca.mockRejectedValueOnce(new Error("zca unavailable"));
150
+
151
+ await expect(sendLinkZalouser("thread-6", "https://openclaw.ai")).resolves.toEqual({
152
+ ok: false,
153
+ error: "zca unavailable",
154
+ });
155
+ });
156
+ });
package/src/send.ts CHANGED
@@ -13,12 +13,41 @@ export type ZalouserSendResult = {
13
13
  error?: string;
14
14
  };
15
15
 
16
+ function resolveProfile(options: ZalouserSendOptions): string {
17
+ return options.profile || process.env.ZCA_PROFILE || "default";
18
+ }
19
+
20
+ function appendCaptionAndGroupFlags(args: string[], options: ZalouserSendOptions): void {
21
+ if (options.caption) {
22
+ args.push("-m", options.caption.slice(0, 2000));
23
+ }
24
+ if (options.isGroup) {
25
+ args.push("-g");
26
+ }
27
+ }
28
+
29
+ async function runSendCommand(
30
+ args: string[],
31
+ profile: string,
32
+ fallbackError: string,
33
+ ): Promise<ZalouserSendResult> {
34
+ try {
35
+ const result = await runZca(args, { profile });
36
+ if (result.ok) {
37
+ return { ok: true, messageId: extractMessageId(result.stdout) };
38
+ }
39
+ return { ok: false, error: result.stderr || fallbackError };
40
+ } catch (err) {
41
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
42
+ }
43
+ }
44
+
16
45
  export async function sendMessageZalouser(
17
46
  threadId: string,
18
47
  text: string,
19
48
  options: ZalouserSendOptions = {},
20
49
  ): Promise<ZalouserSendResult> {
21
- const profile = options.profile || process.env.ZCA_PROFILE || "default";
50
+ const profile = resolveProfile(options);
22
51
 
23
52
  if (!threadId?.trim()) {
24
53
  return { ok: false, error: "No threadId provided" };
@@ -38,17 +67,7 @@ export async function sendMessageZalouser(
38
67
  args.push("-g");
39
68
  }
40
69
 
41
- try {
42
- const result = await runZca(args, { profile });
43
-
44
- if (result.ok) {
45
- return { ok: true, messageId: extractMessageId(result.stdout) };
46
- }
47
-
48
- return { ok: false, error: result.stderr || "Failed to send message" };
49
- } catch (err) {
50
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
51
- }
70
+ return runSendCommand(args, profile, "Failed to send message");
52
71
  }
53
72
 
54
73
  async function sendMediaZalouser(
@@ -56,7 +75,7 @@ async function sendMediaZalouser(
56
75
  mediaUrl: string,
57
76
  options: ZalouserSendOptions = {},
58
77
  ): Promise<ZalouserSendResult> {
59
- const profile = options.profile || process.env.ZCA_PROFILE || "default";
78
+ const profile = resolveProfile(options);
60
79
 
61
80
  if (!threadId?.trim()) {
62
81
  return { ok: false, error: "No threadId provided" };
@@ -78,24 +97,8 @@ async function sendMediaZalouser(
78
97
  }
79
98
 
80
99
  const args = ["msg", command, threadId.trim(), "-u", mediaUrl.trim()];
81
- if (options.caption) {
82
- args.push("-m", options.caption.slice(0, 2000));
83
- }
84
- if (options.isGroup) {
85
- args.push("-g");
86
- }
87
-
88
- try {
89
- const result = await runZca(args, { profile });
90
-
91
- if (result.ok) {
92
- return { ok: true, messageId: extractMessageId(result.stdout) };
93
- }
94
-
95
- return { ok: false, error: result.stderr || `Failed to send ${command}` };
96
- } catch (err) {
97
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
98
- }
100
+ appendCaptionAndGroupFlags(args, options);
101
+ return runSendCommand(args, profile, `Failed to send ${command}`);
99
102
  }
100
103
 
101
104
  export async function sendImageZalouser(
@@ -103,24 +106,10 @@ export async function sendImageZalouser(
103
106
  imageUrl: string,
104
107
  options: ZalouserSendOptions = {},
105
108
  ): Promise<ZalouserSendResult> {
106
- const profile = options.profile || process.env.ZCA_PROFILE || "default";
109
+ const profile = resolveProfile(options);
107
110
  const args = ["msg", "image", threadId.trim(), "-u", imageUrl.trim()];
108
- if (options.caption) {
109
- args.push("-m", options.caption.slice(0, 2000));
110
- }
111
- if (options.isGroup) {
112
- args.push("-g");
113
- }
114
-
115
- try {
116
- const result = await runZca(args, { profile });
117
- if (result.ok) {
118
- return { ok: true, messageId: extractMessageId(result.stdout) };
119
- }
120
- return { ok: false, error: result.stderr || "Failed to send image" };
121
- } catch (err) {
122
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
123
- }
111
+ appendCaptionAndGroupFlags(args, options);
112
+ return runSendCommand(args, profile, "Failed to send image");
124
113
  }
125
114
 
126
115
  export async function sendLinkZalouser(
@@ -128,21 +117,13 @@ export async function sendLinkZalouser(
128
117
  url: string,
129
118
  options: ZalouserSendOptions = {},
130
119
  ): Promise<ZalouserSendResult> {
131
- const profile = options.profile || process.env.ZCA_PROFILE || "default";
120
+ const profile = resolveProfile(options);
132
121
  const args = ["msg", "link", threadId.trim(), url.trim()];
133
122
  if (options.isGroup) {
134
123
  args.push("-g");
135
124
  }
136
125
 
137
- try {
138
- const result = await runZca(args, { profile });
139
- if (result.ok) {
140
- return { ok: true, messageId: extractMessageId(result.stdout) };
141
- }
142
- return { ok: false, error: result.stderr || "Failed to send link" };
143
- } catch (err) {
144
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
145
- }
126
+ return runSendCommand(args, profile, "Failed to send link");
146
127
  }
147
128
 
148
129
  function extractMessageId(stdout: string): string | undefined {
package/src/types.ts CHANGED
@@ -68,35 +68,30 @@ export type ListenOptions = CommonOptions & {
68
68
  prefix?: string;
69
69
  };
70
70
 
71
- export type ZalouserAccountConfig = {
71
+ type ZalouserToolConfig = { allow?: string[]; deny?: string[] };
72
+
73
+ type ZalouserGroupConfig = {
74
+ allow?: boolean;
72
75
  enabled?: boolean;
73
- name?: string;
74
- profile?: string;
75
- dmPolicy?: "pairing" | "allowlist" | "open" | "disabled";
76
- allowFrom?: Array<string | number>;
77
- groupPolicy?: "open" | "allowlist" | "disabled";
78
- groups?: Record<
79
- string,
80
- { allow?: boolean; enabled?: boolean; tools?: { allow?: string[]; deny?: string[] } }
81
- >;
82
- messagePrefix?: string;
83
- responsePrefix?: string;
76
+ tools?: ZalouserToolConfig;
84
77
  };
85
78
 
86
- export type ZalouserConfig = {
79
+ type ZalouserSharedConfig = {
87
80
  enabled?: boolean;
88
81
  name?: string;
89
82
  profile?: string;
90
- defaultAccount?: string;
91
83
  dmPolicy?: "pairing" | "allowlist" | "open" | "disabled";
92
84
  allowFrom?: Array<string | number>;
93
85
  groupPolicy?: "open" | "allowlist" | "disabled";
94
- groups?: Record<
95
- string,
96
- { allow?: boolean; enabled?: boolean; tools?: { allow?: string[]; deny?: string[] } }
97
- >;
86
+ groups?: Record<string, ZalouserGroupConfig>;
98
87
  messagePrefix?: string;
99
88
  responsePrefix?: string;
89
+ };
90
+
91
+ export type ZalouserAccountConfig = ZalouserSharedConfig;
92
+
93
+ export type ZalouserConfig = ZalouserSharedConfig & {
94
+ defaultAccount?: string;
100
95
  accounts?: Record<string, ZalouserAccountConfig>;
101
96
  };
102
97