@openclaw/zalouser 2026.3.7 → 2026.3.10
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 +24 -0
- package/package.json +6 -1
- package/src/channel.directory.test.ts +72 -0
- package/src/channel.sendpayload.test.ts +72 -54
- package/src/channel.ts +122 -14
- package/src/config-schema.ts +12 -9
- package/src/monitor.group-gating.test.ts +379 -11
- package/src/monitor.ts +412 -114
- package/src/onboarding.ts +8 -31
- package/src/runtime.ts +4 -12
- package/src/types.ts +3 -0
- package/src/zalo-js.ts +269 -22
- package/src/zca-client.ts +45 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.3.10
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Version alignment with core OpenClaw release numbers.
|
|
8
|
+
|
|
9
|
+
## 2026.3.9
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
- Version alignment with core OpenClaw release numbers.
|
|
14
|
+
|
|
15
|
+
## 2026.3.8-beta.1
|
|
16
|
+
|
|
17
|
+
### Changes
|
|
18
|
+
|
|
19
|
+
- Version alignment with core OpenClaw release numbers.
|
|
20
|
+
|
|
21
|
+
## 2026.3.8
|
|
22
|
+
|
|
23
|
+
### Changes
|
|
24
|
+
|
|
25
|
+
- Version alignment with core OpenClaw release numbers.
|
|
26
|
+
|
|
3
27
|
## 2026.3.7
|
|
4
28
|
|
|
5
29
|
### Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/zalouser",
|
|
3
|
-
"version": "2026.3.
|
|
3
|
+
"version": "2026.3.10",
|
|
4
4
|
"description": "OpenClaw Zalo Personal Account plugin via native zca-js integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
"npmSpec": "@openclaw/zalouser",
|
|
30
30
|
"localPath": "extensions/zalouser",
|
|
31
31
|
"defaultChoice": "npm"
|
|
32
|
+
},
|
|
33
|
+
"releaseChecks": {
|
|
34
|
+
"rootDependencyMirrorAllowlist": [
|
|
35
|
+
"zca-js"
|
|
36
|
+
]
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { RuntimeEnv } from "openclaw/plugin-sdk/zalouser";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
const listZaloGroupMembersMock = vi.hoisted(() => vi.fn(async () => []));
|
|
5
|
+
|
|
6
|
+
vi.mock("./zalo-js.js", async (importOriginal) => {
|
|
7
|
+
const actual = (await importOriginal()) as Record<string, unknown>;
|
|
8
|
+
return {
|
|
9
|
+
...actual,
|
|
10
|
+
listZaloGroupMembers: listZaloGroupMembersMock,
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
vi.mock("./accounts.js", async (importOriginal) => {
|
|
15
|
+
const actual = (await importOriginal()) as Record<string, unknown>;
|
|
16
|
+
return {
|
|
17
|
+
...actual,
|
|
18
|
+
resolveZalouserAccountSync: () => ({
|
|
19
|
+
accountId: "default",
|
|
20
|
+
profile: "default",
|
|
21
|
+
name: "test",
|
|
22
|
+
enabled: true,
|
|
23
|
+
authenticated: true,
|
|
24
|
+
config: {},
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
import { zalouserPlugin } from "./channel.js";
|
|
30
|
+
|
|
31
|
+
const runtimeStub: RuntimeEnv = {
|
|
32
|
+
log: vi.fn(),
|
|
33
|
+
error: vi.fn(),
|
|
34
|
+
exit: ((code: number): never => {
|
|
35
|
+
throw new Error(`exit ${code}`);
|
|
36
|
+
}) as RuntimeEnv["exit"],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe("zalouser directory group members", () => {
|
|
40
|
+
it("accepts prefixed group ids from directory groups list output", async () => {
|
|
41
|
+
await zalouserPlugin.directory!.listGroupMembers!({
|
|
42
|
+
cfg: {},
|
|
43
|
+
accountId: "default",
|
|
44
|
+
groupId: "group:1471383327500481391",
|
|
45
|
+
runtime: runtimeStub,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(listZaloGroupMembersMock).toHaveBeenCalledWith("default", "1471383327500481391");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("keeps backward compatibility for raw group ids", async () => {
|
|
52
|
+
await zalouserPlugin.directory!.listGroupMembers!({
|
|
53
|
+
cfg: {},
|
|
54
|
+
accountId: "default",
|
|
55
|
+
groupId: "1471383327500481391",
|
|
56
|
+
runtime: runtimeStub,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(listZaloGroupMembersMock).toHaveBeenCalledWith("default", "1471383327500481391");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("accepts provider-native g- group ids without stripping the prefix", async () => {
|
|
63
|
+
await zalouserPlugin.directory!.listGroupMembers!({
|
|
64
|
+
cfg: {},
|
|
65
|
+
accountId: "default",
|
|
66
|
+
groupId: "g-1471383327500481391",
|
|
67
|
+
runtime: runtimeStub,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(listZaloGroupMembersMock).toHaveBeenCalledWith("default", "g-1471383327500481391");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ReplyPayload } from "openclaw/plugin-sdk/zalouser";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
installSendPayloadContractSuite,
|
|
5
|
+
primeSendMock,
|
|
6
|
+
} from "../../../src/test-utils/send-payload-contract.js";
|
|
3
7
|
import { zalouserPlugin } from "./channel.js";
|
|
4
8
|
|
|
5
9
|
vi.mock("./send.js", () => ({
|
|
@@ -24,7 +28,7 @@ vi.mock("./accounts.js", async (importOriginal) => {
|
|
|
24
28
|
function baseCtx(payload: ReplyPayload) {
|
|
25
29
|
return {
|
|
26
30
|
cfg: {},
|
|
27
|
-
to: "987654321",
|
|
31
|
+
to: "user:987654321",
|
|
28
32
|
text: "",
|
|
29
33
|
payload,
|
|
30
34
|
};
|
|
@@ -40,78 +44,92 @@ describe("zalouserPlugin outbound sendPayload", () => {
|
|
|
40
44
|
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-1" });
|
|
41
45
|
});
|
|
42
46
|
|
|
43
|
-
it("
|
|
44
|
-
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-
|
|
47
|
+
it("group target delegates with isGroup=true and stripped threadId", async () => {
|
|
48
|
+
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-g1" });
|
|
45
49
|
|
|
46
|
-
const result = await zalouserPlugin.outbound!.sendPayload!(
|
|
50
|
+
const result = await zalouserPlugin.outbound!.sendPayload!({
|
|
51
|
+
...baseCtx({ text: "hello group" }),
|
|
52
|
+
to: "group:1471383327500481391",
|
|
53
|
+
});
|
|
47
54
|
|
|
48
|
-
expect(mockedSend).toHaveBeenCalledWith(
|
|
49
|
-
|
|
55
|
+
expect(mockedSend).toHaveBeenCalledWith(
|
|
56
|
+
"1471383327500481391",
|
|
57
|
+
"hello group",
|
|
58
|
+
expect.objectContaining({ isGroup: true }),
|
|
59
|
+
);
|
|
60
|
+
expect(result).toMatchObject({ channel: "zalouser", messageId: "zlu-g1" });
|
|
50
61
|
});
|
|
51
62
|
|
|
52
|
-
it("
|
|
53
|
-
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-
|
|
63
|
+
it("treats bare numeric targets as direct chats for backward compatibility", async () => {
|
|
64
|
+
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-d1" });
|
|
54
65
|
|
|
55
|
-
const result = await zalouserPlugin.outbound!.sendPayload!(
|
|
56
|
-
baseCtx({ text: "
|
|
57
|
-
|
|
66
|
+
const result = await zalouserPlugin.outbound!.sendPayload!({
|
|
67
|
+
...baseCtx({ text: "hello" }),
|
|
68
|
+
to: "987654321",
|
|
69
|
+
});
|
|
58
70
|
|
|
59
71
|
expect(mockedSend).toHaveBeenCalledWith(
|
|
60
72
|
"987654321",
|
|
61
|
-
"
|
|
62
|
-
expect.objectContaining({
|
|
73
|
+
"hello",
|
|
74
|
+
expect.objectContaining({ isGroup: false }),
|
|
63
75
|
);
|
|
64
|
-
expect(result).toMatchObject({ channel: "zalouser" });
|
|
76
|
+
expect(result).toMatchObject({ channel: "zalouser", messageId: "zlu-d1" });
|
|
65
77
|
});
|
|
66
78
|
|
|
67
|
-
it("
|
|
68
|
-
mockedSend
|
|
69
|
-
.mockResolvedValueOnce({ ok: true, messageId: "zlu-1" })
|
|
70
|
-
.mockResolvedValueOnce({ ok: true, messageId: "zlu-2" });
|
|
79
|
+
it("preserves provider-native group ids when sending to raw g- targets", async () => {
|
|
80
|
+
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-g-native" });
|
|
71
81
|
|
|
72
|
-
const result = await zalouserPlugin.outbound!.sendPayload!(
|
|
73
|
-
baseCtx({
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}),
|
|
77
|
-
);
|
|
82
|
+
const result = await zalouserPlugin.outbound!.sendPayload!({
|
|
83
|
+
...baseCtx({ text: "hello native group" }),
|
|
84
|
+
to: "g-1471383327500481391",
|
|
85
|
+
});
|
|
78
86
|
|
|
79
|
-
expect(mockedSend).
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"caption",
|
|
84
|
-
expect.objectContaining({ mediaUrl: "https://example.com/1.jpg" }),
|
|
85
|
-
);
|
|
86
|
-
expect(mockedSend).toHaveBeenNthCalledWith(
|
|
87
|
-
2,
|
|
88
|
-
"987654321",
|
|
89
|
-
"",
|
|
90
|
-
expect.objectContaining({ mediaUrl: "https://example.com/2.jpg" }),
|
|
87
|
+
expect(mockedSend).toHaveBeenCalledWith(
|
|
88
|
+
"g-1471383327500481391",
|
|
89
|
+
"hello native group",
|
|
90
|
+
expect.objectContaining({ isGroup: true }),
|
|
91
91
|
);
|
|
92
|
-
expect(result).toMatchObject({ channel: "zalouser", messageId: "zlu-
|
|
92
|
+
expect(result).toMatchObject({ channel: "zalouser", messageId: "zlu-g-native" });
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
installSendPayloadContractSuite({
|
|
96
|
+
channel: "zalouser",
|
|
97
|
+
chunking: { mode: "split", longTextLength: 3000, maxChunkLength: 2000 },
|
|
98
|
+
createHarness: ({ payload, sendResults }) => {
|
|
99
|
+
primeSendMock(mockedSend, { ok: true, messageId: "zlu-1" }, sendResults);
|
|
100
|
+
return {
|
|
101
|
+
run: async () => await zalouserPlugin.outbound!.sendPayload!(baseCtx(payload)),
|
|
102
|
+
sendMock: mockedSend,
|
|
103
|
+
to: "987654321",
|
|
104
|
+
};
|
|
105
|
+
},
|
|
100
106
|
});
|
|
107
|
+
});
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
describe("zalouserPlugin messaging target normalization", () => {
|
|
110
|
+
it("normalizes user/group aliases to canonical targets", () => {
|
|
111
|
+
const normalize = zalouserPlugin.messaging?.normalizeTarget;
|
|
112
|
+
expect(normalize).toBeTypeOf("function");
|
|
113
|
+
if (!normalize) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
expect(normalize("zlu:g:30003")).toBe("group:30003");
|
|
117
|
+
expect(normalize("zalouser:u:20002")).toBe("user:20002");
|
|
118
|
+
expect(normalize("zlu:g-30003")).toBe("group:g-30003");
|
|
119
|
+
expect(normalize("zalouser:u-20002")).toBe("user:u-20002");
|
|
120
|
+
expect(normalize("20002")).toBe("20002");
|
|
121
|
+
});
|
|
109
122
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
it("treats canonical and provider-native user/group targets as ids", () => {
|
|
124
|
+
const looksLikeId = zalouserPlugin.messaging?.targetResolver?.looksLikeId;
|
|
125
|
+
expect(looksLikeId).toBeTypeOf("function");
|
|
126
|
+
if (!looksLikeId) {
|
|
127
|
+
return;
|
|
114
128
|
}
|
|
115
|
-
expect(
|
|
129
|
+
expect(looksLikeId("user:20002")).toBe(true);
|
|
130
|
+
expect(looksLikeId("group:30003")).toBe(true);
|
|
131
|
+
expect(looksLikeId("g-30003")).toBe(true);
|
|
132
|
+
expect(looksLikeId("u-20002")).toBe(true);
|
|
133
|
+
expect(looksLikeId("Alice Nguyen")).toBe(false);
|
|
116
134
|
});
|
|
117
135
|
});
|
package/src/channel.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildAccountScopedDmSecurityPolicy,
|
|
3
|
+
createAccountStatusSink,
|
|
3
4
|
mapAllowFromEntries,
|
|
4
5
|
} from "openclaw/plugin-sdk/compat";
|
|
5
6
|
import type {
|
|
@@ -66,6 +67,97 @@ const meta = {
|
|
|
66
67
|
quickstartAllowFrom: true,
|
|
67
68
|
};
|
|
68
69
|
|
|
70
|
+
function stripZalouserTargetPrefix(raw: string): string {
|
|
71
|
+
return raw
|
|
72
|
+
.trim()
|
|
73
|
+
.replace(/^(zalouser|zlu):/i, "")
|
|
74
|
+
.trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizePrefixedTarget(raw: string): string | undefined {
|
|
78
|
+
const trimmed = stripZalouserTargetPrefix(raw);
|
|
79
|
+
if (!trimmed) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const lower = trimmed.toLowerCase();
|
|
84
|
+
if (lower.startsWith("group:")) {
|
|
85
|
+
const id = trimmed.slice("group:".length).trim();
|
|
86
|
+
return id ? `group:${id}` : undefined;
|
|
87
|
+
}
|
|
88
|
+
if (lower.startsWith("g:")) {
|
|
89
|
+
const id = trimmed.slice("g:".length).trim();
|
|
90
|
+
return id ? `group:${id}` : undefined;
|
|
91
|
+
}
|
|
92
|
+
if (lower.startsWith("user:")) {
|
|
93
|
+
const id = trimmed.slice("user:".length).trim();
|
|
94
|
+
return id ? `user:${id}` : undefined;
|
|
95
|
+
}
|
|
96
|
+
if (lower.startsWith("dm:")) {
|
|
97
|
+
const id = trimmed.slice("dm:".length).trim();
|
|
98
|
+
return id ? `user:${id}` : undefined;
|
|
99
|
+
}
|
|
100
|
+
if (lower.startsWith("u:")) {
|
|
101
|
+
const id = trimmed.slice("u:".length).trim();
|
|
102
|
+
return id ? `user:${id}` : undefined;
|
|
103
|
+
}
|
|
104
|
+
if (/^g-\S+$/i.test(trimmed)) {
|
|
105
|
+
return `group:${trimmed}`;
|
|
106
|
+
}
|
|
107
|
+
if (/^u-\S+$/i.test(trimmed)) {
|
|
108
|
+
return `user:${trimmed}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return trimmed;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function parseZalouserOutboundTarget(raw: string): {
|
|
115
|
+
threadId: string;
|
|
116
|
+
isGroup: boolean;
|
|
117
|
+
} {
|
|
118
|
+
const normalized = normalizePrefixedTarget(raw);
|
|
119
|
+
if (!normalized) {
|
|
120
|
+
throw new Error("Zalouser target is required");
|
|
121
|
+
}
|
|
122
|
+
const lowered = normalized.toLowerCase();
|
|
123
|
+
if (lowered.startsWith("group:")) {
|
|
124
|
+
const threadId = normalized.slice("group:".length).trim();
|
|
125
|
+
if (!threadId) {
|
|
126
|
+
throw new Error("Zalouser group target is missing group id");
|
|
127
|
+
}
|
|
128
|
+
return { threadId, isGroup: true };
|
|
129
|
+
}
|
|
130
|
+
if (lowered.startsWith("user:")) {
|
|
131
|
+
const threadId = normalized.slice("user:".length).trim();
|
|
132
|
+
if (!threadId) {
|
|
133
|
+
throw new Error("Zalouser user target is missing user id");
|
|
134
|
+
}
|
|
135
|
+
return { threadId, isGroup: false };
|
|
136
|
+
}
|
|
137
|
+
// Backward-compatible fallback for bare IDs.
|
|
138
|
+
// Group sends should use explicit `group:<id>` targets.
|
|
139
|
+
return { threadId: normalized, isGroup: false };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parseZalouserDirectoryGroupId(raw: string): string {
|
|
143
|
+
const normalized = normalizePrefixedTarget(raw);
|
|
144
|
+
if (!normalized) {
|
|
145
|
+
throw new Error("Zalouser group target is required");
|
|
146
|
+
}
|
|
147
|
+
const lowered = normalized.toLowerCase();
|
|
148
|
+
if (lowered.startsWith("group:")) {
|
|
149
|
+
const groupId = normalized.slice("group:".length).trim();
|
|
150
|
+
if (!groupId) {
|
|
151
|
+
throw new Error("Zalouser group target is missing group id");
|
|
152
|
+
}
|
|
153
|
+
return groupId;
|
|
154
|
+
}
|
|
155
|
+
if (lowered.startsWith("user:")) {
|
|
156
|
+
throw new Error("Zalouser group members lookup requires a group target (group:<id>)");
|
|
157
|
+
}
|
|
158
|
+
return normalized;
|
|
159
|
+
}
|
|
160
|
+
|
|
69
161
|
function resolveZalouserQrProfile(accountId?: string | null): string {
|
|
70
162
|
const normalized = normalizeAccountId(accountId);
|
|
71
163
|
if (!normalized || normalized === DEFAULT_ACCOUNT_ID) {
|
|
@@ -261,6 +353,8 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
261
353
|
"name",
|
|
262
354
|
"dmPolicy",
|
|
263
355
|
"allowFrom",
|
|
356
|
+
"historyLimit",
|
|
357
|
+
"groupAllowFrom",
|
|
264
358
|
"groupPolicy",
|
|
265
359
|
"groups",
|
|
266
360
|
"messagePrefix",
|
|
@@ -333,16 +427,19 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
333
427
|
},
|
|
334
428
|
},
|
|
335
429
|
messaging: {
|
|
336
|
-
normalizeTarget: (raw) =>
|
|
337
|
-
const trimmed = raw?.trim();
|
|
338
|
-
if (!trimmed) {
|
|
339
|
-
return undefined;
|
|
340
|
-
}
|
|
341
|
-
return trimmed.replace(/^(zalouser|zlu):/i, "");
|
|
342
|
-
},
|
|
430
|
+
normalizeTarget: (raw) => normalizePrefixedTarget(raw),
|
|
343
431
|
targetResolver: {
|
|
344
|
-
looksLikeId:
|
|
345
|
-
|
|
432
|
+
looksLikeId: (raw) => {
|
|
433
|
+
const normalized = normalizePrefixedTarget(raw);
|
|
434
|
+
if (!normalized) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (/^group:[^\s]+$/i.test(normalized) || /^user:[^\s]+$/i.test(normalized)) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
return isNumericTargetId(normalized);
|
|
441
|
+
},
|
|
442
|
+
hint: "<user:id|group:id>",
|
|
346
443
|
},
|
|
347
444
|
},
|
|
348
445
|
directory: {
|
|
@@ -377,7 +474,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
377
474
|
const groups = await listZaloGroupsMatching(account.profile, query);
|
|
378
475
|
const rows = groups.map((group) =>
|
|
379
476
|
mapGroup({
|
|
380
|
-
id: String(group.groupId)
|
|
477
|
+
id: `group:${String(group.groupId)}`,
|
|
381
478
|
name: group.name ?? null,
|
|
382
479
|
raw: group,
|
|
383
480
|
}),
|
|
@@ -386,7 +483,8 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
386
483
|
},
|
|
387
484
|
listGroupMembers: async ({ cfg, accountId, groupId, limit }) => {
|
|
388
485
|
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
|
389
|
-
const
|
|
486
|
+
const normalizedGroupId = parseZalouserDirectoryGroupId(groupId);
|
|
487
|
+
const members = await listZaloGroupMembers(account.profile, normalizedGroupId);
|
|
390
488
|
const rows = members.map((member) =>
|
|
391
489
|
mapUser({
|
|
392
490
|
id: member.userId,
|
|
@@ -511,13 +609,19 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
511
609
|
}),
|
|
512
610
|
sendText: async ({ to, text, accountId, cfg }) => {
|
|
513
611
|
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
|
514
|
-
const
|
|
612
|
+
const target = parseZalouserOutboundTarget(to);
|
|
613
|
+
const result = await sendMessageZalouser(target.threadId, text, {
|
|
614
|
+
profile: account.profile,
|
|
615
|
+
isGroup: target.isGroup,
|
|
616
|
+
});
|
|
515
617
|
return buildChannelSendResult("zalouser", result);
|
|
516
618
|
},
|
|
517
619
|
sendMedia: async ({ to, text, mediaUrl, accountId, cfg, mediaLocalRoots }) => {
|
|
518
620
|
const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
|
|
519
|
-
const
|
|
621
|
+
const target = parseZalouserOutboundTarget(to);
|
|
622
|
+
const result = await sendMessageZalouser(target.threadId, text, {
|
|
520
623
|
profile: account.profile,
|
|
624
|
+
isGroup: target.isGroup,
|
|
521
625
|
mediaUrl,
|
|
522
626
|
mediaLocalRoots,
|
|
523
627
|
});
|
|
@@ -579,6 +683,10 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
579
683
|
} catch {
|
|
580
684
|
// ignore probe errors
|
|
581
685
|
}
|
|
686
|
+
const statusSink = createAccountStatusSink({
|
|
687
|
+
accountId: ctx.accountId,
|
|
688
|
+
setStatus: ctx.setStatus,
|
|
689
|
+
});
|
|
582
690
|
ctx.log?.info(`[${account.accountId}] starting zalouser provider${userLabel}`);
|
|
583
691
|
const { monitorZalouserProvider } = await import("./monitor.js");
|
|
584
692
|
return monitorZalouserProvider({
|
|
@@ -586,7 +694,7 @@ export const zalouserPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
|
586
694
|
config: ctx.cfg,
|
|
587
695
|
runtime: ctx.runtime,
|
|
588
696
|
abortSignal: ctx.abortSignal,
|
|
589
|
-
statusSink
|
|
697
|
+
statusSink,
|
|
590
698
|
});
|
|
591
699
|
},
|
|
592
700
|
loginWithQrStart: async (params) => {
|
package/src/config-schema.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AllowFromListSchema,
|
|
3
|
+
buildCatchallMultiAccountChannelSchema,
|
|
4
|
+
DmPolicySchema,
|
|
5
|
+
GroupPolicySchema,
|
|
6
|
+
} from "openclaw/plugin-sdk/compat";
|
|
1
7
|
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk/zalouser";
|
|
2
8
|
import { z } from "zod";
|
|
3
9
|
|
|
4
|
-
const allowFromEntry = z.union([z.string(), z.number()]);
|
|
5
|
-
|
|
6
10
|
const groupConfigSchema = z.object({
|
|
7
11
|
allow: z.boolean().optional(),
|
|
8
12
|
enabled: z.boolean().optional(),
|
|
@@ -15,15 +19,14 @@ const zalouserAccountSchema = z.object({
|
|
|
15
19
|
enabled: z.boolean().optional(),
|
|
16
20
|
markdown: MarkdownConfigSchema,
|
|
17
21
|
profile: z.string().optional(),
|
|
18
|
-
dmPolicy:
|
|
19
|
-
allowFrom:
|
|
20
|
-
|
|
22
|
+
dmPolicy: DmPolicySchema.optional(),
|
|
23
|
+
allowFrom: AllowFromListSchema,
|
|
24
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
25
|
+
groupAllowFrom: AllowFromListSchema,
|
|
26
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
21
27
|
groups: z.object({}).catchall(groupConfigSchema).optional(),
|
|
22
28
|
messagePrefix: z.string().optional(),
|
|
23
29
|
responsePrefix: z.string().optional(),
|
|
24
30
|
});
|
|
25
31
|
|
|
26
|
-
export const ZalouserConfigSchema = zalouserAccountSchema
|
|
27
|
-
accounts: z.object({}).catchall(zalouserAccountSchema).optional(),
|
|
28
|
-
defaultAccount: z.string().optional(),
|
|
29
|
-
});
|
|
32
|
+
export const ZalouserConfigSchema = buildCatchallMultiAccountChannelSchema(zalouserAccountSchema);
|