@openclaw/zalouser 2026.5.2 → 2026.5.3-beta.2
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/dist/accounts-C00IMUgd.js +63 -0
- package/dist/accounts.runtime-uG7S8cXT.js +2 -0
- package/dist/api-BRwdUWuS.js +139 -0
- package/dist/api.js +7 -0
- package/dist/channel-ou_w_2j-.js +484 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-C9WxiAiR.js +25 -0
- package/dist/channel.setup-CiDeBFrn.js +10 -0
- package/dist/contract-api.js +3 -0
- package/dist/doctor-contract-DgqHp8E2.js +128 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +27 -0
- package/dist/monitor-Cg7K_s_s.js +705 -0
- package/dist/runtime-QNU7vLgI.js +106 -0
- package/dist/runtime-api.js +22 -0
- package/dist/secret-contract-api.js +5 -0
- package/dist/security-audit-BZLhil-V.js +34 -0
- package/dist/send-BsmySxe3.js +534 -0
- package/dist/session-route-C0-Xr8bt.js +92 -0
- package/dist/setup-core-CqipqY98.js +40 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/setup-surface-NCOuKu-l.js +359 -0
- package/dist/shared-DSy8aIUx.js +120 -0
- package/dist/test-api.js +5 -0
- package/dist/zalo-js-CHCUlY3c.js +1279 -0
- package/package.json +15 -6
- package/api.ts +0 -9
- package/channel-plugin-api.ts +0 -3
- package/contract-api.ts +0 -2
- package/doctor-contract-api.ts +0 -1
- package/index.ts +0 -34
- package/runtime-api.ts +0 -67
- package/secret-contract-api.ts +0 -4
- package/setup-entry.ts +0 -9
- package/setup-plugin-api.ts +0 -2
- package/src/accounts.runtime.ts +0 -1
- package/src/accounts.test-mocks.ts +0 -14
- package/src/accounts.test.ts +0 -266
- package/src/accounts.ts +0 -131
- package/src/channel-api.ts +0 -20
- package/src/channel.adapters.ts +0 -391
- package/src/channel.directory.test.ts +0 -59
- package/src/channel.runtime.ts +0 -12
- package/src/channel.sendpayload.test.ts +0 -172
- package/src/channel.setup.test.ts +0 -33
- package/src/channel.setup.ts +0 -12
- package/src/channel.test.ts +0 -377
- package/src/channel.ts +0 -219
- package/src/config-schema.ts +0 -33
- package/src/directory.ts +0 -54
- package/src/doctor-contract.ts +0 -156
- package/src/doctor.test.ts +0 -77
- package/src/doctor.ts +0 -37
- package/src/group-policy.test.ts +0 -61
- package/src/group-policy.ts +0 -83
- package/src/message-sid.test.ts +0 -66
- package/src/message-sid.ts +0 -80
- package/src/monitor.account-scope.test.ts +0 -107
- package/src/monitor.group-gating.test.ts +0 -816
- package/src/monitor.send-mocks.ts +0 -20
- package/src/monitor.ts +0 -1044
- package/src/probe.test.ts +0 -60
- package/src/probe.ts +0 -35
- package/src/qr-temp-file.ts +0 -22
- package/src/reaction.test.ts +0 -19
- package/src/reaction.ts +0 -32
- package/src/runtime.ts +0 -9
- package/src/security-audit.test.ts +0 -80
- package/src/security-audit.ts +0 -71
- package/src/send.test.ts +0 -395
- package/src/send.ts +0 -272
- package/src/session-route.ts +0 -121
- package/src/setup-core.ts +0 -33
- package/src/setup-surface.test.ts +0 -363
- package/src/setup-surface.ts +0 -470
- package/src/setup-test-helpers.ts +0 -42
- package/src/shared.ts +0 -92
- package/src/status-issues.test.ts +0 -31
- package/src/status-issues.ts +0 -58
- package/src/test-helpers.ts +0 -26
- package/src/text-styles.test.ts +0 -203
- package/src/text-styles.ts +0 -540
- package/src/tool.test.ts +0 -212
- package/src/tool.ts +0 -210
- package/src/types.ts +0 -125
- package/src/zalo-js.credentials.test.ts +0 -465
- package/src/zalo-js.test-mocks.ts +0 -89
- package/src/zalo-js.ts +0 -1911
- package/src/zca-client.test.ts +0 -24
- package/src/zca-client.ts +0 -259
- package/src/zca-constants.ts +0 -55
- package/src/zca-js-exports.d.ts +0 -22
- package/test-api.ts +0 -21
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { a as resolveZalouserAccountSync, i as resolveDefaultZalouserAccountId, r as listZalouserAccountIds, t as checkZcaAuthenticated } from "./accounts-C00IMUgd.js";
|
|
2
|
+
import { a as isNumericTargetId, i as isDangerousNameMatchingEnabled, n as DEFAULT_ACCOUNT_ID, o as normalizeAccountId, r as chunkTextForOutbound, s as sendPayloadWithChunkedTextAndMedia, t as createZalouserPluginBase } from "./shared-DSy8aIUx.js";
|
|
3
|
+
import { a as resolveZalouserReactionMessageIds, o as buildZalouserGroupCandidates, s as findZalouserGroupEntry, t as getZalouserRuntime } from "./runtime-QNU7vLgI.js";
|
|
4
|
+
import { n as zalouserSetupAdapter, r as writeQrDataUrlToTempFile, t as createZalouserSetupWizardProxy } from "./setup-core-CqipqY98.js";
|
|
5
|
+
import { i as resolveZalouserOutboundSessionRoute, n as parseZalouserDirectoryGroupId, r as parseZalouserOutboundTarget, t as normalizeZalouserTarget } from "./session-route-C0-Xr8bt.js";
|
|
6
|
+
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
7
|
+
import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
|
|
8
|
+
import { buildPassiveProbedChannelStatusSummary, coerceStatusIssueAccountId, readStatusIssueFields } from "openclaw/plugin-sdk/extension-shared";
|
|
9
|
+
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
10
|
+
import { createAsyncComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
11
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
12
|
+
import { createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
13
|
+
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
14
|
+
import { createEmptyChannelResult, createRawChannelSendResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
|
|
15
|
+
import { createStaticReplyToModeResolver } from "openclaw/plugin-sdk/conversation-runtime";
|
|
16
|
+
//#region extensions/zalouser/src/channel.adapters.ts
|
|
17
|
+
const loadZalouserChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel.runtime-C9WxiAiR.js"));
|
|
18
|
+
const ZALOUSER_TEXT_CHUNK_LIMIT = 2e3;
|
|
19
|
+
function resolveZalouserQrProfile(accountId) {
|
|
20
|
+
const normalized = normalizeAccountId(accountId);
|
|
21
|
+
if (!normalized || normalized === DEFAULT_ACCOUNT_ID) return process.env.ZALOUSER_PROFILE?.trim() || process.env.ZCA_PROFILE?.trim() || "default";
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
24
|
+
function resolveZalouserOutboundChunkMode(cfg, accountId) {
|
|
25
|
+
return getZalouserRuntime().channel.text.resolveChunkMode(cfg, "zalouser", accountId);
|
|
26
|
+
}
|
|
27
|
+
function resolveZalouserOutboundTextChunkLimit(cfg, accountId) {
|
|
28
|
+
return getZalouserRuntime().channel.text.resolveTextChunkLimit(cfg, "zalouser", accountId, { fallbackLimit: ZALOUSER_TEXT_CHUNK_LIMIT });
|
|
29
|
+
}
|
|
30
|
+
function resolveZalouserGroupPolicyEntry(params) {
|
|
31
|
+
const account = resolveZalouserAccountSync({
|
|
32
|
+
cfg: params.cfg,
|
|
33
|
+
accountId: params.accountId ?? void 0
|
|
34
|
+
});
|
|
35
|
+
return findZalouserGroupEntry(account.config.groups ?? {}, buildZalouserGroupCandidates({
|
|
36
|
+
groupId: params.groupId,
|
|
37
|
+
groupChannel: params.groupChannel,
|
|
38
|
+
includeWildcard: true,
|
|
39
|
+
allowNameMatching: isDangerousNameMatchingEnabled(account.config)
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
function resolveZalouserGroupToolPolicy(params) {
|
|
43
|
+
return resolveZalouserGroupPolicyEntry(params)?.tools;
|
|
44
|
+
}
|
|
45
|
+
function resolveZalouserRequireMention(params) {
|
|
46
|
+
const entry = resolveZalouserGroupPolicyEntry(params);
|
|
47
|
+
if (typeof entry?.requireMention === "boolean") return entry.requireMention;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const zalouserRawSendResultAdapter = createRawChannelSendResultAdapter({
|
|
51
|
+
channel: "zalouser",
|
|
52
|
+
sendText: async ({ to, text, accountId, cfg }) => {
|
|
53
|
+
const { sendMessageZalouser } = await loadZalouserChannelRuntime$1();
|
|
54
|
+
const account = resolveZalouserAccountSync({
|
|
55
|
+
cfg,
|
|
56
|
+
accountId
|
|
57
|
+
});
|
|
58
|
+
const target = parseZalouserOutboundTarget(to);
|
|
59
|
+
return await sendMessageZalouser(target.threadId, text, {
|
|
60
|
+
profile: account.profile,
|
|
61
|
+
isGroup: target.isGroup,
|
|
62
|
+
textMode: "markdown",
|
|
63
|
+
textChunkMode: resolveZalouserOutboundChunkMode(cfg, account.accountId),
|
|
64
|
+
textChunkLimit: resolveZalouserOutboundTextChunkLimit(cfg, account.accountId)
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
sendMedia: async ({ to, text, mediaUrl, accountId, cfg, mediaLocalRoots, mediaReadFile }) => {
|
|
68
|
+
const { sendMessageZalouser } = await loadZalouserChannelRuntime$1();
|
|
69
|
+
const account = resolveZalouserAccountSync({
|
|
70
|
+
cfg,
|
|
71
|
+
accountId
|
|
72
|
+
});
|
|
73
|
+
const target = parseZalouserOutboundTarget(to);
|
|
74
|
+
return await sendMessageZalouser(target.threadId, text, {
|
|
75
|
+
profile: account.profile,
|
|
76
|
+
isGroup: target.isGroup,
|
|
77
|
+
mediaUrl,
|
|
78
|
+
mediaLocalRoots,
|
|
79
|
+
mediaReadFile,
|
|
80
|
+
textMode: "markdown",
|
|
81
|
+
textChunkMode: resolveZalouserOutboundChunkMode(cfg, account.accountId),
|
|
82
|
+
textChunkLimit: resolveZalouserOutboundTextChunkLimit(cfg, account.accountId)
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const resolveZalouserDmPolicy = createScopedDmSecurityResolver({
|
|
87
|
+
channelKey: "zalouser",
|
|
88
|
+
resolvePolicy: (account) => account.config.dmPolicy,
|
|
89
|
+
resolveAllowFrom: (account) => account.config.allowFrom,
|
|
90
|
+
policyPathSuffix: "dmPolicy",
|
|
91
|
+
normalizeEntry: (raw) => raw.trim().replace(/^(zalouser|zlu):/i, "")
|
|
92
|
+
});
|
|
93
|
+
const zalouserGroupsAdapter = {
|
|
94
|
+
resolveRequireMention: resolveZalouserRequireMention,
|
|
95
|
+
resolveToolPolicy: resolveZalouserGroupToolPolicy
|
|
96
|
+
};
|
|
97
|
+
const zalouserMessageActions = {
|
|
98
|
+
describeMessageTool: ({ cfg, accountId }) => {
|
|
99
|
+
if ((accountId ? [resolveZalouserAccountSync({
|
|
100
|
+
cfg,
|
|
101
|
+
accountId
|
|
102
|
+
})].filter((account) => account.enabled) : listZalouserAccountIds(cfg).map((resolvedAccountId) => resolveZalouserAccountSync({
|
|
103
|
+
cfg,
|
|
104
|
+
accountId: resolvedAccountId
|
|
105
|
+
})).filter((account) => account.enabled)).length === 0) return null;
|
|
106
|
+
return { actions: ["react"] };
|
|
107
|
+
},
|
|
108
|
+
supportsAction: ({ action }) => action === "react",
|
|
109
|
+
handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
|
|
110
|
+
if (action !== "react") throw new Error(`Zalouser action ${action} not supported`);
|
|
111
|
+
const { sendReactionZalouser } = await loadZalouserChannelRuntime$1();
|
|
112
|
+
const account = resolveZalouserAccountSync({
|
|
113
|
+
cfg,
|
|
114
|
+
accountId
|
|
115
|
+
});
|
|
116
|
+
const threadId = (typeof params.threadId === "string" ? params.threadId.trim() : "") || (typeof params.to === "string" ? params.to.trim() : "") || (typeof params.chatId === "string" ? params.chatId.trim() : "") || (toolContext?.currentChannelId?.trim() ?? "");
|
|
117
|
+
if (!threadId) throw new Error("Zalouser react requires threadId (or to/chatId).");
|
|
118
|
+
const emoji = typeof params.emoji === "string" ? params.emoji.trim() : "";
|
|
119
|
+
if (!emoji) throw new Error("Zalouser react requires emoji.");
|
|
120
|
+
const ids = resolveZalouserReactionMessageIds({
|
|
121
|
+
messageId: typeof params.messageId === "string" ? params.messageId : void 0,
|
|
122
|
+
cliMsgId: typeof params.cliMsgId === "string" ? params.cliMsgId : void 0,
|
|
123
|
+
currentMessageId: toolContext?.currentMessageId
|
|
124
|
+
});
|
|
125
|
+
if (!ids) throw new Error("Zalouser react requires messageId + cliMsgId (or a current message context id).");
|
|
126
|
+
const result = await sendReactionZalouser({
|
|
127
|
+
profile: account.profile,
|
|
128
|
+
threadId,
|
|
129
|
+
isGroup: params.isGroup === true,
|
|
130
|
+
msgId: ids.msgId,
|
|
131
|
+
cliMsgId: ids.cliMsgId,
|
|
132
|
+
emoji,
|
|
133
|
+
remove: params.remove === true
|
|
134
|
+
});
|
|
135
|
+
if (!result.ok) throw new Error(result.error || "Failed to react on Zalo message");
|
|
136
|
+
return {
|
|
137
|
+
content: [{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: params.remove === true ? `Removed reaction ${emoji} from ${ids.msgId}` : `Reacted ${emoji} on ${ids.msgId}`
|
|
140
|
+
}],
|
|
141
|
+
details: {
|
|
142
|
+
messageId: ids.msgId,
|
|
143
|
+
cliMsgId: ids.cliMsgId,
|
|
144
|
+
threadId
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const zalouserResolverAdapter = { resolveTargets: async ({ cfg, accountId, inputs, kind, runtime }) => {
|
|
150
|
+
const results = [];
|
|
151
|
+
for (const input of inputs) {
|
|
152
|
+
const trimmed = input.trim();
|
|
153
|
+
if (!trimmed) {
|
|
154
|
+
results.push({
|
|
155
|
+
input,
|
|
156
|
+
resolved: false,
|
|
157
|
+
note: "empty input"
|
|
158
|
+
});
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (/^\d+$/.test(trimmed)) {
|
|
162
|
+
results.push({
|
|
163
|
+
input,
|
|
164
|
+
resolved: true,
|
|
165
|
+
id: trimmed
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const runtimeModule = await loadZalouserChannelRuntime$1();
|
|
171
|
+
const account = resolveZalouserAccountSync({
|
|
172
|
+
cfg,
|
|
173
|
+
accountId: accountId ?? resolveDefaultZalouserAccountId(cfg)
|
|
174
|
+
});
|
|
175
|
+
if (kind === "user") {
|
|
176
|
+
const friends = await runtimeModule.listZaloFriendsMatching(account.profile, trimmed);
|
|
177
|
+
const best = friends[0];
|
|
178
|
+
results.push({
|
|
179
|
+
input,
|
|
180
|
+
resolved: Boolean(best?.userId),
|
|
181
|
+
id: best?.userId,
|
|
182
|
+
name: best?.displayName,
|
|
183
|
+
note: friends.length > 1 ? "multiple matches; chose first" : void 0
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
const groups = await runtimeModule.listZaloGroupsMatching(account.profile, trimmed);
|
|
187
|
+
const best = groups.find((group) => normalizeLowercaseStringOrEmpty(group.name) === normalizeLowercaseStringOrEmpty(trimmed)) ?? groups[0];
|
|
188
|
+
results.push({
|
|
189
|
+
input,
|
|
190
|
+
resolved: Boolean(best?.groupId),
|
|
191
|
+
id: best?.groupId,
|
|
192
|
+
name: best?.name,
|
|
193
|
+
note: groups.length > 1 ? "multiple matches; chose first" : void 0
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
runtime.error?.(`zalouser resolve failed: ${String(err)}`);
|
|
198
|
+
results.push({
|
|
199
|
+
input,
|
|
200
|
+
resolved: false,
|
|
201
|
+
note: "lookup failed"
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return results;
|
|
206
|
+
} };
|
|
207
|
+
const zalouserAuthAdapter = { login: async ({ cfg, accountId, runtime }) => {
|
|
208
|
+
const { startZaloQrLogin, waitForZaloQrLogin } = await loadZalouserChannelRuntime$1();
|
|
209
|
+
const account = resolveZalouserAccountSync({
|
|
210
|
+
cfg,
|
|
211
|
+
accountId: accountId ?? resolveDefaultZalouserAccountId(cfg)
|
|
212
|
+
});
|
|
213
|
+
runtime.log(`Generating QR login for Zalo Personal (account: ${account.accountId}, profile: ${account.profile})...`);
|
|
214
|
+
const started = await startZaloQrLogin({
|
|
215
|
+
profile: account.profile,
|
|
216
|
+
timeoutMs: 35e3
|
|
217
|
+
});
|
|
218
|
+
if (!started.qrDataUrl) throw new Error(started.message || "Failed to start QR login");
|
|
219
|
+
const qrPath = await writeQrDataUrlToTempFile(started.qrDataUrl, account.profile);
|
|
220
|
+
if (qrPath) runtime.log(`Scan QR image: ${qrPath}`);
|
|
221
|
+
else runtime.log("QR generated but could not be written to a temp file.");
|
|
222
|
+
const waited = await waitForZaloQrLogin({
|
|
223
|
+
profile: account.profile,
|
|
224
|
+
timeoutMs: 18e4
|
|
225
|
+
});
|
|
226
|
+
if (!waited.connected) throw new Error(waited.message || "Zalouser login failed");
|
|
227
|
+
runtime.log(waited.message);
|
|
228
|
+
} };
|
|
229
|
+
const zalouserSecurityAdapter = {
|
|
230
|
+
resolveDmPolicy: resolveZalouserDmPolicy,
|
|
231
|
+
collectAuditFindings: async (params) => (await loadZalouserChannelRuntime$1()).collectZalouserSecurityAuditFindings(params)
|
|
232
|
+
};
|
|
233
|
+
const zalouserThreadingAdapter = { resolveReplyToMode: createStaticReplyToModeResolver("off") };
|
|
234
|
+
const zalouserPairingTextAdapter = {
|
|
235
|
+
idLabel: "zalouserUserId",
|
|
236
|
+
message: "Your pairing request has been approved.",
|
|
237
|
+
normalizeAllowEntry: createPairingPrefixStripper(/^(zalouser|zlu):/i),
|
|
238
|
+
notify: async ({ cfg, id, message }) => {
|
|
239
|
+
const { sendMessageZalouser } = await loadZalouserChannelRuntime$1();
|
|
240
|
+
const account = resolveZalouserAccountSync({ cfg });
|
|
241
|
+
if (!await checkZcaAuthenticated(account.profile)) throw new Error("Zalouser not authenticated");
|
|
242
|
+
await sendMessageZalouser(id, message, { profile: account.profile });
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const zalouserOutboundAdapter = {
|
|
246
|
+
deliveryMode: "direct",
|
|
247
|
+
chunker: chunkTextForOutbound,
|
|
248
|
+
chunkerMode: "markdown",
|
|
249
|
+
sendPayload: async (ctx) => await sendPayloadWithChunkedTextAndMedia({
|
|
250
|
+
ctx,
|
|
251
|
+
sendText: (nextCtx) => zalouserRawSendResultAdapter.sendText(nextCtx),
|
|
252
|
+
sendMedia: (nextCtx) => zalouserRawSendResultAdapter.sendMedia(nextCtx),
|
|
253
|
+
emptyResult: createEmptyChannelResult("zalouser")
|
|
254
|
+
}),
|
|
255
|
+
...zalouserRawSendResultAdapter
|
|
256
|
+
};
|
|
257
|
+
const zalouserMessagingAdapter = {
|
|
258
|
+
targetPrefixes: ["zalouser", "zlu"],
|
|
259
|
+
normalizeTarget: (raw) => normalizeZalouserTarget(raw),
|
|
260
|
+
resolveOutboundSessionRoute: (params) => resolveZalouserOutboundSessionRoute(params),
|
|
261
|
+
targetResolver: {
|
|
262
|
+
looksLikeId: (raw) => {
|
|
263
|
+
const normalized = normalizeZalouserTarget(raw);
|
|
264
|
+
if (!normalized) return false;
|
|
265
|
+
if (/^group:[^\s]+$/i.test(normalized) || /^user:[^\s]+$/i.test(normalized)) return true;
|
|
266
|
+
return isNumericTargetId(normalized);
|
|
267
|
+
},
|
|
268
|
+
hint: "<user:id|group:id>"
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region extensions/zalouser/src/directory.ts
|
|
273
|
+
function mapUser$1(params) {
|
|
274
|
+
return {
|
|
275
|
+
kind: "user",
|
|
276
|
+
id: params.id,
|
|
277
|
+
name: params.name ?? void 0,
|
|
278
|
+
avatarUrl: params.avatarUrl ?? void 0,
|
|
279
|
+
raw: params.raw
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async function listZalouserDirectoryGroupMembers(params, deps) {
|
|
283
|
+
const account = resolveZalouserAccountSync({
|
|
284
|
+
cfg: params.cfg,
|
|
285
|
+
accountId: params.accountId
|
|
286
|
+
});
|
|
287
|
+
const normalizedGroupId = parseZalouserDirectoryGroupId(params.groupId);
|
|
288
|
+
const rows = (await deps.listZaloGroupMembers(account.profile, normalizedGroupId)).map((member) => mapUser$1({
|
|
289
|
+
id: member.userId,
|
|
290
|
+
name: member.displayName,
|
|
291
|
+
avatarUrl: member.avatar ?? null,
|
|
292
|
+
raw: member
|
|
293
|
+
}));
|
|
294
|
+
return typeof params.limit === "number" && params.limit > 0 ? rows.slice(0, params.limit) : rows;
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region extensions/zalouser/src/status-issues.ts
|
|
298
|
+
const ZALOUSER_STATUS_FIELDS = [
|
|
299
|
+
"accountId",
|
|
300
|
+
"enabled",
|
|
301
|
+
"configured",
|
|
302
|
+
"dmPolicy",
|
|
303
|
+
"lastError"
|
|
304
|
+
];
|
|
305
|
+
function collectZalouserStatusIssues(accounts) {
|
|
306
|
+
const issues = [];
|
|
307
|
+
for (const entry of accounts) {
|
|
308
|
+
const account = readStatusIssueFields(entry, ZALOUSER_STATUS_FIELDS);
|
|
309
|
+
if (!account) continue;
|
|
310
|
+
const accountId = coerceStatusIssueAccountId(account.accountId) ?? "default";
|
|
311
|
+
if (!(account.enabled !== false)) continue;
|
|
312
|
+
if (!(account.configured === true)) {
|
|
313
|
+
issues.push({
|
|
314
|
+
channel: "zalouser",
|
|
315
|
+
accountId,
|
|
316
|
+
kind: "auth",
|
|
317
|
+
message: "Not authenticated (no saved Zalo session).",
|
|
318
|
+
fix: "Run: openclaw channels login --channel zalouser"
|
|
319
|
+
});
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (account.dmPolicy === "open") issues.push({
|
|
323
|
+
channel: "zalouser",
|
|
324
|
+
accountId,
|
|
325
|
+
kind: "config",
|
|
326
|
+
message: "Zalo Personal dmPolicy is \"open\", allowing any user to message the bot without pairing.",
|
|
327
|
+
fix: "Set channels.zalouser.dmPolicy to \"pairing\" or \"allowlist\" to restrict access."
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return issues;
|
|
331
|
+
}
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region extensions/zalouser/src/channel.ts
|
|
334
|
+
const loadZalouserChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-C9WxiAiR.js"));
|
|
335
|
+
const zalouserSetupWizardProxy = createZalouserSetupWizardProxy(async () => (await import("./setup-surface-NCOuKu-l.js").then((n) => n.t)).zalouserSetupWizard);
|
|
336
|
+
function mapUser(params) {
|
|
337
|
+
return {
|
|
338
|
+
kind: "user",
|
|
339
|
+
id: params.id,
|
|
340
|
+
name: params.name ?? void 0,
|
|
341
|
+
avatarUrl: params.avatarUrl ?? void 0,
|
|
342
|
+
raw: params.raw
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function mapGroup(params) {
|
|
346
|
+
return {
|
|
347
|
+
kind: "group",
|
|
348
|
+
id: params.id,
|
|
349
|
+
name: params.name ?? void 0,
|
|
350
|
+
raw: params.raw
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const zalouserPlugin = createChatChannelPlugin({
|
|
354
|
+
base: {
|
|
355
|
+
...createZalouserPluginBase({
|
|
356
|
+
setupWizard: zalouserSetupWizardProxy,
|
|
357
|
+
setup: zalouserSetupAdapter
|
|
358
|
+
}),
|
|
359
|
+
groups: zalouserGroupsAdapter,
|
|
360
|
+
actions: zalouserMessageActions,
|
|
361
|
+
messaging: zalouserMessagingAdapter,
|
|
362
|
+
directory: {
|
|
363
|
+
self: async ({ cfg, accountId }) => {
|
|
364
|
+
const { getZaloUserInfo } = await loadZalouserChannelRuntime();
|
|
365
|
+
const parsed = await getZaloUserInfo(resolveZalouserAccountSync({
|
|
366
|
+
cfg,
|
|
367
|
+
accountId
|
|
368
|
+
}).profile);
|
|
369
|
+
if (!parsed?.userId) return null;
|
|
370
|
+
return mapUser({
|
|
371
|
+
id: parsed.userId,
|
|
372
|
+
name: parsed.displayName ?? null,
|
|
373
|
+
avatarUrl: parsed.avatar ?? null,
|
|
374
|
+
raw: parsed
|
|
375
|
+
});
|
|
376
|
+
},
|
|
377
|
+
listPeers: async ({ cfg, accountId, query, limit }) => {
|
|
378
|
+
const { listZaloFriendsMatching } = await loadZalouserChannelRuntime();
|
|
379
|
+
const rows = (await listZaloFriendsMatching(resolveZalouserAccountSync({
|
|
380
|
+
cfg,
|
|
381
|
+
accountId
|
|
382
|
+
}).profile, query)).map((friend) => mapUser({
|
|
383
|
+
id: friend.userId,
|
|
384
|
+
name: friend.displayName ?? null,
|
|
385
|
+
avatarUrl: friend.avatar ?? null,
|
|
386
|
+
raw: friend
|
|
387
|
+
}));
|
|
388
|
+
return typeof limit === "number" && limit > 0 ? rows.slice(0, limit) : rows;
|
|
389
|
+
},
|
|
390
|
+
listGroups: async ({ cfg, accountId, query, limit }) => {
|
|
391
|
+
const { listZaloGroupsMatching } = await loadZalouserChannelRuntime();
|
|
392
|
+
const rows = (await listZaloGroupsMatching(resolveZalouserAccountSync({
|
|
393
|
+
cfg,
|
|
394
|
+
accountId
|
|
395
|
+
}).profile, query)).map((group) => mapGroup({
|
|
396
|
+
id: `group:${group.groupId}`,
|
|
397
|
+
name: group.name ?? null,
|
|
398
|
+
raw: group
|
|
399
|
+
}));
|
|
400
|
+
return typeof limit === "number" && limit > 0 ? rows.slice(0, limit) : rows;
|
|
401
|
+
},
|
|
402
|
+
listGroupMembers: async ({ cfg, accountId, groupId, limit }) => {
|
|
403
|
+
const { listZaloGroupMembers } = await loadZalouserChannelRuntime();
|
|
404
|
+
return await listZalouserDirectoryGroupMembers({
|
|
405
|
+
cfg,
|
|
406
|
+
accountId: accountId ?? void 0,
|
|
407
|
+
groupId,
|
|
408
|
+
limit: limit ?? void 0
|
|
409
|
+
}, { listZaloGroupMembers });
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
resolver: zalouserResolverAdapter,
|
|
413
|
+
auth: zalouserAuthAdapter,
|
|
414
|
+
status: createAsyncComputedAccountStatusAdapter({
|
|
415
|
+
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
|
416
|
+
collectStatusIssues: collectZalouserStatusIssues,
|
|
417
|
+
buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot),
|
|
418
|
+
probeAccount: async ({ account, timeoutMs }) => (await loadZalouserChannelRuntime()).probeZalouser(account.profile, timeoutMs),
|
|
419
|
+
resolveAccountSnapshot: async ({ account, runtime }) => {
|
|
420
|
+
const configured = await checkZcaAuthenticated(account.profile);
|
|
421
|
+
return {
|
|
422
|
+
accountId: account.accountId,
|
|
423
|
+
name: account.name,
|
|
424
|
+
enabled: account.enabled,
|
|
425
|
+
configured,
|
|
426
|
+
extra: {
|
|
427
|
+
dmPolicy: account.config.dmPolicy ?? "pairing",
|
|
428
|
+
lastError: configured ? runtime?.lastError ?? null : runtime?.lastError ?? "not authenticated"
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}),
|
|
433
|
+
gateway: {
|
|
434
|
+
startAccount: async (ctx) => {
|
|
435
|
+
const { getZaloUserInfo } = await loadZalouserChannelRuntime();
|
|
436
|
+
const account = ctx.account;
|
|
437
|
+
let userLabel = "";
|
|
438
|
+
try {
|
|
439
|
+
const userInfo = await getZaloUserInfo(account.profile);
|
|
440
|
+
if (userInfo?.displayName) userLabel = ` (${userInfo.displayName})`;
|
|
441
|
+
ctx.setStatus({
|
|
442
|
+
accountId: account.accountId,
|
|
443
|
+
profile: userInfo
|
|
444
|
+
});
|
|
445
|
+
} catch {}
|
|
446
|
+
const statusSink = createAccountStatusSink({
|
|
447
|
+
accountId: ctx.accountId,
|
|
448
|
+
setStatus: ctx.setStatus
|
|
449
|
+
});
|
|
450
|
+
ctx.log?.info(`[${account.accountId}] starting zalouser provider${userLabel}`);
|
|
451
|
+
const { monitorZalouserProvider } = await import("./monitor-Cg7K_s_s.js");
|
|
452
|
+
return monitorZalouserProvider({
|
|
453
|
+
account,
|
|
454
|
+
config: ctx.cfg,
|
|
455
|
+
runtime: ctx.runtime,
|
|
456
|
+
abortSignal: ctx.abortSignal,
|
|
457
|
+
statusSink
|
|
458
|
+
});
|
|
459
|
+
},
|
|
460
|
+
loginWithQrStart: async (params) => {
|
|
461
|
+
const { startZaloQrLogin } = await loadZalouserChannelRuntime();
|
|
462
|
+
return await startZaloQrLogin({
|
|
463
|
+
profile: resolveZalouserQrProfile(params.accountId),
|
|
464
|
+
force: params.force,
|
|
465
|
+
timeoutMs: params.timeoutMs
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
loginWithQrWait: async (params) => {
|
|
469
|
+
const { waitForZaloQrLogin } = await loadZalouserChannelRuntime();
|
|
470
|
+
return await waitForZaloQrLogin({
|
|
471
|
+
profile: resolveZalouserQrProfile(params.accountId),
|
|
472
|
+
timeoutMs: params.timeoutMs
|
|
473
|
+
});
|
|
474
|
+
},
|
|
475
|
+
logoutAccount: async (ctx) => await (await loadZalouserChannelRuntime()).logoutZaloProfile(ctx.account.profile || resolveZalouserQrProfile(ctx.accountId))
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
security: zalouserSecurityAdapter,
|
|
479
|
+
threading: zalouserThreadingAdapter,
|
|
480
|
+
pairing: { text: zalouserPairingTextAdapter },
|
|
481
|
+
outbound: zalouserOutboundAdapter
|
|
482
|
+
});
|
|
483
|
+
//#endregion
|
|
484
|
+
export { zalouserPlugin as t };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { t as collectZalouserSecurityAuditFindings } from "./security-audit-BZLhil-V.js";
|
|
2
|
+
import { a as listZaloGroupMembers, b as waitForZaloQrLogin, c as logoutZaloProfile, i as listZaloFriendsMatching, n as getZaloUserInfo, s as listZaloGroupsMatching, y as startZaloQrLogin } from "./zalo-js-CHCUlY3c.js";
|
|
3
|
+
import { a as sendReactionZalouser, i as sendMessageZalouser } from "./send-BsmySxe3.js";
|
|
4
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
|
+
//#region extensions/zalouser/src/probe.ts
|
|
6
|
+
async function probeZalouser(profile, timeoutMs) {
|
|
7
|
+
try {
|
|
8
|
+
const user = timeoutMs ? await Promise.race([getZaloUserInfo(profile), new Promise((resolve) => setTimeout(() => resolve(null), Math.max(timeoutMs, 1e3)))]) : await getZaloUserInfo(profile);
|
|
9
|
+
if (!user) return {
|
|
10
|
+
ok: false,
|
|
11
|
+
error: "Not authenticated"
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
ok: true,
|
|
15
|
+
user
|
|
16
|
+
};
|
|
17
|
+
} catch (error) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
error: formatErrorMessage(error)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { collectZalouserSecurityAuditFindings, getZaloUserInfo, listZaloFriendsMatching, listZaloGroupMembers, listZaloGroupsMatching, logoutZaloProfile, probeZalouser, sendMessageZalouser, sendReactionZalouser, startZaloQrLogin, waitForZaloQrLogin };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { n as zalouserSetupWizard } from "./setup-surface-NCOuKu-l.js";
|
|
2
|
+
import { t as createZalouserPluginBase } from "./shared-DSy8aIUx.js";
|
|
3
|
+
import { n as zalouserSetupAdapter } from "./setup-core-CqipqY98.js";
|
|
4
|
+
//#region extensions/zalouser/src/channel.setup.ts
|
|
5
|
+
const zalouserSetupPlugin = { ...createZalouserPluginBase({
|
|
6
|
+
setupWizard: zalouserSetupWizard,
|
|
7
|
+
setup: zalouserSetupAdapter
|
|
8
|
+
}) };
|
|
9
|
+
//#endregion
|
|
10
|
+
export { zalouserSetupPlugin as t };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-DgqHp8E2.js";
|
|
2
|
+
import { t as collectZalouserSecurityAuditFindings } from "./security-audit-BZLhil-V.js";
|
|
3
|
+
export { collectZalouserSecurityAuditFindings, legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
//#region extensions/zalouser/src/doctor-contract.ts
|
|
2
|
+
function asObjectRecord(value) {
|
|
3
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4
|
+
}
|
|
5
|
+
function hasLegacyZalouserGroupAllowAlias(value) {
|
|
6
|
+
const group = asObjectRecord(value);
|
|
7
|
+
return Boolean(group && typeof group.allow === "boolean");
|
|
8
|
+
}
|
|
9
|
+
function hasLegacyZalouserGroupAllowAliases(value) {
|
|
10
|
+
const groups = asObjectRecord(value);
|
|
11
|
+
return Boolean(groups && Object.values(groups).some((group) => hasLegacyZalouserGroupAllowAlias(group)));
|
|
12
|
+
}
|
|
13
|
+
function hasLegacyZalouserAccountGroupAllowAliases(value) {
|
|
14
|
+
const accounts = asObjectRecord(value);
|
|
15
|
+
if (!accounts) return false;
|
|
16
|
+
return Object.values(accounts).some((account) => {
|
|
17
|
+
const accountRecord = asObjectRecord(account);
|
|
18
|
+
return Boolean(accountRecord && hasLegacyZalouserGroupAllowAliases(accountRecord.groups));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function normalizeZalouserGroupAllowAliases(params) {
|
|
22
|
+
let changed = false;
|
|
23
|
+
const nextGroups = { ...params.groups };
|
|
24
|
+
for (const [groupId, groupValue] of Object.entries(params.groups)) {
|
|
25
|
+
const group = asObjectRecord(groupValue);
|
|
26
|
+
if (!group || typeof group.allow !== "boolean") continue;
|
|
27
|
+
const nextGroup = { ...group };
|
|
28
|
+
if (typeof nextGroup.enabled !== "boolean") nextGroup.enabled = group.allow;
|
|
29
|
+
delete nextGroup.allow;
|
|
30
|
+
nextGroups[groupId] = nextGroup;
|
|
31
|
+
changed = true;
|
|
32
|
+
params.changes.push(`Moved ${params.pathPrefix}.${groupId}.allow → ${params.pathPrefix}.${groupId}.enabled (${String(nextGroup.enabled)}).`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
groups: nextGroups,
|
|
36
|
+
changed
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function normalizeZalouserCompatibilityConfig(cfg) {
|
|
40
|
+
const zalouser = asObjectRecord(asObjectRecord(cfg.channels)?.zalouser);
|
|
41
|
+
if (!zalouser) return {
|
|
42
|
+
config: cfg,
|
|
43
|
+
changes: []
|
|
44
|
+
};
|
|
45
|
+
const changes = [];
|
|
46
|
+
let updatedZalouser = zalouser;
|
|
47
|
+
let changed = false;
|
|
48
|
+
const groups = asObjectRecord(updatedZalouser.groups);
|
|
49
|
+
if (groups) {
|
|
50
|
+
const normalized = normalizeZalouserGroupAllowAliases({
|
|
51
|
+
groups,
|
|
52
|
+
pathPrefix: "channels.zalouser.groups",
|
|
53
|
+
changes
|
|
54
|
+
});
|
|
55
|
+
if (normalized.changed) {
|
|
56
|
+
updatedZalouser = {
|
|
57
|
+
...updatedZalouser,
|
|
58
|
+
groups: normalized.groups
|
|
59
|
+
};
|
|
60
|
+
changed = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const accounts = asObjectRecord(updatedZalouser.accounts);
|
|
64
|
+
if (accounts) {
|
|
65
|
+
let accountsChanged = false;
|
|
66
|
+
const nextAccounts = { ...accounts };
|
|
67
|
+
for (const [accountId, accountValue] of Object.entries(accounts)) {
|
|
68
|
+
const account = asObjectRecord(accountValue);
|
|
69
|
+
if (!account) continue;
|
|
70
|
+
const accountGroups = asObjectRecord(account.groups);
|
|
71
|
+
if (!accountGroups) continue;
|
|
72
|
+
const normalized = normalizeZalouserGroupAllowAliases({
|
|
73
|
+
groups: accountGroups,
|
|
74
|
+
pathPrefix: `channels.zalouser.accounts.${accountId}.groups`,
|
|
75
|
+
changes
|
|
76
|
+
});
|
|
77
|
+
if (!normalized.changed) continue;
|
|
78
|
+
nextAccounts[accountId] = {
|
|
79
|
+
...account,
|
|
80
|
+
groups: normalized.groups
|
|
81
|
+
};
|
|
82
|
+
accountsChanged = true;
|
|
83
|
+
}
|
|
84
|
+
if (accountsChanged) {
|
|
85
|
+
updatedZalouser = {
|
|
86
|
+
...updatedZalouser,
|
|
87
|
+
accounts: nextAccounts
|
|
88
|
+
};
|
|
89
|
+
changed = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!changed) return {
|
|
93
|
+
config: cfg,
|
|
94
|
+
changes: []
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
config: {
|
|
98
|
+
...cfg,
|
|
99
|
+
channels: {
|
|
100
|
+
...cfg.channels,
|
|
101
|
+
zalouser: updatedZalouser
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
changes
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const legacyConfigRules = [{
|
|
108
|
+
path: [
|
|
109
|
+
"channels",
|
|
110
|
+
"zalouser",
|
|
111
|
+
"groups"
|
|
112
|
+
],
|
|
113
|
+
message: "channels.zalouser.groups.<id>.allow is legacy; use channels.zalouser.groups.<id>.enabled instead. Run \"openclaw doctor --fix\".",
|
|
114
|
+
match: hasLegacyZalouserGroupAllowAliases
|
|
115
|
+
}, {
|
|
116
|
+
path: [
|
|
117
|
+
"channels",
|
|
118
|
+
"zalouser",
|
|
119
|
+
"accounts"
|
|
120
|
+
],
|
|
121
|
+
message: "channels.zalouser.accounts.<id>.groups.<id>.allow is legacy; use channels.zalouser.accounts.<id>.groups.<id>.enabled instead. Run \"openclaw doctor --fix\".",
|
|
122
|
+
match: hasLegacyZalouserAccountGroupAllowAliases
|
|
123
|
+
}];
|
|
124
|
+
function normalizeCompatibilityConfig(params) {
|
|
125
|
+
return normalizeZalouserCompatibilityConfig(params.cfg);
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { normalizeCompatibilityConfig as n, legacyConfigRules as t };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineBundledChannelEntry, loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/zalouser/index.ts
|
|
3
|
+
function createZalouserTool(context) {
|
|
4
|
+
return loadBundledEntryExportSync(import.meta.url, {
|
|
5
|
+
specifier: "./api.js",
|
|
6
|
+
exportName: "createZalouserTool"
|
|
7
|
+
})(context);
|
|
8
|
+
}
|
|
9
|
+
var zalouser_default = defineBundledChannelEntry({
|
|
10
|
+
id: "zalouser",
|
|
11
|
+
name: "Zalo Personal",
|
|
12
|
+
description: "Zalo personal account messaging via native zca-js integration",
|
|
13
|
+
importMetaUrl: import.meta.url,
|
|
14
|
+
plugin: {
|
|
15
|
+
specifier: "./channel-plugin-api.js",
|
|
16
|
+
exportName: "zalouserPlugin"
|
|
17
|
+
},
|
|
18
|
+
runtime: {
|
|
19
|
+
specifier: "./runtime-api.js",
|
|
20
|
+
exportName: "setZalouserRuntime"
|
|
21
|
+
},
|
|
22
|
+
registerFull(api) {
|
|
23
|
+
api.registerTool((ctx) => createZalouserTool(ctx), { name: "zalouser" });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
//#endregion
|
|
27
|
+
export { zalouser_default as default };
|