@openclaw/zalouser 2026.2.21 → 2026.2.22
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 +6 -0
- package/package.json +1 -1
- package/src/monitor.ts +15 -2
- package/src/onboarding.ts +56 -129
- package/src/send.test.ts +156 -0
- package/src/send.ts +39 -58
- package/src/types.ts +13 -18
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/monitor.ts
CHANGED
|
@@ -3,8 +3,11 @@ import type { OpenClawConfig, MarkdownTableMode, RuntimeEnv } from "openclaw/plu
|
|
|
3
3
|
import {
|
|
4
4
|
createReplyPrefixOptions,
|
|
5
5
|
mergeAllowlist,
|
|
6
|
+
resolveOpenProviderRuntimeGroupPolicy,
|
|
7
|
+
resolveDefaultGroupPolicy,
|
|
6
8
|
resolveSenderCommandAuthorization,
|
|
7
9
|
summarizeMapping,
|
|
10
|
+
warnMissingProviderGroupPolicyFallbackOnce,
|
|
8
11
|
} from "openclaw/plugin-sdk";
|
|
9
12
|
import { getZalouserRuntime } from "./runtime.js";
|
|
10
13
|
import { sendMessageZalouser } from "./send.js";
|
|
@@ -177,8 +180,18 @@ async function processMessage(
|
|
|
177
180
|
const groupName = metadata?.threadName ?? "";
|
|
178
181
|
const chatId = threadId;
|
|
179
182
|
|
|
180
|
-
const defaultGroupPolicy = config
|
|
181
|
-
const
|
|
183
|
+
const defaultGroupPolicy = resolveDefaultGroupPolicy(config);
|
|
184
|
+
const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
|
|
185
|
+
providerConfigPresent: config.channels?.zalouser !== undefined,
|
|
186
|
+
groupPolicy: account.config.groupPolicy,
|
|
187
|
+
defaultGroupPolicy,
|
|
188
|
+
});
|
|
189
|
+
warnMissingProviderGroupPolicyFallbackOnce({
|
|
190
|
+
providerMissingFallbackApplied,
|
|
191
|
+
providerKey: "zalouser",
|
|
192
|
+
accountId: account.accountId,
|
|
193
|
+
log: (message) => logVerbose(core, runtime, message),
|
|
194
|
+
});
|
|
182
195
|
const groups = account.config.groups ?? {};
|
|
183
196
|
if (isGroup) {
|
|
184
197
|
if (groupPolicy === "disabled") {
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
407
|
-
next
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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 ?? "
|
|
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),
|
package/src/send.test.ts
ADDED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
82
|
-
|
|
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
|
|
109
|
+
const profile = resolveProfile(options);
|
|
107
110
|
const args = ["msg", "image", threadId.trim(), "-u", imageUrl.trim()];
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
71
|
+
type ZalouserToolConfig = { allow?: string[]; deny?: string[] };
|
|
72
|
+
|
|
73
|
+
type ZalouserGroupConfig = {
|
|
74
|
+
allow?: boolean;
|
|
72
75
|
enabled?: boolean;
|
|
73
|
-
|
|
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
|
-
|
|
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
|
|