@kodelyth/zalouser 2026.5.39 → 2026.5.42
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/README.md +120 -0
- package/api.ts +9 -0
- package/channel-plugin-api.ts +3 -0
- package/contract-api.ts +2 -0
- package/dist/accounts-DOefD_if.js +66 -0
- package/dist/accounts.runtime-KT101uuu.js +2 -0
- package/dist/api-DSWT4Dh_.js +133 -0
- package/dist/api.js +7 -0
- package/dist/channel-pby_3Sur.js +602 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-0aJ2O7Y8.js +25 -0
- package/dist/channel.setup-CqyWwqcQ.js +9 -0
- package/dist/contract-api.js +3 -0
- package/dist/doctor-contract-B9EvrW0j.js +128 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +27 -0
- package/dist/monitor-CVtrUqyW.js +708 -0
- package/dist/runtime-api.js +19 -0
- package/dist/secret-contract-api.js +5 -0
- package/dist/security-audit-D_rftvs-.js +34 -0
- package/dist/send-uRjUB8mG.js +542 -0
- package/dist/session-route-CalHiv1d.js +92 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/setup-surface-Cfj4GQlB.js +360 -0
- package/dist/shared-DjK0e2FC.js +160 -0
- package/dist/test-api.js +5 -0
- package/dist/zalo-js-B80cRyDF.js +1285 -0
- package/doctor-contract-api.ts +1 -0
- package/index.ts +34 -0
- package/klaw.plugin.json +3 -286
- package/package.json +4 -4
- package/runtime-api.ts +62 -0
- package/secret-contract-api.ts +4 -0
- package/setup-entry.ts +9 -0
- package/setup-plugin-api.ts +2 -0
- package/src/accounts.runtime.ts +1 -0
- package/src/accounts.test-mocks.ts +14 -0
- package/src/accounts.test.ts +298 -0
- package/src/accounts.ts +136 -0
- package/src/channel-api.ts +16 -0
- package/src/channel.adapters.ts +432 -0
- package/src/channel.directory.test.ts +59 -0
- package/src/channel.runtime.ts +12 -0
- package/src/channel.sendpayload.test.ts +311 -0
- package/src/channel.setup.test.ts +30 -0
- package/src/channel.setup.ts +12 -0
- package/src/channel.test.ts +424 -0
- package/src/channel.ts +221 -0
- package/src/config-schema.ts +33 -0
- package/src/directory.ts +54 -0
- package/src/doctor-contract.ts +156 -0
- package/src/doctor.test.ts +87 -0
- package/src/doctor.ts +37 -0
- package/src/group-policy.test.ts +61 -0
- package/src/group-policy.ts +83 -0
- package/src/message-sid.test.ts +66 -0
- package/src/message-sid.ts +80 -0
- package/src/monitor.account-scope.test.ts +122 -0
- package/src/monitor.group-gating.test.ts +967 -0
- package/src/monitor.send-mocks.ts +20 -0
- package/src/monitor.ts +1057 -0
- package/src/probe.test.ts +60 -0
- package/src/probe.ts +35 -0
- package/src/qr-temp-file.ts +19 -0
- package/src/reaction.test.ts +19 -0
- package/src/reaction.ts +32 -0
- package/src/runtime.ts +9 -0
- package/src/security-audit.test.ts +83 -0
- package/src/security-audit.ts +71 -0
- package/src/send-receipt.ts +31 -0
- package/src/send.test.ts +424 -0
- package/src/send.ts +280 -0
- package/src/session-route.ts +121 -0
- package/src/setup-core.ts +36 -0
- package/src/setup-surface.test.ts +367 -0
- package/src/setup-surface.ts +481 -0
- package/src/setup-test-helpers.ts +42 -0
- package/src/shared.ts +92 -0
- package/src/status-issues.test.ts +31 -0
- package/src/status-issues.ts +55 -0
- package/src/test-helpers.ts +26 -0
- package/src/text-styles.test.ts +203 -0
- package/src/text-styles.ts +540 -0
- package/src/tool.test.ts +212 -0
- package/src/tool.ts +200 -0
- package/src/types.ts +127 -0
- package/src/zalo-js.credentials.test.ts +465 -0
- package/src/zalo-js.test-mocks.ts +89 -0
- package/src/zalo-js.ts +1889 -0
- package/src/zca-client.test.ts +27 -0
- package/src/zca-client.ts +259 -0
- package/src/zca-constants.ts +55 -0
- package/src/zca-js-exports.d.ts +22 -0
- package/test-api.ts +21 -0
- package/tsconfig.json +16 -0
- package/api.js +0 -7
- package/channel-plugin-api.js +0 -7
- package/contract-api.js +0 -7
- package/doctor-contract-api.js +0 -7
- package/index.js +0 -7
- package/runtime-api.js +0 -7
- package/secret-contract-api.js +0 -7
- package/setup-entry.js +0 -7
- package/setup-plugin-api.js +0 -7
- package/test-api.js +0 -7
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import { a as resolveZalouserAccountSync, i as resolveDefaultZalouserAccountId, r as listZalouserAccountIds, t as checkZcaAuthenticated } from "./accounts-DOefD_if.js";
|
|
2
|
+
import { a as DEFAULT_ACCOUNT_ID, c as isNumericTargetId, i as writeQrDataUrlToTempFile, l as normalizeAccountId, n as createZalouserSetupWizardProxy, o as chunkTextForOutbound, r as zalouserSetupAdapter, s as isDangerousNameMatchingEnabled, t as createZalouserPluginBase, u as sendPayloadWithChunkedTextAndMedia } from "./shared-DjK0e2FC.js";
|
|
3
|
+
import { i as resolveZalouserOutboundSessionRoute, n as parseZalouserDirectoryGroupId, r as parseZalouserOutboundTarget, t as normalizeZalouserTarget } from "./session-route-CalHiv1d.js";
|
|
4
|
+
import { createChatChannelPlugin } from "klaw/plugin-sdk/channel-core";
|
|
5
|
+
import { createAccountStatusSink } from "klaw/plugin-sdk/channel-lifecycle";
|
|
6
|
+
import { buildPassiveProbedChannelStatusSummary, coerceStatusIssueAccountId, readStatusIssueFields } from "klaw/plugin-sdk/extension-shared";
|
|
7
|
+
import { createLazyRuntimeModule } from "klaw/plugin-sdk/lazy-runtime";
|
|
8
|
+
import { createAsyncComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "klaw/plugin-sdk/status-helpers";
|
|
9
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
10
|
+
import { createScopedDmSecurityResolver } from "klaw/plugin-sdk/channel-config-helpers";
|
|
11
|
+
import { defineChannelMessageAdapter } from "klaw/plugin-sdk/channel-message";
|
|
12
|
+
import { createPairingPrefixStripper } from "klaw/plugin-sdk/channel-pairing";
|
|
13
|
+
import { createEmptyChannelResult, createRawChannelSendResultAdapter } from "klaw/plugin-sdk/channel-send-result";
|
|
14
|
+
import { createStaticReplyToModeResolver } from "klaw/plugin-sdk/conversation-runtime";
|
|
15
|
+
import { createPluginRuntimeStore } from "klaw/plugin-sdk/runtime-store";
|
|
16
|
+
//#region extensions/zalouser/src/group-policy.ts
|
|
17
|
+
function toGroupCandidate(value) {
|
|
18
|
+
return value?.trim() ?? "";
|
|
19
|
+
}
|
|
20
|
+
function normalizeZalouserGroupSlug(raw) {
|
|
21
|
+
const trimmed = normalizeOptionalLowercaseString(raw) ?? "";
|
|
22
|
+
if (!trimmed) return "";
|
|
23
|
+
return trimmed.replace(/^#/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
24
|
+
}
|
|
25
|
+
function buildZalouserGroupCandidates(params) {
|
|
26
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27
|
+
const out = [];
|
|
28
|
+
const push = (value) => {
|
|
29
|
+
const normalized = toGroupCandidate(value);
|
|
30
|
+
if (!normalized || seen.has(normalized)) return;
|
|
31
|
+
seen.add(normalized);
|
|
32
|
+
out.push(normalized);
|
|
33
|
+
};
|
|
34
|
+
const groupId = toGroupCandidate(params.groupId);
|
|
35
|
+
const groupChannel = toGroupCandidate(params.groupChannel);
|
|
36
|
+
const groupName = toGroupCandidate(params.groupName);
|
|
37
|
+
push(groupId);
|
|
38
|
+
if (params.includeGroupIdAlias === true && groupId) push(`group:${groupId}`);
|
|
39
|
+
if (params.allowNameMatching !== false) {
|
|
40
|
+
push(groupChannel);
|
|
41
|
+
push(groupName);
|
|
42
|
+
if (groupName) push(normalizeZalouserGroupSlug(groupName));
|
|
43
|
+
}
|
|
44
|
+
if (params.includeWildcard !== false) push("*");
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function findZalouserGroupEntry(groups, candidates) {
|
|
48
|
+
if (!groups) return;
|
|
49
|
+
for (const candidate of candidates) {
|
|
50
|
+
const entry = groups[candidate];
|
|
51
|
+
if (entry) return entry;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isZalouserGroupEntryAllowed(entry) {
|
|
55
|
+
if (!entry) return false;
|
|
56
|
+
return entry.allow !== false && entry.enabled !== false;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region extensions/zalouser/src/message-sid.ts
|
|
60
|
+
function toMessageSidPart(value) {
|
|
61
|
+
if (typeof value === "string") return value.trim();
|
|
62
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(Math.trunc(value));
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
function parseZalouserMessageSidFull(value) {
|
|
66
|
+
const raw = toMessageSidPart(value);
|
|
67
|
+
if (!raw) return null;
|
|
68
|
+
const [msgIdPart, cliMsgIdPart] = raw.split(":").map((entry) => entry.trim());
|
|
69
|
+
if (!msgIdPart || !cliMsgIdPart) return null;
|
|
70
|
+
return {
|
|
71
|
+
msgId: msgIdPart,
|
|
72
|
+
cliMsgId: cliMsgIdPart
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function resolveZalouserReactionMessageIds(params) {
|
|
76
|
+
const explicitMessageId = toMessageSidPart(params.messageId);
|
|
77
|
+
const explicitCliMsgId = toMessageSidPart(params.cliMsgId);
|
|
78
|
+
if (explicitMessageId && explicitCliMsgId) return {
|
|
79
|
+
msgId: explicitMessageId,
|
|
80
|
+
cliMsgId: explicitCliMsgId
|
|
81
|
+
};
|
|
82
|
+
const parsedFromCurrent = parseZalouserMessageSidFull(params.currentMessageId);
|
|
83
|
+
if (parsedFromCurrent) return parsedFromCurrent;
|
|
84
|
+
const currentRaw = toMessageSidPart(params.currentMessageId);
|
|
85
|
+
if (!currentRaw) return null;
|
|
86
|
+
if (explicitMessageId && !explicitCliMsgId) return {
|
|
87
|
+
msgId: explicitMessageId,
|
|
88
|
+
cliMsgId: currentRaw
|
|
89
|
+
};
|
|
90
|
+
if (!explicitMessageId && explicitCliMsgId) return {
|
|
91
|
+
msgId: currentRaw,
|
|
92
|
+
cliMsgId: explicitCliMsgId
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
msgId: currentRaw,
|
|
96
|
+
cliMsgId: currentRaw
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function formatZalouserMessageSidFull(params) {
|
|
100
|
+
const msgId = toMessageSidPart(params.msgId);
|
|
101
|
+
const cliMsgId = toMessageSidPart(params.cliMsgId);
|
|
102
|
+
if (!msgId && !cliMsgId) return;
|
|
103
|
+
if (msgId && cliMsgId) return `${msgId}:${cliMsgId}`;
|
|
104
|
+
return msgId || cliMsgId || void 0;
|
|
105
|
+
}
|
|
106
|
+
function resolveZalouserMessageSid(params) {
|
|
107
|
+
const msgId = toMessageSidPart(params.msgId);
|
|
108
|
+
const cliMsgId = toMessageSidPart(params.cliMsgId);
|
|
109
|
+
if (msgId || cliMsgId) return msgId || cliMsgId;
|
|
110
|
+
return toMessageSidPart(params.fallback) || void 0;
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region extensions/zalouser/src/runtime.ts
|
|
114
|
+
const { setRuntime: setZalouserRuntime, getRuntime: getZalouserRuntime } = createPluginRuntimeStore({
|
|
115
|
+
pluginId: "zalouser",
|
|
116
|
+
errorMessage: "Zalouser runtime not initialized"
|
|
117
|
+
});
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region extensions/zalouser/src/channel.adapters.ts
|
|
120
|
+
const loadZalouserChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel.runtime-0aJ2O7Y8.js"));
|
|
121
|
+
const ZALOUSER_TEXT_CHUNK_LIMIT = 2e3;
|
|
122
|
+
function resolveZalouserQrProfile(accountId) {
|
|
123
|
+
const normalized = normalizeAccountId(accountId);
|
|
124
|
+
if (!normalized || normalized === DEFAULT_ACCOUNT_ID) return process.env.ZALOUSER_PROFILE?.trim() || process.env.ZCA_PROFILE?.trim() || "default";
|
|
125
|
+
return normalized;
|
|
126
|
+
}
|
|
127
|
+
function resolveZalouserOutboundChunkMode(cfg, accountId) {
|
|
128
|
+
return getZalouserRuntime().channel.text.resolveChunkMode(cfg, "zalouser", accountId);
|
|
129
|
+
}
|
|
130
|
+
function resolveZalouserOutboundTextChunkLimit(cfg, accountId) {
|
|
131
|
+
return getZalouserRuntime().channel.text.resolveTextChunkLimit(cfg, "zalouser", accountId, { fallbackLimit: ZALOUSER_TEXT_CHUNK_LIMIT });
|
|
132
|
+
}
|
|
133
|
+
function resolveZalouserGroupPolicyEntry(params) {
|
|
134
|
+
const account = resolveZalouserAccountSync({
|
|
135
|
+
cfg: params.cfg,
|
|
136
|
+
accountId: params.accountId ?? void 0
|
|
137
|
+
});
|
|
138
|
+
return findZalouserGroupEntry(account.config.groups ?? {}, buildZalouserGroupCandidates({
|
|
139
|
+
groupId: params.groupId,
|
|
140
|
+
groupChannel: params.groupChannel,
|
|
141
|
+
includeWildcard: true,
|
|
142
|
+
allowNameMatching: isDangerousNameMatchingEnabled(account.config)
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
function resolveZalouserGroupToolPolicy(params) {
|
|
146
|
+
return resolveZalouserGroupPolicyEntry(params)?.tools;
|
|
147
|
+
}
|
|
148
|
+
function resolveZalouserRequireMention(params) {
|
|
149
|
+
const entry = resolveZalouserGroupPolicyEntry(params);
|
|
150
|
+
if (typeof entry?.requireMention === "boolean") return entry.requireMention;
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
async function sendZalouserTextFromContext({ to, text, accountId, cfg }) {
|
|
154
|
+
const { sendMessageZalouser } = await loadZalouserChannelRuntime$1();
|
|
155
|
+
const account = resolveZalouserAccountSync({
|
|
156
|
+
cfg,
|
|
157
|
+
accountId
|
|
158
|
+
});
|
|
159
|
+
const target = parseZalouserOutboundTarget(to);
|
|
160
|
+
return await sendMessageZalouser(target.threadId, text, {
|
|
161
|
+
profile: account.profile,
|
|
162
|
+
isGroup: target.isGroup,
|
|
163
|
+
textMode: "markdown",
|
|
164
|
+
textChunkMode: resolveZalouserOutboundChunkMode(cfg, account.accountId),
|
|
165
|
+
textChunkLimit: resolveZalouserOutboundTextChunkLimit(cfg, account.accountId)
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async function sendZalouserMediaFromContext({ to, text, mediaUrl, accountId, cfg, mediaLocalRoots, mediaReadFile }) {
|
|
169
|
+
const { sendMessageZalouser } = await loadZalouserChannelRuntime$1();
|
|
170
|
+
const account = resolveZalouserAccountSync({
|
|
171
|
+
cfg,
|
|
172
|
+
accountId
|
|
173
|
+
});
|
|
174
|
+
const target = parseZalouserOutboundTarget(to);
|
|
175
|
+
return await sendMessageZalouser(target.threadId, text, {
|
|
176
|
+
profile: account.profile,
|
|
177
|
+
isGroup: target.isGroup,
|
|
178
|
+
mediaUrl,
|
|
179
|
+
mediaLocalRoots,
|
|
180
|
+
mediaReadFile,
|
|
181
|
+
textMode: "markdown",
|
|
182
|
+
textChunkMode: resolveZalouserOutboundChunkMode(cfg, account.accountId),
|
|
183
|
+
textChunkLimit: resolveZalouserOutboundTextChunkLimit(cfg, account.accountId)
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const zalouserRawSendResultAdapter = createRawChannelSendResultAdapter({
|
|
187
|
+
channel: "zalouser",
|
|
188
|
+
sendText: sendZalouserTextFromContext,
|
|
189
|
+
sendMedia: sendZalouserMediaFromContext
|
|
190
|
+
});
|
|
191
|
+
const zalouserMessageAdapter = defineChannelMessageAdapter({
|
|
192
|
+
id: "zalouser",
|
|
193
|
+
durableFinal: { capabilities: {
|
|
194
|
+
text: true,
|
|
195
|
+
media: true,
|
|
196
|
+
messageSendingHooks: true
|
|
197
|
+
} },
|
|
198
|
+
send: {
|
|
199
|
+
text: sendZalouserTextFromContext,
|
|
200
|
+
media: sendZalouserMediaFromContext
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
const resolveZalouserDmPolicy = createScopedDmSecurityResolver({
|
|
204
|
+
channelKey: "zalouser",
|
|
205
|
+
resolvePolicy: (account) => account.config.dmPolicy,
|
|
206
|
+
resolveAllowFrom: (account) => account.config.allowFrom,
|
|
207
|
+
policyPathSuffix: "dmPolicy",
|
|
208
|
+
normalizeEntry: (raw) => raw.trim().replace(/^(zalouser|zlu):/i, "")
|
|
209
|
+
});
|
|
210
|
+
const zalouserGroupsAdapter = {
|
|
211
|
+
resolveRequireMention: resolveZalouserRequireMention,
|
|
212
|
+
resolveToolPolicy: resolveZalouserGroupToolPolicy
|
|
213
|
+
};
|
|
214
|
+
const zalouserMessageActions = {
|
|
215
|
+
describeMessageTool: ({ cfg, accountId }) => {
|
|
216
|
+
if ((accountId ? [resolveZalouserAccountSync({
|
|
217
|
+
cfg,
|
|
218
|
+
accountId
|
|
219
|
+
})].filter((account) => account.enabled) : listZalouserAccountIds(cfg).map((resolvedAccountId) => resolveZalouserAccountSync({
|
|
220
|
+
cfg,
|
|
221
|
+
accountId: resolvedAccountId
|
|
222
|
+
})).filter((account) => account.enabled)).length === 0) return null;
|
|
223
|
+
return { actions: ["react"] };
|
|
224
|
+
},
|
|
225
|
+
supportsAction: ({ action }) => action === "react",
|
|
226
|
+
handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
|
|
227
|
+
if (action !== "react") throw new Error(`Zalouser action ${action} not supported`);
|
|
228
|
+
const { sendReactionZalouser } = await loadZalouserChannelRuntime$1();
|
|
229
|
+
const account = resolveZalouserAccountSync({
|
|
230
|
+
cfg,
|
|
231
|
+
accountId
|
|
232
|
+
});
|
|
233
|
+
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() ?? "");
|
|
234
|
+
if (!threadId) throw new Error("Zalouser react requires threadId (or to/chatId).");
|
|
235
|
+
const emoji = typeof params.emoji === "string" ? params.emoji.trim() : "";
|
|
236
|
+
if (!emoji) throw new Error("Zalouser react requires emoji.");
|
|
237
|
+
const ids = resolveZalouserReactionMessageIds({
|
|
238
|
+
messageId: typeof params.messageId === "string" ? params.messageId : void 0,
|
|
239
|
+
cliMsgId: typeof params.cliMsgId === "string" ? params.cliMsgId : void 0,
|
|
240
|
+
currentMessageId: toolContext?.currentMessageId
|
|
241
|
+
});
|
|
242
|
+
if (!ids) throw new Error("Zalouser react requires messageId + cliMsgId (or a current message context id).");
|
|
243
|
+
const result = await sendReactionZalouser({
|
|
244
|
+
profile: account.profile,
|
|
245
|
+
threadId,
|
|
246
|
+
isGroup: params.isGroup === true,
|
|
247
|
+
msgId: ids.msgId,
|
|
248
|
+
cliMsgId: ids.cliMsgId,
|
|
249
|
+
emoji,
|
|
250
|
+
remove: params.remove === true
|
|
251
|
+
});
|
|
252
|
+
if (!result.ok) throw new Error(result.error || "Failed to react on Zalo message");
|
|
253
|
+
return {
|
|
254
|
+
content: [{
|
|
255
|
+
type: "text",
|
|
256
|
+
text: params.remove === true ? `Removed reaction ${emoji} from ${ids.msgId}` : `Reacted ${emoji} on ${ids.msgId}`
|
|
257
|
+
}],
|
|
258
|
+
details: {
|
|
259
|
+
messageId: ids.msgId,
|
|
260
|
+
cliMsgId: ids.cliMsgId,
|
|
261
|
+
threadId
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
const zalouserResolverAdapter = { resolveTargets: async ({ cfg, accountId, inputs, kind, runtime }) => {
|
|
267
|
+
const results = [];
|
|
268
|
+
for (const input of inputs) {
|
|
269
|
+
const trimmed = input.trim();
|
|
270
|
+
if (!trimmed) {
|
|
271
|
+
results.push({
|
|
272
|
+
input,
|
|
273
|
+
resolved: false,
|
|
274
|
+
note: "empty input"
|
|
275
|
+
});
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (/^\d+$/.test(trimmed)) {
|
|
279
|
+
results.push({
|
|
280
|
+
input,
|
|
281
|
+
resolved: true,
|
|
282
|
+
id: trimmed
|
|
283
|
+
});
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const runtimeModule = await loadZalouserChannelRuntime$1();
|
|
288
|
+
const account = resolveZalouserAccountSync({
|
|
289
|
+
cfg,
|
|
290
|
+
accountId: accountId ?? resolveDefaultZalouserAccountId(cfg)
|
|
291
|
+
});
|
|
292
|
+
if (kind === "user") {
|
|
293
|
+
const friends = await runtimeModule.listZaloFriendsMatching(account.profile, trimmed);
|
|
294
|
+
const best = friends[0];
|
|
295
|
+
results.push({
|
|
296
|
+
input,
|
|
297
|
+
resolved: Boolean(best?.userId),
|
|
298
|
+
id: best?.userId,
|
|
299
|
+
name: best?.displayName,
|
|
300
|
+
note: friends.length > 1 ? "multiple matches; chose first" : void 0
|
|
301
|
+
});
|
|
302
|
+
} else {
|
|
303
|
+
const groups = await runtimeModule.listZaloGroupsMatching(account.profile, trimmed);
|
|
304
|
+
const best = groups.find((group) => normalizeLowercaseStringOrEmpty(group.name) === normalizeLowercaseStringOrEmpty(trimmed)) ?? groups[0];
|
|
305
|
+
results.push({
|
|
306
|
+
input,
|
|
307
|
+
resolved: Boolean(best?.groupId),
|
|
308
|
+
id: best?.groupId,
|
|
309
|
+
name: best?.name,
|
|
310
|
+
note: groups.length > 1 ? "multiple matches; chose first" : void 0
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
} catch (err) {
|
|
314
|
+
runtime.error?.(`zalouser resolve failed: ${String(err)}`);
|
|
315
|
+
results.push({
|
|
316
|
+
input,
|
|
317
|
+
resolved: false,
|
|
318
|
+
note: "lookup failed"
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return results;
|
|
323
|
+
} };
|
|
324
|
+
const zalouserAuthAdapter = { login: async ({ cfg, accountId, runtime }) => {
|
|
325
|
+
const { startZaloQrLogin, waitForZaloQrLogin } = await loadZalouserChannelRuntime$1();
|
|
326
|
+
const account = resolveZalouserAccountSync({
|
|
327
|
+
cfg,
|
|
328
|
+
accountId: accountId ?? resolveDefaultZalouserAccountId(cfg)
|
|
329
|
+
});
|
|
330
|
+
runtime.log(`Generating QR login for Zalo Personal (account: ${account.accountId}, profile: ${account.profile})...`);
|
|
331
|
+
const started = await startZaloQrLogin({
|
|
332
|
+
profile: account.profile,
|
|
333
|
+
timeoutMs: 35e3
|
|
334
|
+
});
|
|
335
|
+
if (!started.qrDataUrl) throw new Error(started.message || "Failed to start QR login");
|
|
336
|
+
const qrPath = await writeQrDataUrlToTempFile(started.qrDataUrl, account.profile);
|
|
337
|
+
if (qrPath) runtime.log(`Scan QR image: ${qrPath}`);
|
|
338
|
+
else runtime.log("QR generated but could not be written to a temp file.");
|
|
339
|
+
const waited = await waitForZaloQrLogin({
|
|
340
|
+
profile: account.profile,
|
|
341
|
+
timeoutMs: 18e4
|
|
342
|
+
});
|
|
343
|
+
if (!waited.connected) throw new Error(waited.message || "Zalouser login failed");
|
|
344
|
+
runtime.log(waited.message);
|
|
345
|
+
} };
|
|
346
|
+
const zalouserSecurityAdapter = {
|
|
347
|
+
resolveDmPolicy: resolveZalouserDmPolicy,
|
|
348
|
+
collectAuditFindings: async (params) => (await loadZalouserChannelRuntime$1()).collectZalouserSecurityAuditFindings(params)
|
|
349
|
+
};
|
|
350
|
+
const zalouserThreadingAdapter = { resolveReplyToMode: createStaticReplyToModeResolver("off") };
|
|
351
|
+
const zalouserPairingTextAdapter = {
|
|
352
|
+
idLabel: "zalouserUserId",
|
|
353
|
+
message: "Your pairing request has been approved.",
|
|
354
|
+
normalizeAllowEntry: createPairingPrefixStripper(/^(zalouser|zlu):/i),
|
|
355
|
+
notify: async ({ cfg, id, message }) => {
|
|
356
|
+
const { sendMessageZalouser } = await loadZalouserChannelRuntime$1();
|
|
357
|
+
const account = resolveZalouserAccountSync({ cfg });
|
|
358
|
+
if (!await checkZcaAuthenticated(account.profile)) throw new Error("Zalouser not authenticated");
|
|
359
|
+
await sendMessageZalouser(id, message, { profile: account.profile });
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const zalouserOutboundAdapter = {
|
|
363
|
+
deliveryMode: "direct",
|
|
364
|
+
chunker: chunkTextForOutbound,
|
|
365
|
+
chunkerMode: "markdown",
|
|
366
|
+
sendPayload: async (ctx) => await sendPayloadWithChunkedTextAndMedia({
|
|
367
|
+
ctx,
|
|
368
|
+
sendText: (nextCtx) => zalouserRawSendResultAdapter.sendText(nextCtx),
|
|
369
|
+
sendMedia: (nextCtx) => zalouserRawSendResultAdapter.sendMedia(nextCtx),
|
|
370
|
+
emptyResult: createEmptyChannelResult("zalouser")
|
|
371
|
+
}),
|
|
372
|
+
...zalouserRawSendResultAdapter
|
|
373
|
+
};
|
|
374
|
+
const zalouserMessagingAdapter = {
|
|
375
|
+
targetPrefixes: ["zalouser", "zlu"],
|
|
376
|
+
normalizeTarget: (raw) => normalizeZalouserTarget(raw),
|
|
377
|
+
resolveOutboundSessionRoute: (params) => resolveZalouserOutboundSessionRoute(params),
|
|
378
|
+
targetResolver: {
|
|
379
|
+
looksLikeId: (raw) => {
|
|
380
|
+
const normalized = normalizeZalouserTarget(raw);
|
|
381
|
+
if (!normalized) return false;
|
|
382
|
+
if (/^group:[^\s]+$/i.test(normalized) || /^user:[^\s]+$/i.test(normalized)) return true;
|
|
383
|
+
return isNumericTargetId(normalized);
|
|
384
|
+
},
|
|
385
|
+
hint: "<user:id|group:id>"
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region extensions/zalouser/src/directory.ts
|
|
390
|
+
function mapUser$1(params) {
|
|
391
|
+
return {
|
|
392
|
+
kind: "user",
|
|
393
|
+
id: params.id,
|
|
394
|
+
name: params.name ?? void 0,
|
|
395
|
+
avatarUrl: params.avatarUrl ?? void 0,
|
|
396
|
+
raw: params.raw
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
async function listZalouserDirectoryGroupMembers(params, deps) {
|
|
400
|
+
const account = resolveZalouserAccountSync({
|
|
401
|
+
cfg: params.cfg,
|
|
402
|
+
accountId: params.accountId
|
|
403
|
+
});
|
|
404
|
+
const normalizedGroupId = parseZalouserDirectoryGroupId(params.groupId);
|
|
405
|
+
const rows = (await deps.listZaloGroupMembers(account.profile, normalizedGroupId)).map((member) => mapUser$1({
|
|
406
|
+
id: member.userId,
|
|
407
|
+
name: member.displayName,
|
|
408
|
+
avatarUrl: member.avatar ?? null,
|
|
409
|
+
raw: member
|
|
410
|
+
}));
|
|
411
|
+
return typeof params.limit === "number" && params.limit > 0 ? rows.slice(0, params.limit) : rows;
|
|
412
|
+
}
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region extensions/zalouser/src/status-issues.ts
|
|
415
|
+
const ZALOUSER_STATUS_FIELDS = [
|
|
416
|
+
"accountId",
|
|
417
|
+
"enabled",
|
|
418
|
+
"configured",
|
|
419
|
+
"dmPolicy",
|
|
420
|
+
"lastError"
|
|
421
|
+
];
|
|
422
|
+
function collectZalouserStatusIssues(accounts) {
|
|
423
|
+
const issues = [];
|
|
424
|
+
for (const entry of accounts) {
|
|
425
|
+
const account = readStatusIssueFields(entry, ZALOUSER_STATUS_FIELDS);
|
|
426
|
+
if (!account) continue;
|
|
427
|
+
const accountId = coerceStatusIssueAccountId(account.accountId) ?? "default";
|
|
428
|
+
if (!(account.enabled !== false)) continue;
|
|
429
|
+
if (!(account.configured === true)) {
|
|
430
|
+
issues.push({
|
|
431
|
+
channel: "zalouser",
|
|
432
|
+
accountId,
|
|
433
|
+
kind: "auth",
|
|
434
|
+
message: "Not authenticated (no saved Zalo session).",
|
|
435
|
+
fix: "Run: klaw channels login --channel zalouser"
|
|
436
|
+
});
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (account.dmPolicy === "open") issues.push({
|
|
440
|
+
channel: "zalouser",
|
|
441
|
+
accountId,
|
|
442
|
+
kind: "config",
|
|
443
|
+
message: "Zalo Personal dmPolicy is \"open\", allowing any user to message the bot without pairing.",
|
|
444
|
+
fix: "Set channels.zalouser.dmPolicy to \"pairing\" or \"allowlist\" to restrict access."
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return issues;
|
|
448
|
+
}
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region extensions/zalouser/src/channel.ts
|
|
451
|
+
const loadZalouserChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-0aJ2O7Y8.js"));
|
|
452
|
+
const zalouserSetupWizardProxy = createZalouserSetupWizardProxy(async () => (await import("./setup-surface-Cfj4GQlB.js").then((n) => n.t)).zalouserSetupWizard);
|
|
453
|
+
function mapUser(params) {
|
|
454
|
+
return {
|
|
455
|
+
kind: "user",
|
|
456
|
+
id: params.id,
|
|
457
|
+
name: params.name ?? void 0,
|
|
458
|
+
avatarUrl: params.avatarUrl ?? void 0,
|
|
459
|
+
raw: params.raw
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function mapGroup(params) {
|
|
463
|
+
return {
|
|
464
|
+
kind: "group",
|
|
465
|
+
id: params.id,
|
|
466
|
+
name: params.name ?? void 0,
|
|
467
|
+
raw: params.raw
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const zalouserPlugin = createChatChannelPlugin({
|
|
471
|
+
base: {
|
|
472
|
+
...createZalouserPluginBase({
|
|
473
|
+
setupWizard: zalouserSetupWizardProxy,
|
|
474
|
+
setup: zalouserSetupAdapter
|
|
475
|
+
}),
|
|
476
|
+
groups: zalouserGroupsAdapter,
|
|
477
|
+
actions: zalouserMessageActions,
|
|
478
|
+
messaging: zalouserMessagingAdapter,
|
|
479
|
+
directory: {
|
|
480
|
+
self: async ({ cfg, accountId }) => {
|
|
481
|
+
const { getZaloUserInfo } = await loadZalouserChannelRuntime();
|
|
482
|
+
const parsed = await getZaloUserInfo(resolveZalouserAccountSync({
|
|
483
|
+
cfg,
|
|
484
|
+
accountId
|
|
485
|
+
}).profile);
|
|
486
|
+
if (!parsed?.userId) return null;
|
|
487
|
+
return mapUser({
|
|
488
|
+
id: parsed.userId,
|
|
489
|
+
name: parsed.displayName ?? null,
|
|
490
|
+
avatarUrl: parsed.avatar ?? null,
|
|
491
|
+
raw: parsed
|
|
492
|
+
});
|
|
493
|
+
},
|
|
494
|
+
listPeers: async ({ cfg, accountId, query, limit }) => {
|
|
495
|
+
const { listZaloFriendsMatching } = await loadZalouserChannelRuntime();
|
|
496
|
+
const rows = (await listZaloFriendsMatching(resolveZalouserAccountSync({
|
|
497
|
+
cfg,
|
|
498
|
+
accountId
|
|
499
|
+
}).profile, query)).map((friend) => mapUser({
|
|
500
|
+
id: friend.userId,
|
|
501
|
+
name: friend.displayName ?? null,
|
|
502
|
+
avatarUrl: friend.avatar ?? null,
|
|
503
|
+
raw: friend
|
|
504
|
+
}));
|
|
505
|
+
return typeof limit === "number" && limit > 0 ? rows.slice(0, limit) : rows;
|
|
506
|
+
},
|
|
507
|
+
listGroups: async ({ cfg, accountId, query, limit }) => {
|
|
508
|
+
const { listZaloGroupsMatching } = await loadZalouserChannelRuntime();
|
|
509
|
+
const rows = (await listZaloGroupsMatching(resolveZalouserAccountSync({
|
|
510
|
+
cfg,
|
|
511
|
+
accountId
|
|
512
|
+
}).profile, query)).map((group) => mapGroup({
|
|
513
|
+
id: `group:${group.groupId}`,
|
|
514
|
+
name: group.name ?? null,
|
|
515
|
+
raw: group
|
|
516
|
+
}));
|
|
517
|
+
return typeof limit === "number" && limit > 0 ? rows.slice(0, limit) : rows;
|
|
518
|
+
},
|
|
519
|
+
listGroupMembers: async ({ cfg, accountId, groupId, limit }) => {
|
|
520
|
+
const { listZaloGroupMembers } = await loadZalouserChannelRuntime();
|
|
521
|
+
return await listZalouserDirectoryGroupMembers({
|
|
522
|
+
cfg,
|
|
523
|
+
accountId: accountId ?? void 0,
|
|
524
|
+
groupId,
|
|
525
|
+
limit: limit ?? void 0
|
|
526
|
+
}, { listZaloGroupMembers });
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
resolver: zalouserResolverAdapter,
|
|
530
|
+
auth: zalouserAuthAdapter,
|
|
531
|
+
message: zalouserMessageAdapter,
|
|
532
|
+
status: createAsyncComputedAccountStatusAdapter({
|
|
533
|
+
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
|
534
|
+
collectStatusIssues: collectZalouserStatusIssues,
|
|
535
|
+
buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot),
|
|
536
|
+
probeAccount: async ({ account, timeoutMs }) => (await loadZalouserChannelRuntime()).probeZalouser(account.profile, timeoutMs),
|
|
537
|
+
resolveAccountSnapshot: async ({ account, runtime }) => {
|
|
538
|
+
const configured = await checkZcaAuthenticated(account.profile);
|
|
539
|
+
return {
|
|
540
|
+
accountId: account.accountId,
|
|
541
|
+
name: account.name,
|
|
542
|
+
enabled: account.enabled,
|
|
543
|
+
configured,
|
|
544
|
+
extra: {
|
|
545
|
+
dmPolicy: account.config.dmPolicy ?? "pairing",
|
|
546
|
+
lastError: configured ? runtime?.lastError ?? null : runtime?.lastError ?? "not authenticated"
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
}),
|
|
551
|
+
gateway: {
|
|
552
|
+
startAccount: async (ctx) => {
|
|
553
|
+
const { getZaloUserInfo } = await loadZalouserChannelRuntime();
|
|
554
|
+
const account = ctx.account;
|
|
555
|
+
let userLabel = "";
|
|
556
|
+
try {
|
|
557
|
+
const userInfo = await getZaloUserInfo(account.profile);
|
|
558
|
+
if (userInfo?.displayName) userLabel = ` (${userInfo.displayName})`;
|
|
559
|
+
ctx.setStatus({
|
|
560
|
+
accountId: account.accountId,
|
|
561
|
+
profile: userInfo
|
|
562
|
+
});
|
|
563
|
+
} catch {}
|
|
564
|
+
const statusSink = createAccountStatusSink({
|
|
565
|
+
accountId: ctx.accountId,
|
|
566
|
+
setStatus: ctx.setStatus
|
|
567
|
+
});
|
|
568
|
+
ctx.log?.info(`[${account.accountId}] starting zalouser provider${userLabel}`);
|
|
569
|
+
const { monitorZalouserProvider } = await import("./monitor-CVtrUqyW.js");
|
|
570
|
+
return monitorZalouserProvider({
|
|
571
|
+
account,
|
|
572
|
+
config: ctx.cfg,
|
|
573
|
+
runtime: ctx.runtime,
|
|
574
|
+
abortSignal: ctx.abortSignal,
|
|
575
|
+
statusSink
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
loginWithQrStart: async (params) => {
|
|
579
|
+
const { startZaloQrLogin } = await loadZalouserChannelRuntime();
|
|
580
|
+
return await startZaloQrLogin({
|
|
581
|
+
profile: resolveZalouserQrProfile(params.accountId),
|
|
582
|
+
force: params.force,
|
|
583
|
+
timeoutMs: params.timeoutMs
|
|
584
|
+
});
|
|
585
|
+
},
|
|
586
|
+
loginWithQrWait: async (params) => {
|
|
587
|
+
const { waitForZaloQrLogin } = await loadZalouserChannelRuntime();
|
|
588
|
+
return await waitForZaloQrLogin({
|
|
589
|
+
profile: resolveZalouserQrProfile(params.accountId),
|
|
590
|
+
timeoutMs: params.timeoutMs
|
|
591
|
+
});
|
|
592
|
+
},
|
|
593
|
+
logoutAccount: async (ctx) => await (await loadZalouserChannelRuntime()).logoutZaloProfile(ctx.account.profile || resolveZalouserQrProfile(ctx.accountId))
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
security: zalouserSecurityAdapter,
|
|
597
|
+
threading: zalouserThreadingAdapter,
|
|
598
|
+
pairing: { text: zalouserPairingTextAdapter },
|
|
599
|
+
outbound: zalouserOutboundAdapter
|
|
600
|
+
});
|
|
601
|
+
//#endregion
|
|
602
|
+
export { resolveZalouserMessageSid as a, isZalouserGroupEntryAllowed as c, formatZalouserMessageSidFull as i, getZalouserRuntime as n, buildZalouserGroupCandidates as o, setZalouserRuntime as r, findZalouserGroupEntry as s, zalouserPlugin as t };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { t as collectZalouserSecurityAuditFindings } from "./security-audit-D_rftvs-.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-B80cRyDF.js";
|
|
3
|
+
import { a as sendReactionZalouser, i as sendMessageZalouser } from "./send-uRjUB8mG.js";
|
|
4
|
+
import { formatErrorMessage } from "klaw/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,9 @@
|
|
|
1
|
+
import { n as zalouserSetupWizard } from "./setup-surface-Cfj4GQlB.js";
|
|
2
|
+
import { r as zalouserSetupAdapter, t as createZalouserPluginBase } from "./shared-DjK0e2FC.js";
|
|
3
|
+
//#region extensions/zalouser/src/channel.setup.ts
|
|
4
|
+
const zalouserSetupPlugin = { ...createZalouserPluginBase({
|
|
5
|
+
setupWizard: zalouserSetupWizard,
|
|
6
|
+
setup: zalouserSetupAdapter
|
|
7
|
+
}) };
|
|
8
|
+
//#endregion
|
|
9
|
+
export { zalouserSetupPlugin as t };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-B9EvrW0j.js";
|
|
2
|
+
import { t as collectZalouserSecurityAuditFindings } from "./security-audit-D_rftvs-.js";
|
|
3
|
+
export { collectZalouserSecurityAuditFindings, legacyConfigRules, normalizeCompatibilityConfig };
|