@kodelyth/zalo 2026.5.39 → 2026.6.1

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 ADDED
@@ -0,0 +1,50 @@
1
+ # @klaw/zalo
2
+
3
+ Zalo channel plugin for Klaw (Bot API).
4
+
5
+ ## Install (local checkout)
6
+
7
+ ```bash
8
+ klaw plugins install ./path/to/local/zalo-plugin
9
+ ```
10
+
11
+ ## Install (npm)
12
+
13
+ ```bash
14
+ klaw plugins install @klaw/zalo
15
+ ```
16
+
17
+ Onboarding: select Zalo and confirm the install prompt to fetch the plugin automatically.
18
+
19
+ ## Config
20
+
21
+ ```json5
22
+ {
23
+ channels: {
24
+ zalo: {
25
+ enabled: true,
26
+ botToken: "12345689:abc-xyz",
27
+ dmPolicy: "pairing",
28
+ proxy: "http://proxy.local:8080",
29
+ },
30
+ },
31
+ }
32
+ ```
33
+
34
+ ## Webhook mode
35
+
36
+ ```json5
37
+ {
38
+ channels: {
39
+ zalo: {
40
+ webhookUrl: "https://example.com/zalo-webhook",
41
+ webhookSecret: "your-secret-8-plus-chars",
42
+ webhookPath: "/zalo-webhook",
43
+ },
44
+ },
45
+ }
46
+ ```
47
+
48
+ If `webhookPath` is omitted, the plugin uses the webhook URL path.
49
+
50
+ Restart the gateway after config changes.
@@ -0,0 +1,5 @@
1
+ import { t as sendMessageZalo } from "./send-CGAqdfSA.js";
2
+ //#region extensions/zalo/src/actions.runtime.ts
3
+ const zaloActionsRuntime = { sendMessageZalo };
4
+ //#endregion
5
+ export { zaloActionsRuntime };
package/dist/api.js ADDED
@@ -0,0 +1,5 @@
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-Dr75wK6l.js";
2
+ import { t as zaloPlugin } from "./channel-D8ylaEdN.js";
3
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-DTQVR6Nd.js";
4
+ import { zaloSetupWizard } from "./setup-api.js";
5
+ export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
@@ -0,0 +1,367 @@
1
+ import { c as listZaloAccountIds, f as buildSecretInputSchema, l as resolveDefaultZaloAccountId, r as zaloSetupAdapter, s as listEnabledZaloAccounts, t as createZaloSetupWizardProxy, u as resolveZaloAccount } from "./setup-core-Dr75wK6l.js";
2
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-CRFukr2n.js";
3
+ import { describeWebhookAccountSnapshot } from "klaw/plugin-sdk/account-helpers";
4
+ import { DEFAULT_ACCOUNT_ID } from "klaw/plugin-sdk/account-id";
5
+ import { formatAllowFromLowercase } from "klaw/plugin-sdk/allow-from";
6
+ import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, mapAllowFromEntries } from "klaw/plugin-sdk/channel-config-helpers";
7
+ import { buildChannelConfigSchema, createChatChannelPlugin } from "klaw/plugin-sdk/channel-core";
8
+ import { defineChannelMessageAdapter } from "klaw/plugin-sdk/channel-message";
9
+ import { buildOpenGroupPolicyRestrictSendersWarning, buildOpenGroupPolicyWarning, createOpenProviderGroupPolicyWarningCollector } from "klaw/plugin-sdk/channel-policy";
10
+ import { createEmptyChannelResult, createRawChannelSendResultAdapter } from "klaw/plugin-sdk/channel-send-result";
11
+ import { buildTokenChannelStatusSummary } from "klaw/plugin-sdk/channel-status";
12
+ import { createStaticReplyToModeResolver } from "klaw/plugin-sdk/conversation-runtime";
13
+ import { createChannelDirectoryAdapter, listResolvedDirectoryUserEntriesFromAllowFrom } from "klaw/plugin-sdk/directory-runtime";
14
+ import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "klaw/plugin-sdk/lazy-runtime";
15
+ import { isNumericTargetId, sendPayloadWithChunkedTextAndMedia } from "klaw/plugin-sdk/reply-payload";
16
+ import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "klaw/plugin-sdk/status-helpers";
17
+ import { chunkTextForOutbound } from "klaw/plugin-sdk/text-chunking";
18
+ import { normalizeLowercaseStringOrEmpty } from "klaw/plugin-sdk/string-coerce-runtime";
19
+ import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "klaw/plugin-sdk/core";
20
+ import { jsonResult, readStringParam } from "klaw/plugin-sdk/channel-actions";
21
+ import { extractToolSend } from "klaw/plugin-sdk/tool-send";
22
+ import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "klaw/plugin-sdk/approval-auth-runtime";
23
+ import { AllowFromListSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, buildCatchallMultiAccountChannelSchema } from "klaw/plugin-sdk/channel-config-schema";
24
+ import { z } from "zod";
25
+ import { coerceStatusIssueAccountId, readStatusIssueFields } from "klaw/plugin-sdk/extension-shared";
26
+ //#region extensions/zalo/src/actions.ts
27
+ const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-C61oPfyd.js"), "zaloActionsRuntime");
28
+ const providerId = "zalo";
29
+ function listEnabledAccounts(cfg, accountId) {
30
+ return (accountId ? [resolveZaloAccount({
31
+ cfg,
32
+ accountId
33
+ })] : listEnabledZaloAccounts(cfg)).filter((account) => account.enabled && account.tokenSource !== "none");
34
+ }
35
+ const zaloMessageActions = {
36
+ describeMessageTool: ({ cfg, accountId }) => {
37
+ if (listEnabledAccounts(cfg, accountId).length === 0) return null;
38
+ const actions = new Set(["send"]);
39
+ return {
40
+ actions: Array.from(actions),
41
+ capabilities: []
42
+ };
43
+ },
44
+ extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
45
+ handleAction: async ({ action, params, cfg, accountId }) => {
46
+ if (action === "send") {
47
+ const to = readStringParam(params, "to", { required: true });
48
+ const content = readStringParam(params, "message", {
49
+ required: true,
50
+ allowEmpty: true
51
+ });
52
+ const mediaUrl = readStringParam(params, "media", { trim: false });
53
+ const { sendMessageZalo } = await loadZaloActionsRuntime();
54
+ const result = await sendMessageZalo(to ?? "", content ?? "", {
55
+ accountId: accountId ?? void 0,
56
+ mediaUrl: mediaUrl ?? void 0,
57
+ cfg
58
+ });
59
+ if (!result.ok) return jsonResult({
60
+ ok: false,
61
+ error: result.error ?? "Failed to send Zalo message"
62
+ });
63
+ return jsonResult({
64
+ ok: true,
65
+ to,
66
+ messageId: result.messageId
67
+ });
68
+ }
69
+ throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
70
+ }
71
+ };
72
+ //#endregion
73
+ //#region extensions/zalo/src/approval-auth.ts
74
+ function normalizeZaloApproverId(value) {
75
+ const normalized = String(value).trim().replace(/^(zalo|zl):/i, "").trim();
76
+ return /^\d+$/.test(normalized) ? normalized : void 0;
77
+ }
78
+ const zaloApprovalAuth = createResolvedApproverActionAuthAdapter({
79
+ channelLabel: "Zalo",
80
+ resolveApprovers: ({ cfg, accountId }) => {
81
+ const account = resolveZaloAccount({
82
+ cfg,
83
+ accountId
84
+ }).config;
85
+ return resolveApprovalApprovers({
86
+ allowFrom: account.allowFrom,
87
+ normalizeApprover: normalizeZaloApproverId
88
+ });
89
+ },
90
+ normalizeSenderId: (value) => normalizeZaloApproverId(value)
91
+ });
92
+ const ZaloConfigSchema = buildCatchallMultiAccountChannelSchema(z.object({
93
+ name: z.string().optional(),
94
+ enabled: z.boolean().optional(),
95
+ markdown: MarkdownConfigSchema,
96
+ botToken: buildSecretInputSchema().optional(),
97
+ tokenFile: z.string().optional(),
98
+ webhookUrl: z.string().optional(),
99
+ webhookSecret: buildSecretInputSchema().optional(),
100
+ webhookPath: z.string().optional(),
101
+ dmPolicy: DmPolicySchema.optional(),
102
+ allowFrom: AllowFromListSchema,
103
+ groupPolicy: GroupPolicySchema.optional(),
104
+ groupAllowFrom: AllowFromListSchema,
105
+ mediaMaxMb: z.number().optional(),
106
+ proxy: z.string().optional(),
107
+ responsePrefix: z.string().optional()
108
+ }));
109
+ //#endregion
110
+ //#region extensions/zalo/src/session-route.ts
111
+ function resolveZaloOutboundSessionRoute(params) {
112
+ const trimmed = stripChannelTargetPrefix(params.target, "zalo", "zl");
113
+ if (!trimmed) return null;
114
+ const isGroup = normalizeLowercaseStringOrEmpty(trimmed).startsWith("group:");
115
+ const peerId = stripTargetKindPrefix(trimmed);
116
+ if (!peerId) return null;
117
+ return buildChannelOutboundSessionRoute({
118
+ cfg: params.cfg,
119
+ agentId: params.agentId,
120
+ channel: "zalo",
121
+ accountId: params.accountId,
122
+ peer: {
123
+ kind: isGroup ? "group" : "direct",
124
+ id: peerId
125
+ },
126
+ chatType: isGroup ? "group" : "direct",
127
+ from: isGroup ? `zalo:group:${peerId}` : `zalo:${peerId}`,
128
+ to: `zalo:${peerId}`
129
+ });
130
+ }
131
+ //#endregion
132
+ //#region extensions/zalo/src/status-issues.ts
133
+ const ZALO_STATUS_FIELDS = [
134
+ "accountId",
135
+ "enabled",
136
+ "configured",
137
+ "dmPolicy"
138
+ ];
139
+ function collectZaloStatusIssues(accounts) {
140
+ const issues = [];
141
+ for (const entry of accounts) {
142
+ const account = readStatusIssueFields(entry, ZALO_STATUS_FIELDS);
143
+ if (!account) continue;
144
+ const accountId = coerceStatusIssueAccountId(account.accountId) ?? "default";
145
+ const enabled = account.enabled !== false;
146
+ const configured = account.configured === true;
147
+ if (!enabled || !configured) continue;
148
+ if (account.dmPolicy === "open") issues.push({
149
+ channel: "zalo",
150
+ accountId,
151
+ kind: "config",
152
+ message: "Zalo dmPolicy is \"open\", allowing any user to message the bot without pairing.",
153
+ fix: "Set channels.zalo.dmPolicy to \"pairing\" or \"allowlist\" to restrict access."
154
+ });
155
+ }
156
+ return issues;
157
+ }
158
+ //#endregion
159
+ //#region extensions/zalo/src/channel.ts
160
+ const meta = {
161
+ id: "zalo",
162
+ label: "Zalo",
163
+ selectionLabel: "Zalo (Bot API)",
164
+ docsPath: "/channels/zalo",
165
+ docsLabel: "zalo",
166
+ blurb: "Vietnam-focused messaging platform with Bot API.",
167
+ aliases: ["zl"],
168
+ order: 80,
169
+ quickstartAllowFrom: true
170
+ };
171
+ function normalizeZaloMessagingTarget(raw) {
172
+ const trimmed = raw?.trim();
173
+ if (!trimmed) return;
174
+ return trimmed.replace(/^(zalo|zl):/i, "").trim();
175
+ }
176
+ const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-sf-rx5n-.js"));
177
+ const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-C8zxrnzG.js")).zaloSetupWizard);
178
+ const zaloTextChunkLimit = 2e3;
179
+ const zaloRawSendResultAdapter = createRawChannelSendResultAdapter({
180
+ channel: "zalo",
181
+ sendText: async ({ to, text, accountId, cfg }) => await (await loadZaloChannelRuntime()).sendZaloText({
182
+ to,
183
+ text,
184
+ accountId: accountId ?? void 0,
185
+ cfg
186
+ }),
187
+ sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => await (await loadZaloChannelRuntime()).sendZaloText({
188
+ to,
189
+ text,
190
+ accountId: accountId ?? void 0,
191
+ mediaUrl,
192
+ cfg
193
+ })
194
+ });
195
+ const zaloMessageAdapter = defineChannelMessageAdapter({
196
+ id: "zalo",
197
+ durableFinal: { capabilities: {
198
+ text: true,
199
+ media: true,
200
+ messageSendingHooks: true
201
+ } },
202
+ send: {
203
+ text: async ({ to, text, accountId, cfg }) => await (await loadZaloChannelRuntime()).sendZaloText({
204
+ to,
205
+ text,
206
+ accountId: accountId ?? void 0,
207
+ cfg
208
+ }),
209
+ media: async ({ to, text, mediaUrl, accountId, cfg }) => await (await loadZaloChannelRuntime()).sendZaloText({
210
+ to,
211
+ text,
212
+ accountId: accountId ?? void 0,
213
+ mediaUrl,
214
+ cfg
215
+ })
216
+ }
217
+ });
218
+ const zaloConfigAdapter = createScopedChannelConfigAdapter({
219
+ sectionKey: "zalo",
220
+ listAccountIds: listZaloAccountIds,
221
+ resolveAccount: adaptScopedAccountAccessor(resolveZaloAccount),
222
+ defaultAccountId: resolveDefaultZaloAccountId,
223
+ clearBaseFields: [
224
+ "botToken",
225
+ "tokenFile",
226
+ "name"
227
+ ],
228
+ resolveAllowFrom: (account) => account.config.allowFrom,
229
+ formatAllowFrom: (allowFrom) => formatAllowFromLowercase({
230
+ allowFrom,
231
+ stripPrefixRe: /^(zalo|zl):/i
232
+ })
233
+ });
234
+ const resolveZaloDmPolicy = createScopedDmSecurityResolver({
235
+ channelKey: "zalo",
236
+ resolvePolicy: (account) => account.config.dmPolicy,
237
+ resolveAllowFrom: (account) => account.config.allowFrom,
238
+ policyPathSuffix: "dmPolicy",
239
+ normalizeEntry: (raw) => raw.trim().replace(/^(zalo|zl):/i, "")
240
+ });
241
+ const collectZaloSecurityWarnings = createOpenProviderGroupPolicyWarningCollector({
242
+ providerConfigPresent: (cfg) => cfg.channels?.zalo !== void 0,
243
+ resolveGroupPolicy: ({ account }) => account.config.groupPolicy,
244
+ collect: ({ account, groupPolicy }) => {
245
+ if (groupPolicy !== "open") return [];
246
+ const explicitGroupAllowFrom = mapAllowFromEntries(account.config.groupAllowFrom);
247
+ const dmAllowFrom = mapAllowFromEntries(account.config.allowFrom);
248
+ if ((explicitGroupAllowFrom.length > 0 ? explicitGroupAllowFrom : dmAllowFrom).length > 0) return [buildOpenGroupPolicyRestrictSendersWarning({
249
+ surface: "Zalo groups",
250
+ openScope: "any member",
251
+ groupPolicyPath: "channels.zalo.groupPolicy",
252
+ groupAllowFromPath: "channels.zalo.groupAllowFrom"
253
+ })];
254
+ return [buildOpenGroupPolicyWarning({
255
+ surface: "Zalo groups",
256
+ openBehavior: "with no groupAllowFrom/allowFrom allowlist; any member can trigger (mention-gated)",
257
+ remediation: "Set channels.zalo.groupPolicy=\"allowlist\" + channels.zalo.groupAllowFrom"
258
+ })];
259
+ }
260
+ });
261
+ const zaloPlugin = createChatChannelPlugin({
262
+ base: {
263
+ id: "zalo",
264
+ meta,
265
+ setup: zaloSetupAdapter,
266
+ setupWizard: zaloSetupWizard,
267
+ capabilities: {
268
+ chatTypes: ["direct", "group"],
269
+ media: true,
270
+ reactions: false,
271
+ threads: false,
272
+ polls: false,
273
+ nativeCommands: false,
274
+ blockStreaming: true
275
+ },
276
+ reload: { configPrefixes: ["channels.zalo"] },
277
+ configSchema: buildChannelConfigSchema(ZaloConfigSchema),
278
+ config: {
279
+ ...zaloConfigAdapter,
280
+ isConfigured: (account) => Boolean(account.token?.trim()),
281
+ describeAccount: (account) => describeWebhookAccountSnapshot({
282
+ account,
283
+ configured: Boolean(account.token?.trim()),
284
+ mode: account.config.webhookUrl ? "webhook" : "polling",
285
+ extra: { tokenSource: account.tokenSource }
286
+ })
287
+ },
288
+ approvalCapability: zaloApprovalAuth,
289
+ secrets: {
290
+ secretTargetRegistryEntries,
291
+ collectRuntimeConfigAssignments
292
+ },
293
+ groups: { resolveRequireMention: () => true },
294
+ actions: zaloMessageActions,
295
+ messaging: {
296
+ targetPrefixes: ["zalo", "zl"],
297
+ normalizeTarget: normalizeZaloMessagingTarget,
298
+ resolveOutboundSessionRoute: (params) => resolveZaloOutboundSessionRoute(params),
299
+ targetResolver: {
300
+ looksLikeId: isNumericTargetId,
301
+ hint: "<chatId>"
302
+ }
303
+ },
304
+ directory: createChannelDirectoryAdapter({
305
+ listPeers: async (params) => listResolvedDirectoryUserEntriesFromAllowFrom({
306
+ ...params,
307
+ resolveAccount: adaptScopedAccountAccessor(resolveZaloAccount),
308
+ resolveAllowFrom: (account) => account.config.allowFrom,
309
+ normalizeId: (entry) => entry.trim().replace(/^(zalo|zl):/i, "")
310
+ }),
311
+ listGroups: async () => []
312
+ }),
313
+ status: createComputedAccountStatusAdapter({
314
+ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
315
+ collectStatusIssues: collectZaloStatusIssues,
316
+ buildChannelSummary: ({ snapshot }) => buildTokenChannelStatusSummary(snapshot),
317
+ probeAccount: async ({ account, timeoutMs }) => await (await loadZaloChannelRuntime()).probeZaloAccount({
318
+ account,
319
+ timeoutMs
320
+ }),
321
+ resolveAccountSnapshot: ({ account }) => {
322
+ const configured = Boolean(account.token?.trim());
323
+ return {
324
+ accountId: account.accountId,
325
+ name: account.name,
326
+ enabled: account.enabled,
327
+ configured,
328
+ extra: {
329
+ tokenSource: account.tokenSource,
330
+ mode: account.config.webhookUrl ? "webhook" : "polling",
331
+ dmPolicy: account.config.dmPolicy ?? "pairing"
332
+ }
333
+ };
334
+ }
335
+ }),
336
+ gateway: { startAccount: async (ctx) => await (await loadZaloChannelRuntime()).startZaloGatewayAccount(ctx) },
337
+ message: zaloMessageAdapter
338
+ },
339
+ security: {
340
+ resolveDmPolicy: resolveZaloDmPolicy,
341
+ collectWarnings: collectZaloSecurityWarnings
342
+ },
343
+ pairing: { text: {
344
+ idLabel: "zaloUserId",
345
+ message: "Your pairing request has been approved.",
346
+ normalizeAllowEntry: (entry) => entry.trim().replace(/^(zalo|zl):/i, ""),
347
+ notify: async (params) => await (await loadZaloChannelRuntime()).notifyZaloPairingApproval(params)
348
+ } },
349
+ threading: { resolveReplyToMode: createStaticReplyToModeResolver("off") },
350
+ outbound: {
351
+ deliveryMode: "direct",
352
+ chunker: chunkTextForOutbound,
353
+ chunkerMode: "text",
354
+ textChunkLimit: zaloTextChunkLimit,
355
+ sendPayload: async (ctx) => await sendPayloadWithChunkedTextAndMedia({
356
+ ctx,
357
+ textChunkLimit: zaloTextChunkLimit,
358
+ chunker: chunkTextForOutbound,
359
+ sendText: (nextCtx) => zaloRawSendResultAdapter.sendText(nextCtx),
360
+ sendMedia: (nextCtx) => zaloRawSendResultAdapter.sendMedia(nextCtx),
361
+ emptyResult: createEmptyChannelResult("zalo")
362
+ }),
363
+ ...zaloRawSendResultAdapter
364
+ }
365
+ });
366
+ //#endregion
367
+ export { zaloPlugin as t };
@@ -0,0 +1,2 @@
1
+ import { t as zaloPlugin } from "./channel-D8ylaEdN.js";
2
+ export { zaloPlugin };
@@ -0,0 +1,105 @@
1
+ import { p as normalizeSecretInputString } from "./setup-core-Dr75wK6l.js";
2
+ import { i as PAIRING_APPROVED_MESSAGE } from "./runtime-api-CxXTp1Q2.js";
3
+ import { a as getMe, n as resolveZaloProxyFetch, r as ZaloApiError, t as sendMessageZalo } from "./send-CGAqdfSA.js";
4
+ import { createAccountStatusSink } from "klaw/plugin-sdk/channel-lifecycle";
5
+ //#region extensions/zalo/src/probe.ts
6
+ async function probeZalo(token, timeoutMs = 5e3, fetcher) {
7
+ if (!token?.trim()) return {
8
+ ok: false,
9
+ error: "No token provided",
10
+ elapsedMs: 0
11
+ };
12
+ const startTime = Date.now();
13
+ try {
14
+ const response = await getMe(token.trim(), timeoutMs, fetcher);
15
+ const elapsedMs = Date.now() - startTime;
16
+ if (response.ok && response.result) return {
17
+ ok: true,
18
+ bot: response.result,
19
+ elapsedMs
20
+ };
21
+ return {
22
+ ok: false,
23
+ error: "Invalid response from Zalo API",
24
+ elapsedMs
25
+ };
26
+ } catch (err) {
27
+ const elapsedMs = Date.now() - startTime;
28
+ if (err instanceof ZaloApiError) return {
29
+ ok: false,
30
+ error: err.description ?? err.message,
31
+ elapsedMs
32
+ };
33
+ if (err instanceof Error) {
34
+ if (err.name === "AbortError") return {
35
+ ok: false,
36
+ error: `Request timed out after ${timeoutMs}ms`,
37
+ elapsedMs
38
+ };
39
+ return {
40
+ ok: false,
41
+ error: err.message,
42
+ elapsedMs
43
+ };
44
+ }
45
+ return {
46
+ ok: false,
47
+ error: String(err),
48
+ elapsedMs
49
+ };
50
+ }
51
+ }
52
+ //#endregion
53
+ //#region extensions/zalo/src/channel.runtime.ts
54
+ async function notifyZaloPairingApproval(params) {
55
+ const { resolveZaloAccount } = await import("./setup-core-Dr75wK6l.js").then((n) => n.o);
56
+ const account = resolveZaloAccount({ cfg: params.cfg });
57
+ if (!account.token) throw new Error("Zalo token not configured");
58
+ await sendMessageZalo(params.id, PAIRING_APPROVED_MESSAGE, { token: account.token });
59
+ }
60
+ async function sendZaloText(params) {
61
+ return await sendMessageZalo(params.to, params.text, params);
62
+ }
63
+ async function probeZaloAccount(params) {
64
+ return await probeZalo(params.account.token, params.timeoutMs, resolveZaloProxyFetch(params.account.config.proxy));
65
+ }
66
+ async function startZaloGatewayAccount(ctx) {
67
+ const account = ctx.account;
68
+ const token = account.token.trim();
69
+ const mode = account.config.webhookUrl ? "webhook" : "polling";
70
+ let zaloBotLabel = "";
71
+ const fetcher = resolveZaloProxyFetch(account.config.proxy);
72
+ try {
73
+ const probe = await probeZalo(token, 2500, fetcher);
74
+ const name = probe.ok ? probe.bot?.name?.trim() : null;
75
+ if (name) zaloBotLabel = ` (${name})`;
76
+ if (!probe.ok) ctx.log?.warn?.(`[${account.accountId}] Zalo probe failed before provider start (${String(probe.elapsedMs)}ms): ${probe.error}`);
77
+ ctx.setStatus({
78
+ accountId: account.accountId,
79
+ bot: probe.bot
80
+ });
81
+ } catch (err) {
82
+ ctx.log?.warn?.(`[${account.accountId}] Zalo probe threw before provider start: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
83
+ }
84
+ const statusSink = createAccountStatusSink({
85
+ accountId: ctx.accountId,
86
+ setStatus: ctx.setStatus
87
+ });
88
+ ctx.log?.info(`[${account.accountId}] starting provider${zaloBotLabel} mode=${mode}`);
89
+ const { monitorZaloProvider } = await import("./monitor-CQ1bjGih.js");
90
+ return monitorZaloProvider({
91
+ token,
92
+ account,
93
+ config: ctx.cfg,
94
+ runtime: ctx.runtime,
95
+ abortSignal: ctx.abortSignal,
96
+ useWebhook: Boolean(account.config.webhookUrl),
97
+ webhookUrl: account.config.webhookUrl,
98
+ webhookSecret: normalizeSecretInputString(account.config.webhookSecret),
99
+ webhookPath: account.config.webhookPath,
100
+ fetcher,
101
+ statusSink
102
+ });
103
+ }
104
+ //#endregion
105
+ export { notifyZaloPairingApproval, probeZaloAccount, sendZaloText, startZaloGatewayAccount };
@@ -0,0 +1,3 @@
1
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-CRFukr2n.js";
2
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-DTQVR6Nd.js";
3
+ export { collectRuntimeConfigAssignments, resolveZaloRuntimeGroupPolicy, secretTargetRegistryEntries };
@@ -0,0 +1,15 @@
1
+ import { resolveOpenProviderRuntimeGroupPolicy } from "klaw/plugin-sdk/runtime-group-policy";
2
+ //#region extensions/zalo/src/group-access.ts
3
+ const ZALO_ALLOW_FROM_PREFIX_RE = /^(zalo|zl):/i;
4
+ function normalizeZaloAllowEntry(value) {
5
+ return value.trim().replace(ZALO_ALLOW_FROM_PREFIX_RE, "").trim().toLowerCase();
6
+ }
7
+ function resolveZaloRuntimeGroupPolicy(params) {
8
+ return resolveOpenProviderRuntimeGroupPolicy({
9
+ providerConfigPresent: params.providerConfigPresent,
10
+ groupPolicy: params.groupPolicy,
11
+ defaultGroupPolicy: params.defaultGroupPolicy
12
+ });
13
+ }
14
+ //#endregion
15
+ export { resolveZaloRuntimeGroupPolicy as n, normalizeZaloAllowEntry as t };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import { defineBundledChannelEntry } from "klaw/plugin-sdk/channel-entry-contract";
2
+ //#region extensions/zalo/index.ts
3
+ var zalo_default = defineBundledChannelEntry({
4
+ id: "zalo",
5
+ name: "Zalo",
6
+ description: "Zalo channel plugin",
7
+ importMetaUrl: import.meta.url,
8
+ plugin: {
9
+ specifier: "./channel-plugin-api.js",
10
+ exportName: "zaloPlugin"
11
+ },
12
+ secrets: {
13
+ specifier: "./secret-contract-api.js",
14
+ exportName: "channelSecrets"
15
+ },
16
+ runtime: {
17
+ specifier: "./runtime-api.js",
18
+ exportName: "setZaloRuntime"
19
+ }
20
+ });
21
+ //#endregion
22
+ export { zalo_default as default };