@openclaw/zalo 2026.7.2-beta.2 → 2026.7.2-beta.4
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/{actions.runtime-OmfJFYZn.js → actions.runtime-DwjVG1H3.js} +1 -1
- package/dist/api.js +2 -2
- package/dist/{channel-CBPOM3Pc.js → channel-CvZItLo-.js} +7 -5
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BsZBukDW.js → channel.runtime-U7gDleS5.js} +5 -5
- package/dist/{monitor-DTi243P3.js → monitor-DfHhlqTv.js} +84 -73
- package/dist/{monitor.webhook-BFkM-Syx.js → monitor.webhook-BCTmWQY8.js} +21 -66
- package/dist/{runtime-api-CAA4ZtqF.js → runtime-api-B53PGWBW.js} +1 -2
- package/dist/runtime-api.js +2 -2
- package/dist/{send-9sVVxyap.js → send-WPSy5EgF.js} +14 -6
- package/dist/setup-api.js +1 -1
- package/dist/{setup-core-fwCh0QUi.js → setup-core-BBA-V-j0.js} +42 -10
- package/dist/{setup-surface-8eRimod9.js → setup-surface-dFkadq_c.js} +1 -1
- package/dist/webhook-spool-Ozf0tcbs.js +174 -0
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +0 -6
- package/package.json +46 -5
package/dist/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-
|
|
2
|
-
import { t as zaloPlugin } from "./channel-
|
|
1
|
+
import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-BBA-V-j0.js";
|
|
2
|
+
import { t as zaloPlugin } from "./channel-CvZItLo-.js";
|
|
3
3
|
import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-8qHRzDHx.js";
|
|
4
4
|
import { zaloSetupWizard } from "./setup-api.js";
|
|
5
5
|
export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { c as listEnabledZaloAccounts, d as resolveZaloAccount, i as zaloSetupContract, l as listZaloAccountIds, p as buildSecretInputSchema, r as zaloSetupAdapter, t as createZaloSetupWizardProxy, u as resolveDefaultZaloAccountId } from "./setup-core-BBA-V-j0.js";
|
|
2
2
|
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Bxvjh0Hq.js";
|
|
3
3
|
import { describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
4
4
|
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
|
@@ -14,7 +14,7 @@ import { createChannelDirectoryAdapter, listResolvedDirectoryUserEntriesFromAllo
|
|
|
14
14
|
import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
15
15
|
import { sendPayloadWithChunkedTextAndMedia } from "openclaw/plugin-sdk/reply-payload";
|
|
16
16
|
import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, standardDmPolicyOpenIssue } from "openclaw/plugin-sdk/status-helpers";
|
|
17
|
-
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
|
|
17
|
+
import { chunkTextForOutbound, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
18
18
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
19
19
|
import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
|
|
20
20
|
import { jsonResult, readStringParam } from "openclaw/plugin-sdk/channel-actions";
|
|
@@ -24,7 +24,7 @@ import { AllowFromListSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigS
|
|
|
24
24
|
import { z } from "zod";
|
|
25
25
|
import { coerceStatusIssueAccountId, readStatusIssueFields } from "openclaw/plugin-sdk/extension-shared";
|
|
26
26
|
//#region extensions/zalo/src/actions.ts
|
|
27
|
-
const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-
|
|
27
|
+
const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-DwjVG1H3.js"), "zaloActionsRuntime");
|
|
28
28
|
const providerId = "zalo";
|
|
29
29
|
const ZALO_ACTIONS = /* @__PURE__ */ new Set(["send"]);
|
|
30
30
|
function listEnabledAccounts(cfg, accountId) {
|
|
@@ -176,8 +176,8 @@ function looksLikeZaloChatId(raw, normalized) {
|
|
|
176
176
|
const target = normalizeZaloMessagingTarget(normalized ?? raw);
|
|
177
177
|
return Boolean(target);
|
|
178
178
|
}
|
|
179
|
-
const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-
|
|
180
|
-
const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-
|
|
179
|
+
const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-U7gDleS5.js"));
|
|
180
|
+
const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-dFkadq_c.js")).zaloSetupWizard);
|
|
181
181
|
const zaloTextChunkLimit = 2e3;
|
|
182
182
|
async function sendZaloDelivery(ctx) {
|
|
183
183
|
const result = await (await loadZaloChannelRuntime()).sendZaloText({
|
|
@@ -258,6 +258,7 @@ const zaloPlugin = createChatChannelPlugin({
|
|
|
258
258
|
id: "zalo",
|
|
259
259
|
meta,
|
|
260
260
|
setup: zaloSetupAdapter,
|
|
261
|
+
setupContract: zaloSetupContract,
|
|
261
262
|
setupWizard: zaloSetupWizard,
|
|
262
263
|
capabilities: {
|
|
263
264
|
chatTypes: ["direct", "group"],
|
|
@@ -347,6 +348,7 @@ const zaloPlugin = createChatChannelPlugin({
|
|
|
347
348
|
chunker: chunkTextForOutbound,
|
|
348
349
|
chunkerMode: "text",
|
|
349
350
|
textChunkLimit: zaloTextChunkLimit,
|
|
351
|
+
sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text),
|
|
350
352
|
sendPayload: async (ctx) => await sendPayloadWithChunkedTextAndMedia({
|
|
351
353
|
ctx,
|
|
352
354
|
textChunkLimit: zaloTextChunkLimit,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as zaloPlugin } from "./channel-
|
|
1
|
+
import { t as zaloPlugin } from "./channel-CvZItLo-.js";
|
|
2
2
|
export { zaloPlugin };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as PAIRING_APPROVED_MESSAGE } from "./runtime-api-
|
|
3
|
-
import { a as getMe, n as resolveZaloProxyFetch, r as ZaloApiError, t as sendMessageZalo } from "./send-
|
|
1
|
+
import { m as normalizeSecretInputString } from "./setup-core-BBA-V-j0.js";
|
|
2
|
+
import { i as PAIRING_APPROVED_MESSAGE } from "./runtime-api-B53PGWBW.js";
|
|
3
|
+
import { a as getMe, n as resolveZaloProxyFetch, r as ZaloApiError, t as sendMessageZalo } from "./send-WPSy5EgF.js";
|
|
4
4
|
import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-outbound";
|
|
5
5
|
//#region extensions/zalo/src/probe.ts
|
|
6
6
|
async function probeZalo(token, timeoutMs = 5e3, fetcher) {
|
|
@@ -52,7 +52,7 @@ async function probeZalo(token, timeoutMs = 5e3, fetcher) {
|
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region extensions/zalo/src/channel.runtime.ts
|
|
54
54
|
async function notifyZaloPairingApproval(params) {
|
|
55
|
-
const { resolveZaloAccount } = await import("./setup-core-
|
|
55
|
+
const { resolveZaloAccount } = await import("./setup-core-BBA-V-j0.js").then((n) => n.s);
|
|
56
56
|
const account = resolveZaloAccount({ cfg: params.cfg });
|
|
57
57
|
if (!account.token) throw new Error("Zalo token not configured");
|
|
58
58
|
await sendMessageZalo(params.id, PAIRING_APPROVED_MESSAGE, { token: account.token });
|
|
@@ -86,7 +86,7 @@ async function startZaloGatewayAccount(ctx) {
|
|
|
86
86
|
setStatus: ctx.setStatus
|
|
87
87
|
});
|
|
88
88
|
ctx.log?.info(`[${account.accountId}] starting provider${zaloBotLabel} mode=${mode}`);
|
|
89
|
-
const { monitorZaloProvider } = await import("./monitor-
|
|
89
|
+
const { monitorZaloProvider } = await import("./monitor-DfHhlqTv.js");
|
|
90
90
|
return monitorZaloProvider({
|
|
91
91
|
token,
|
|
92
92
|
account,
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { n as resolveZaloRuntimeGroupPolicy, t as normalizeZaloAllowEntry } from "./group-access-8qHRzDHx.js";
|
|
2
|
-
import { t as getZaloRuntime } from "./runtime-api-
|
|
3
|
-
import { c as sendChatAction, d as setWebhook, i as deleteWebhook, l as sendMessage, n as resolveZaloProxyFetch, o as getUpdates, r as ZaloApiError, s as getWebhookInfo, u as sendPhoto } from "./send-
|
|
4
|
-
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
2
|
+
import { t as getZaloRuntime } from "./runtime-api-B53PGWBW.js";
|
|
3
|
+
import { c as sendChatAction, d as setWebhook, f as ZALO_OUTBOUND_MEDIA_TTL_MS, i as deleteWebhook, l as sendMessage, n as resolveZaloProxyFetch, o as getUpdates, r as ZaloApiError, s as getWebhookInfo, u as sendPhoto } from "./send-WPSy5EgF.js";
|
|
4
|
+
import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
5
5
|
import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
6
6
|
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
7
|
import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
8
8
|
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
|
9
9
|
import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
|
|
10
|
-
import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
|
|
11
10
|
import { registerPluginHttpRoute, resolveWebhookPath } from "openclaw/plugin-sdk/webhook-ingress";
|
|
12
11
|
import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
13
|
-
import { formatInboundMediaUnavailableText } from "openclaw/plugin-sdk/channel-inbound";
|
|
12
|
+
import { formatInboundMediaUnavailableText, resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
|
|
14
13
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
15
14
|
import { sleepWithAbort, waitForAbortSignal } from "openclaw/plugin-sdk/runtime-env";
|
|
16
15
|
import { createHostedOutboundMediaStore } from "openclaw/plugin-sdk/outbound-media";
|
|
@@ -31,7 +30,6 @@ function resolveZaloDurableReplyOptions(params) {
|
|
|
31
30
|
}
|
|
32
31
|
//#endregion
|
|
33
32
|
//#region extensions/zalo/src/outbound-media.ts
|
|
34
|
-
const ZALO_OUTBOUND_MEDIA_TTL_MS = 2 * 6e4;
|
|
35
33
|
const ZALO_OUTBOUND_MEDIA_SEGMENT = "media";
|
|
36
34
|
const ZALO_OUTBOUND_MEDIA_PREFIX = `/${ZALO_OUTBOUND_MEDIA_SEGMENT}/`;
|
|
37
35
|
const ZALO_OUTBOUND_MEDIA_ID_RE = /^[a-f0-9]{24}$/;
|
|
@@ -75,7 +73,7 @@ function resolveHostedZaloMediaRoutePath(params) {
|
|
|
75
73
|
}
|
|
76
74
|
async function prepareHostedZaloMediaUrl(params) {
|
|
77
75
|
const now = asDateTimestampMs(Date.now());
|
|
78
|
-
if ((now === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(
|
|
76
|
+
if ((now === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(12e4, { nowMs: now })) === void 0) throw new Error("Zalo outbound media expiry could not be resolved");
|
|
79
77
|
const routePath = resolveHostedZaloMediaRoutePath({
|
|
80
78
|
webhookUrl: params.webhookUrl,
|
|
81
79
|
webhookPath: params.webhookPath
|
|
@@ -165,7 +163,12 @@ function resolveZaloTimestampMs(date) {
|
|
|
165
163
|
if (!date) return;
|
|
166
164
|
return date >= UNIX_MILLISECONDS_THRESHOLD ? date : date * 1e3;
|
|
167
165
|
}
|
|
168
|
-
const
|
|
166
|
+
const loadZaloWebhookRuntime = createLazyRuntimeNamedExport(() => import("./monitor.webhook-BCTmWQY8.js"), "zaloWebhookRuntime");
|
|
167
|
+
const loadZaloWebhookIngressRuntime = createLazyRuntimeNamedExport(() => import("./webhook-spool-Ozf0tcbs.js"), "zaloWebhookIngressRuntime");
|
|
168
|
+
const loadZaloWebhookModule = createLazyRuntimeModule(async () => ({
|
|
169
|
+
...await loadZaloWebhookRuntime(),
|
|
170
|
+
...await loadZaloWebhookIngressRuntime()
|
|
171
|
+
}));
|
|
169
172
|
function releaseSharedHostedMediaRouteRef(routePath) {
|
|
170
173
|
const current = hostedMediaRouteRefs.get(routePath);
|
|
171
174
|
if (!current) return;
|
|
@@ -226,22 +229,7 @@ function logVerbose(core, runtime, message) {
|
|
|
226
229
|
}
|
|
227
230
|
async function handleZaloWebhookRequest(req, res) {
|
|
228
231
|
const { handleZaloWebhookRequest: handleZaloWebhookRequestInternal } = await loadZaloWebhookModule();
|
|
229
|
-
return await handleZaloWebhookRequestInternal(req, res
|
|
230
|
-
await processUpdate({
|
|
231
|
-
update,
|
|
232
|
-
token: target.token,
|
|
233
|
-
account: target.account,
|
|
234
|
-
config: target.config,
|
|
235
|
-
runtime: target.runtime,
|
|
236
|
-
core: target.core,
|
|
237
|
-
mediaMaxMb: target.mediaMaxMb,
|
|
238
|
-
canHostMedia: target.canHostMedia,
|
|
239
|
-
webhookUrl: target.webhookUrl,
|
|
240
|
-
webhookPath: target.webhookPath,
|
|
241
|
-
statusSink: target.statusSink,
|
|
242
|
-
fetcher: target.fetcher
|
|
243
|
-
});
|
|
244
|
-
});
|
|
232
|
+
return await handleZaloWebhookRequestInternal(req, res);
|
|
245
233
|
}
|
|
246
234
|
function startPollingLoop(params) {
|
|
247
235
|
const { token, account, config, runtime, core, mediaMaxMb, canHostMedia, webhookUrl, webhookPath, abortSignal, isStopped, statusSink, fetcher } = params;
|
|
@@ -298,7 +286,8 @@ async function processUpdate(params) {
|
|
|
298
286
|
webhookUrl: params.webhookUrl,
|
|
299
287
|
webhookPath: params.webhookPath,
|
|
300
288
|
statusSink,
|
|
301
|
-
fetcher
|
|
289
|
+
fetcher,
|
|
290
|
+
turnAdoptionLifecycle: params.turnAdoptionLifecycle
|
|
302
291
|
};
|
|
303
292
|
if (!message) return;
|
|
304
293
|
switch (event_name) {
|
|
@@ -330,6 +319,7 @@ async function handleTextMessage(params) {
|
|
|
330
319
|
await processMessageWithPipeline({
|
|
331
320
|
...params,
|
|
332
321
|
text,
|
|
322
|
+
mediaKind: void 0,
|
|
333
323
|
mediaPath: void 0,
|
|
334
324
|
mediaType: void 0
|
|
335
325
|
});
|
|
@@ -340,7 +330,8 @@ async function handleImageMessage(params) {
|
|
|
340
330
|
const authorization = await authorizeZaloMessage({
|
|
341
331
|
...params,
|
|
342
332
|
text: caption,
|
|
343
|
-
|
|
333
|
+
mediaKind: "image",
|
|
334
|
+
mediaPath: void 0,
|
|
344
335
|
mediaType: void 0
|
|
345
336
|
});
|
|
346
337
|
if (!authorization) return;
|
|
@@ -360,21 +351,21 @@ async function handleImageMessage(params) {
|
|
|
360
351
|
runtime.error?.(`[${account.accountId}] Failed to download Zalo image: ${String(err)}`);
|
|
361
352
|
}
|
|
362
353
|
const agentBody = mediaPath ? authorization.rawBody : formatInboundMediaUnavailableText({
|
|
363
|
-
body:
|
|
364
|
-
mediaPlaceholder: "<media:image>",
|
|
354
|
+
body: authorization.rawBody,
|
|
365
355
|
notice: "[zalo image attachment unavailable]"
|
|
366
356
|
});
|
|
367
357
|
await processMessageWithPipeline({
|
|
368
358
|
...params,
|
|
369
359
|
authorization,
|
|
370
360
|
agentBody,
|
|
361
|
+
mediaKind: "image",
|
|
371
362
|
text: caption,
|
|
372
363
|
mediaPath,
|
|
373
364
|
mediaType
|
|
374
365
|
});
|
|
375
366
|
}
|
|
376
367
|
async function authorizeZaloMessage(params) {
|
|
377
|
-
const { message, account, config, runtime, core, text,
|
|
368
|
+
const { message, account, config, runtime, core, text, token, statusSink, fetcher } = params;
|
|
378
369
|
const pairing = createChannelPairingController({
|
|
379
370
|
core,
|
|
380
371
|
channel: "zalo",
|
|
@@ -387,7 +378,7 @@ async function authorizeZaloMessage(params) {
|
|
|
387
378
|
const senderName = from.display_name ?? from.name;
|
|
388
379
|
const dmPolicy = account.config.dmPolicy ?? "pairing";
|
|
389
380
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(config);
|
|
390
|
-
const rawBody = text?.trim()
|
|
381
|
+
const rawBody = text?.trim() ?? "";
|
|
391
382
|
const { groupPolicy, providerMissingFallbackApplied } = resolveZaloRuntimeGroupPolicy({
|
|
392
383
|
providerConfigPresent: config.channels?.zalo !== void 0,
|
|
393
384
|
groupPolicy: account.config.groupPolicy,
|
|
@@ -469,7 +460,7 @@ async function authorizeZaloMessage(params) {
|
|
|
469
460
|
};
|
|
470
461
|
}
|
|
471
462
|
async function processMessageWithPipeline(params) {
|
|
472
|
-
const { message, token, account, config, runtime, core, mediaPath, mediaType, statusSink, fetcher, agentBody: agentBodyOverride, authorization: authorizationOverride } = params;
|
|
463
|
+
const { message, token, account, config, runtime, core, mediaPath, mediaKind, mediaType, statusSink, fetcher, agentBody: agentBodyOverride, authorization: authorizationOverride } = params;
|
|
473
464
|
const { message_id, date } = message;
|
|
474
465
|
const authorization = authorizationOverride ?? await authorizeZaloMessage({
|
|
475
466
|
...params,
|
|
@@ -479,16 +470,14 @@ async function processMessageWithPipeline(params) {
|
|
|
479
470
|
if (!authorization) return;
|
|
480
471
|
const { isGroup, chatId, senderId, senderName, rawBody, commandAuthorized } = authorization;
|
|
481
472
|
const agentBody = agentBodyOverride ?? rawBody;
|
|
482
|
-
const { route, buildEnvelope } =
|
|
473
|
+
const { route, buildEnvelope } = resolveChannelInboundRouteEnvelope({
|
|
483
474
|
cfg: config,
|
|
484
475
|
channel: "zalo",
|
|
485
476
|
accountId: account.accountId,
|
|
486
477
|
peer: {
|
|
487
478
|
kind: isGroup ? "group" : "direct",
|
|
488
479
|
id: chatId
|
|
489
|
-
}
|
|
490
|
-
runtime: core.channel,
|
|
491
|
-
sessionStore: config.session?.store
|
|
480
|
+
}
|
|
492
481
|
});
|
|
493
482
|
if (isGroup && core.channel.commands.isControlCommandMessage(rawBody, config) && commandAuthorized !== true) {
|
|
494
483
|
logVerbose(core, runtime, `zalo: drop control command from unauthorized sender ${senderId}`);
|
|
@@ -496,7 +485,7 @@ async function processMessageWithPipeline(params) {
|
|
|
496
485
|
}
|
|
497
486
|
const fromLabel = isGroup ? `group:${chatId}` : senderName || `user:${senderId}`;
|
|
498
487
|
const timestamp = resolveZaloTimestampMs(date);
|
|
499
|
-
const
|
|
488
|
+
const body = buildEnvelope({
|
|
500
489
|
channel: "Zalo",
|
|
501
490
|
from: fromLabel,
|
|
502
491
|
timestamp,
|
|
@@ -519,6 +508,7 @@ async function processMessageWithPipeline(params) {
|
|
|
519
508
|
},
|
|
520
509
|
route: {
|
|
521
510
|
agentId: route.agentId,
|
|
511
|
+
dmScope: route.dmScope,
|
|
522
512
|
accountId: route.accountId,
|
|
523
513
|
routeSessionKey: route.sessionKey
|
|
524
514
|
},
|
|
@@ -529,10 +519,11 @@ async function processMessageWithPipeline(params) {
|
|
|
529
519
|
rawBody,
|
|
530
520
|
commandBody: rawBody
|
|
531
521
|
},
|
|
532
|
-
media: mediaPath || mediaType ? [{
|
|
522
|
+
media: mediaKind || mediaPath || mediaType ? [{
|
|
533
523
|
path: mediaPath,
|
|
534
524
|
url: mediaPath,
|
|
535
|
-
contentType: mediaType
|
|
525
|
+
contentType: mediaType,
|
|
526
|
+
kind: mediaKind ?? void 0
|
|
536
527
|
}] : void 0,
|
|
537
528
|
extra: {
|
|
538
529
|
CommandAuthorized: commandAuthorized,
|
|
@@ -544,16 +535,16 @@ async function processMessageWithPipeline(params) {
|
|
|
544
535
|
channel: "zalo",
|
|
545
536
|
accountId: account.accountId
|
|
546
537
|
});
|
|
547
|
-
await core.channel.inbound.
|
|
538
|
+
await core.channel.inbound.dispatch({
|
|
548
539
|
cfg: config,
|
|
549
540
|
channel: "zalo",
|
|
550
541
|
accountId: account.accountId,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
542
|
+
route: {
|
|
543
|
+
agentId: route.agentId,
|
|
544
|
+
dmScope: route.dmScope,
|
|
545
|
+
sessionKey: route.sessionKey
|
|
546
|
+
},
|
|
554
547
|
ctxPayload,
|
|
555
|
-
recordInboundSession: core.channel.session.recordInboundSession,
|
|
556
|
-
dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
557
548
|
delivery: {
|
|
558
549
|
preparePayload: (payload) => prepareZaloDurableReplyPayload({
|
|
559
550
|
payload,
|
|
@@ -610,7 +601,8 @@ async function processMessageWithPipeline(params) {
|
|
|
610
601
|
} },
|
|
611
602
|
record: { onRecordError: (err) => {
|
|
612
603
|
runtime.error?.(`zalo: failed updating session meta: ${String(err)}`);
|
|
613
|
-
} }
|
|
604
|
+
} },
|
|
605
|
+
...params.turnAdoptionLifecycle ? { turnAdoptionLifecycle: params.turnAdoptionLifecycle } : {}
|
|
614
606
|
});
|
|
615
607
|
}
|
|
616
608
|
async function deliverZaloReply(params) {
|
|
@@ -672,6 +664,7 @@ async function monitorZaloProvider(options) {
|
|
|
672
664
|
}) : void 0;
|
|
673
665
|
let stopped = false;
|
|
674
666
|
const stopHandlers = [];
|
|
667
|
+
const asyncStopHandlers = [];
|
|
675
668
|
let cleanupWebhook;
|
|
676
669
|
const stop = () => {
|
|
677
670
|
if (stopped) return;
|
|
@@ -693,46 +686,44 @@ async function monitorZaloProvider(options) {
|
|
|
693
686
|
stopHandlers.push(unregisterHostedMediaRoute);
|
|
694
687
|
}
|
|
695
688
|
if (useWebhook) {
|
|
696
|
-
const { registerZaloWebhookTarget } = await loadZaloWebhookModule();
|
|
689
|
+
const { createZaloWebhookIngress, registerZaloWebhookTarget } = await loadZaloWebhookModule();
|
|
697
690
|
if (!effectiveWebhookUrl || !webhookSecret) throw new Error("Zalo webhookUrl and webhookSecret are required for webhook mode");
|
|
698
691
|
if (!effectiveWebhookUrl.startsWith("https://")) throw new Error("Zalo webhook URL must use HTTPS");
|
|
699
692
|
if (webhookSecret.length < 8 || webhookSecret.length > 256) throw new Error("Zalo webhook secret must be 8-256 characters");
|
|
700
693
|
const path = effectiveWebhookPath;
|
|
701
694
|
if (!path) throw new Error("Zalo webhookPath could not be derived");
|
|
702
695
|
runtime.log?.(`[${account.accountId}] Zalo configuring webhook path=${path} target=${describeWebhookTarget(effectiveWebhookUrl)}`);
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
runtime
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
696
|
+
const ingress = createZaloWebhookIngress({
|
|
697
|
+
accountId: account.accountId,
|
|
698
|
+
runtime,
|
|
699
|
+
deliver: async (update, turnAdoptionLifecycle) => {
|
|
700
|
+
statusSink?.({ lastInboundAt: Date.now() });
|
|
701
|
+
await processUpdate({
|
|
702
|
+
update,
|
|
703
|
+
token,
|
|
704
|
+
account,
|
|
705
|
+
config,
|
|
706
|
+
runtime,
|
|
707
|
+
core,
|
|
708
|
+
mediaMaxMb: effectiveMediaMaxMb,
|
|
709
|
+
canHostMedia,
|
|
710
|
+
webhookUrl: effectiveWebhookUrl,
|
|
711
|
+
webhookPath: path,
|
|
712
|
+
statusSink,
|
|
713
|
+
fetcher,
|
|
714
|
+
turnAdoptionLifecycle
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
ingress.start();
|
|
719
|
+
asyncStopHandlers.push(ingress.stop);
|
|
722
720
|
const unregister = registerZaloWebhookTarget({
|
|
723
|
-
token,
|
|
724
721
|
account,
|
|
725
722
|
config,
|
|
726
723
|
runtime,
|
|
727
|
-
core,
|
|
728
724
|
path,
|
|
729
|
-
webhookUrl: effectiveWebhookUrl,
|
|
730
|
-
webhookPath: path,
|
|
731
725
|
secret: webhookSecret,
|
|
732
|
-
|
|
733
|
-
mediaMaxMb: effectiveMediaMaxMb,
|
|
734
|
-
canHostMedia,
|
|
735
|
-
fetcher
|
|
726
|
+
acceptWebhook: ingress.accept
|
|
736
727
|
}, { route: {
|
|
737
728
|
auth: "plugin",
|
|
738
729
|
match: "exact",
|
|
@@ -749,6 +740,25 @@ async function monitorZaloProvider(options) {
|
|
|
749
740
|
}
|
|
750
741
|
} });
|
|
751
742
|
stopHandlers.push(unregister);
|
|
743
|
+
await setWebhook(token, {
|
|
744
|
+
url: effectiveWebhookUrl,
|
|
745
|
+
secret_token: webhookSecret
|
|
746
|
+
}, fetcher);
|
|
747
|
+
let webhookCleanupPromise;
|
|
748
|
+
cleanupWebhook = async () => {
|
|
749
|
+
if (!webhookCleanupPromise) webhookCleanupPromise = (async () => {
|
|
750
|
+
runtime.log?.(`[${account.accountId}] Zalo stopping; deleting webhook`);
|
|
751
|
+
try {
|
|
752
|
+
await deleteWebhook(token, fetcher, WEBHOOK_CLEANUP_TIMEOUT_MS);
|
|
753
|
+
runtime.log?.(`[${account.accountId}] Zalo webhook deleted`);
|
|
754
|
+
} catch (err) {
|
|
755
|
+
const detail = err instanceof Error && err.name === "AbortError" ? `timed out after ${String(WEBHOOK_CLEANUP_TIMEOUT_MS)}ms` : formatZaloError(err);
|
|
756
|
+
runtime.error?.(`[${account.accountId}] Zalo webhook delete failed: ${detail}`);
|
|
757
|
+
}
|
|
758
|
+
})();
|
|
759
|
+
await webhookCleanupPromise;
|
|
760
|
+
};
|
|
761
|
+
runtime.log?.(`[${account.accountId}] Zalo webhook registered path=${path}`);
|
|
752
762
|
await waitForAbortSignal(abortSignal);
|
|
753
763
|
return;
|
|
754
764
|
}
|
|
@@ -792,6 +802,7 @@ async function monitorZaloProvider(options) {
|
|
|
792
802
|
abortSignal.removeEventListener("abort", stopOnAbort);
|
|
793
803
|
await cleanupWebhook?.();
|
|
794
804
|
stop();
|
|
805
|
+
for (const stopAsync of asyncStopHandlers) await stopAsync();
|
|
795
806
|
runtime.log?.(`[${account.accountId}] Zalo provider stopped mode=${mode}`);
|
|
796
807
|
}
|
|
797
808
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { B as registerWebhookTargetWithPluginRoute,
|
|
1
|
+
import { B as registerWebhookTargetWithPluginRoute, G as resolveWebhookTargetWithAuthOrRejectSync, V as resolveClientIp, Z as withResolvedWebhookRequestPipeline, a as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, b as createFixedWindowRateLimiter, l as applyBasicWebhookRequestGuards, o as WEBHOOK_RATE_LIMIT_DEFAULTS, x as createWebhookAnomalyTracker, z as registerWebhookTarget } from "./runtime-api-B53PGWBW.js";
|
|
2
|
+
import { ZaloWebhookPayloadError } from "./webhook-spool-Ozf0tcbs.js";
|
|
2
3
|
import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
3
|
-
import {
|
|
4
|
-
import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
4
|
+
import { readWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
5
5
|
//#region extensions/zalo/src/monitor.webhook.ts
|
|
6
|
-
const ZALO_WEBHOOK_REPLAY_WINDOW_MS = 5 * 6e4;
|
|
7
6
|
const webhookTargets = /* @__PURE__ */ new Map();
|
|
8
7
|
const webhookRateLimiter = createFixedWindowRateLimiter({
|
|
9
8
|
windowMs: WEBHOOK_RATE_LIMIT_DEFAULTS.windowMs,
|
|
@@ -17,7 +16,6 @@ const webhookAnomalyTracker = createWebhookAnomalyTracker({
|
|
|
17
16
|
});
|
|
18
17
|
function clearZaloWebhookSecurityStateForTest() {
|
|
19
18
|
webhookRateLimiter.clear();
|
|
20
|
-
recentWebhookEvents.clearMemory();
|
|
21
19
|
webhookAnomalyTracker.clear();
|
|
22
20
|
}
|
|
23
21
|
function getZaloWebhookRateLimitStateSizeForTest() {
|
|
@@ -26,46 +24,6 @@ function getZaloWebhookRateLimitStateSizeForTest() {
|
|
|
26
24
|
function getZaloWebhookStatusCounterSizeForTest() {
|
|
27
25
|
return webhookAnomalyTracker.size();
|
|
28
26
|
}
|
|
29
|
-
function buildReplayEventCacheKey(target, update) {
|
|
30
|
-
const messageId = update.message?.message_id;
|
|
31
|
-
if (!messageId) return null;
|
|
32
|
-
const chatId = update.message?.chat?.id ?? "";
|
|
33
|
-
const senderId = update.message?.from?.id ?? "";
|
|
34
|
-
return JSON.stringify([
|
|
35
|
-
target.path,
|
|
36
|
-
target.account.accountId,
|
|
37
|
-
update.event_name,
|
|
38
|
-
chatId,
|
|
39
|
-
senderId,
|
|
40
|
-
messageId
|
|
41
|
-
]);
|
|
42
|
-
}
|
|
43
|
-
const recentWebhookEvents = createChannelReplayGuard({
|
|
44
|
-
dedupe: {
|
|
45
|
-
ttlMs: ZALO_WEBHOOK_REPLAY_WINDOW_MS,
|
|
46
|
-
memoryMaxSize: 5e3
|
|
47
|
-
},
|
|
48
|
-
buildReplayKey: ({ target, update }) => buildReplayEventCacheKey(target, update)
|
|
49
|
-
});
|
|
50
|
-
var ZaloRetryableWebhookError = class extends Error {
|
|
51
|
-
constructor(message, options) {
|
|
52
|
-
super(message, options);
|
|
53
|
-
this.name = "ZaloRetryableWebhookError";
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
async function processZaloReplayGuardedUpdate(params) {
|
|
57
|
-
const event = {
|
|
58
|
-
target: params.target,
|
|
59
|
-
update: params.update
|
|
60
|
-
};
|
|
61
|
-
return (await recentWebhookEvents.processGuarded(event, async () => {
|
|
62
|
-
params.target.statusSink?.({ lastInboundAt: Date.now() });
|
|
63
|
-
await params.processUpdate(event);
|
|
64
|
-
}, {
|
|
65
|
-
dedupe: { now: params.nowMs },
|
|
66
|
-
onError: (error) => error instanceof ZaloRetryableWebhookError ? "release" : "commit"
|
|
67
|
-
})).kind === "processed" ? "processed" : "duplicate";
|
|
68
|
-
}
|
|
69
27
|
function recordWebhookStatus(runtime, path, statusCode) {
|
|
70
28
|
webhookAnomalyTracker.record({
|
|
71
29
|
key: `${path}:${statusCode}`,
|
|
@@ -86,7 +44,7 @@ function registerZaloWebhookTarget(target, opts) {
|
|
|
86
44
|
}).unregister;
|
|
87
45
|
return registerWebhookTarget(webhookTargets, target, opts).unregister;
|
|
88
46
|
}
|
|
89
|
-
async function handleZaloWebhookRequest(req, res
|
|
47
|
+
async function handleZaloWebhookRequest(req, res) {
|
|
90
48
|
return await withResolvedWebhookRequestPipeline({
|
|
91
49
|
req,
|
|
92
50
|
res,
|
|
@@ -102,13 +60,12 @@ async function handleZaloWebhookRequest(req, res, processUpdate) {
|
|
|
102
60
|
trustedProxies,
|
|
103
61
|
allowRealIpFallback
|
|
104
62
|
}) ?? req.socket.remoteAddress ?? "unknown"}`;
|
|
105
|
-
const nowMs = Date.now();
|
|
106
63
|
if (!applyBasicWebhookRequestGuards({
|
|
107
64
|
req,
|
|
108
65
|
res,
|
|
109
66
|
rateLimiter: webhookRateLimiter,
|
|
110
67
|
rateLimitKey,
|
|
111
|
-
nowMs
|
|
68
|
+
nowMs: Date.now()
|
|
112
69
|
})) {
|
|
113
70
|
recordWebhookStatus(targets[0]?.runtime, path, res.statusCode);
|
|
114
71
|
return true;
|
|
@@ -131,40 +88,38 @@ async function handleZaloWebhookRequest(req, res, processUpdate) {
|
|
|
131
88
|
recordWebhookStatus(target.runtime, path, res.statusCode);
|
|
132
89
|
return true;
|
|
133
90
|
}
|
|
134
|
-
const body = await
|
|
91
|
+
const body = await readWebhookBodyOrReject({
|
|
135
92
|
req,
|
|
136
93
|
res,
|
|
137
94
|
maxBytes: 1024 * 1024,
|
|
138
95
|
timeoutMs: 3e4,
|
|
139
|
-
|
|
140
|
-
invalidJsonMessage: "Bad Request"
|
|
96
|
+
invalidBodyMessage: "Bad Request"
|
|
141
97
|
});
|
|
142
98
|
if (!body.ok) {
|
|
143
99
|
recordWebhookStatus(target.runtime, path, res.statusCode);
|
|
144
100
|
return true;
|
|
145
101
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
res.statusCode
|
|
151
|
-
res.end("Bad Request");
|
|
102
|
+
try {
|
|
103
|
+
await target.acceptWebhook(body.value);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
res.statusCode = error instanceof ZaloWebhookPayloadError ? 400 : 500;
|
|
106
|
+
res.end(res.statusCode === 400 ? "Bad Request" : "Internal Server Error");
|
|
152
107
|
recordWebhookStatus(target.runtime, path, res.statusCode);
|
|
108
|
+
target.runtime.error?.(`[${target.account.accountId}] Zalo webhook admission failed: ${String(error)}`);
|
|
153
109
|
return true;
|
|
154
110
|
}
|
|
155
|
-
runDetachedWebhookWork(() => processZaloReplayGuardedUpdate({
|
|
156
|
-
target,
|
|
157
|
-
update,
|
|
158
|
-
processUpdate,
|
|
159
|
-
nowMs
|
|
160
|
-
})).catch((err) => {
|
|
161
|
-
target.runtime.error?.(`[${target.account.accountId}] Zalo webhook failed: ${String(err)}`);
|
|
162
|
-
});
|
|
163
111
|
res.statusCode = 200;
|
|
164
112
|
res.end("ok");
|
|
165
113
|
return true;
|
|
166
114
|
}
|
|
167
115
|
});
|
|
168
116
|
}
|
|
117
|
+
const zaloWebhookRuntime = {
|
|
118
|
+
clearZaloWebhookSecurityStateForTest,
|
|
119
|
+
getZaloWebhookRateLimitStateSizeForTest,
|
|
120
|
+
getZaloWebhookStatusCounterSizeForTest,
|
|
121
|
+
handleZaloWebhookRequest,
|
|
122
|
+
registerZaloWebhookTarget
|
|
123
|
+
};
|
|
169
124
|
//#endregion
|
|
170
|
-
export {
|
|
125
|
+
export { zaloWebhookRuntime };
|
|
@@ -10,7 +10,6 @@ import { addWildcardAllowFrom as addWildcardAllowFrom$1, applyAccountNameToChann
|
|
|
10
10
|
import { resolveDefaultGroupPolicy as resolveDefaultGroupPolicy$1, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce as warnMissingProviderGroupPolicyFallbackOnce$1 } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
11
11
|
import { createChannelPairingController as createChannelPairingController$1 } from "openclaw/plugin-sdk/channel-pairing";
|
|
12
12
|
import { logTypingFailure as logTypingFailure$1 } from "openclaw/plugin-sdk/channel-feedback";
|
|
13
|
-
import { resolveInboundRouteEnvelopeBuilderWithRuntime as resolveInboundRouteEnvelopeBuilderWithRuntime$1 } from "openclaw/plugin-sdk/inbound-envelope";
|
|
14
13
|
import { waitForAbortSignal } from "openclaw/plugin-sdk/runtime";
|
|
15
14
|
import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, readJsonWebhookBodyOrReject, registerPluginHttpRoute as registerPluginHttpRoute$1, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveWebhookPath as resolveWebhookPath$1, resolveWebhookTargetWithAuthOrRejectSync, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-ingress";
|
|
16
15
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
@@ -20,4 +19,4 @@ const { setRuntime: setZaloRuntime, getRuntime: getZaloRuntime } = createPluginR
|
|
|
20
19
|
errorMessage: "Zalo runtime not initialized"
|
|
21
20
|
});
|
|
22
21
|
//#endregion
|
|
23
|
-
export { mergeAllowFromEntries$1 as A, registerWebhookTargetWithPluginRoute as B, formatAllowFromLowercase$1 as C, isNumericTargetId as D, isNormalizedSenderAllowed as E, promptSingleChannelSecretInput$1 as F,
|
|
22
|
+
export { mergeAllowFromEntries$1 as A, registerWebhookTargetWithPluginRoute as B, formatAllowFromLowercase$1 as C, isNumericTargetId as D, isNormalizedSenderAllowed as E, promptSingleChannelSecretInput$1 as F, resolveWebhookTargetWithAuthOrRejectSync as G, resolveDefaultGroupPolicy$1 as H, readJsonWebhookBodyOrReject as I, setTopLevelChannelDmPolicyWithAllowFrom as J, runSingleChannelSecretStep$1 as K, readStringParam as L, normalizeAccountId$1 as M, normalizeResolvedSecretInputString as N, jsonResult as O, normalizeSecretInputString as P, registerPluginHttpRoute$1 as R, deliverTextOrMediaReply$1 as S, hasConfiguredSecretInput$1 as T, resolveOpenProviderRuntimeGroupPolicy$1 as U, resolveClientIp as V, resolveWebhookPath$1 as W, warnMissingProviderGroupPolicyFallbackOnce$1 as X, waitForAbortSignal as Y, withResolvedWebhookRequestPipeline as Z, createChannelMessageReplyPipeline as _, WEBHOOK_ANOMALY_COUNTER_DEFAULTS as a, createFixedWindowRateLimiter as b, applyAccountNameToChannelSection as c, buildBaseAccountStatusSnapshot as d, buildChannelConfigSchema as f, chunkTextForOutbound$1 as g, buildTokenChannelStatusSummary$1 as h, PAIRING_APPROVED_MESSAGE as i, migrateBaseNameToDefaultAccount as j, logTypingFailure$1 as k, applyBasicWebhookRequestGuards as l, buildSingleChannelSecretPromptState$1 as m, setZaloRuntime as n, WEBHOOK_RATE_LIMIT_DEFAULTS as o, buildSecretInputSchema as p, sendPayloadWithChunkedTextAndMedia$1 as q, DEFAULT_ACCOUNT_ID$1 as r, addWildcardAllowFrom$1 as s, getZaloRuntime as t, applySetupAccountConfigPatch as u, createChannelPairingController$1 as v, formatPairingApproveHint as w, createWebhookAnomalyTracker as x, createDedupeCache as y, registerWebhookTarget as z };
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as mergeAllowFromEntries, B as registerWebhookTargetWithPluginRoute, C as formatAllowFromLowercase, D as isNumericTargetId, E as isNormalizedSenderAllowed, F as promptSingleChannelSecretInput, G as
|
|
2
|
-
export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, addWildcardAllowFrom, applyAccountNameToChannelSection, applyBasicWebhookRequestGuards, applySetupAccountConfigPatch, buildBaseAccountStatusSnapshot, buildChannelConfigSchema, buildSecretInputSchema, buildSingleChannelSecretPromptState, buildTokenChannelStatusSummary, chunkTextForOutbound, createChannelMessageReplyPipeline, createChannelPairingController, createDedupeCache, createFixedWindowRateLimiter, createWebhookAnomalyTracker, deliverTextOrMediaReply, formatAllowFromLowercase, formatPairingApproveHint, hasConfiguredSecretInput, isNormalizedSenderAllowed, isNumericTargetId, jsonResult, logTypingFailure, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, normalizeAccountId, normalizeResolvedSecretInputString, normalizeSecretInputString, promptSingleChannelSecretInput, readJsonWebhookBodyOrReject, readStringParam, registerPluginHttpRoute, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveClientIp, resolveDefaultGroupPolicy,
|
|
1
|
+
import { A as mergeAllowFromEntries, B as registerWebhookTargetWithPluginRoute, C as formatAllowFromLowercase, D as isNumericTargetId, E as isNormalizedSenderAllowed, F as promptSingleChannelSecretInput, G as resolveWebhookTargetWithAuthOrRejectSync, H as resolveDefaultGroupPolicy, I as readJsonWebhookBodyOrReject, J as setTopLevelChannelDmPolicyWithAllowFrom, K as runSingleChannelSecretStep, L as readStringParam, M as normalizeAccountId, N as normalizeResolvedSecretInputString, O as jsonResult, P as normalizeSecretInputString, R as registerPluginHttpRoute, S as deliverTextOrMediaReply, T as hasConfiguredSecretInput, U as resolveOpenProviderRuntimeGroupPolicy, V as resolveClientIp, W as resolveWebhookPath, X as warnMissingProviderGroupPolicyFallbackOnce, Y as waitForAbortSignal, Z as withResolvedWebhookRequestPipeline, _ as createChannelMessageReplyPipeline, a as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, b as createFixedWindowRateLimiter, c as applyAccountNameToChannelSection, d as buildBaseAccountStatusSnapshot, f as buildChannelConfigSchema, g as chunkTextForOutbound, h as buildTokenChannelStatusSummary, i as PAIRING_APPROVED_MESSAGE, j as migrateBaseNameToDefaultAccount, k as logTypingFailure, l as applyBasicWebhookRequestGuards, m as buildSingleChannelSecretPromptState, n as setZaloRuntime, o as WEBHOOK_RATE_LIMIT_DEFAULTS, p as buildSecretInputSchema, q as sendPayloadWithChunkedTextAndMedia, r as DEFAULT_ACCOUNT_ID, s as addWildcardAllowFrom, u as applySetupAccountConfigPatch, v as createChannelPairingController, w as formatPairingApproveHint, x as createWebhookAnomalyTracker, y as createDedupeCache, z as registerWebhookTarget } from "./runtime-api-B53PGWBW.js";
|
|
2
|
+
export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, addWildcardAllowFrom, applyAccountNameToChannelSection, applyBasicWebhookRequestGuards, applySetupAccountConfigPatch, buildBaseAccountStatusSnapshot, buildChannelConfigSchema, buildSecretInputSchema, buildSingleChannelSecretPromptState, buildTokenChannelStatusSummary, chunkTextForOutbound, createChannelMessageReplyPipeline, createChannelPairingController, createDedupeCache, createFixedWindowRateLimiter, createWebhookAnomalyTracker, deliverTextOrMediaReply, formatAllowFromLowercase, formatPairingApproveHint, hasConfiguredSecretInput, isNormalizedSenderAllowed, isNumericTargetId, jsonResult, logTypingFailure, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, normalizeAccountId, normalizeResolvedSecretInputString, normalizeSecretInputString, promptSingleChannelSecretInput, readJsonWebhookBodyOrReject, readStringParam, registerPluginHttpRoute, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveClientIp, resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, resolveWebhookPath, resolveWebhookTargetWithAuthOrRejectSync, runSingleChannelSecretStep, sendPayloadWithChunkedTextAndMedia, setTopLevelChannelDmPolicyWithAllowFrom, setZaloRuntime, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as
|
|
1
|
+
import { d as resolveZaloAccount, f as resolveZaloToken } from "./setup-core-BBA-V-j0.js";
|
|
2
2
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
3
3
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
4
4
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
@@ -6,6 +6,11 @@ import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
|
6
6
|
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
|
7
7
|
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
8
8
|
import { makeProxyFetch } from "openclaw/plugin-sdk/fetch-runtime";
|
|
9
|
+
//#region extensions/zalo/src/timeouts.ts
|
|
10
|
+
const ZALO_DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
11
|
+
const ZALO_OUTBOUND_MEDIA_TTL_MS = 2 * 6e4;
|
|
12
|
+
const ZALO_SEND_PHOTO_REQUEST_TIMEOUT_MS = 15e4;
|
|
13
|
+
//#endregion
|
|
9
14
|
//#region extensions/zalo/src/api.ts
|
|
10
15
|
/**
|
|
11
16
|
* Zalo Bot API client
|
|
@@ -45,8 +50,8 @@ function resolveZaloApiUrl(apiUrl) {
|
|
|
45
50
|
async function callZaloApi(method, token, body, options) {
|
|
46
51
|
const url = `${resolveZaloApiUrl(options?.apiUrl)}/bot${token}/${method}`;
|
|
47
52
|
const controller = new AbortController();
|
|
48
|
-
const requestTimeoutMs =
|
|
49
|
-
const timeoutId =
|
|
53
|
+
const requestTimeoutMs = resolveTimerTimeoutMs(options?.timeoutMs, ZALO_DEFAULT_REQUEST_TIMEOUT_MS);
|
|
54
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
50
55
|
const fetcher = options?.fetch ?? fetch;
|
|
51
56
|
try {
|
|
52
57
|
const data = await readProviderJsonResponse(await fetcher(url, {
|
|
@@ -58,7 +63,7 @@ async function callZaloApi(method, token, body, options) {
|
|
|
58
63
|
if (!data.ok) throw new ZaloApiError(data.description ?? `Zalo API error: ${method}`, data.error_code, data.description);
|
|
59
64
|
return data;
|
|
60
65
|
} finally {
|
|
61
|
-
|
|
66
|
+
clearTimeout(timeoutId);
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
/**
|
|
@@ -92,7 +97,10 @@ async function sendPhoto(token, params, fetcher) {
|
|
|
92
97
|
return callZaloApi("sendPhoto", token, {
|
|
93
98
|
...params,
|
|
94
99
|
photo: parsedPhotoUrl.href
|
|
95
|
-
}, {
|
|
100
|
+
}, {
|
|
101
|
+
timeoutMs: ZALO_SEND_PHOTO_REQUEST_TIMEOUT_MS,
|
|
102
|
+
fetch: fetcher
|
|
103
|
+
});
|
|
96
104
|
}
|
|
97
105
|
/**
|
|
98
106
|
* Send a temporary chat action such as typing.
|
|
@@ -285,4 +293,4 @@ async function sendPhotoZalo(chatId, photoUrl, options = {}) {
|
|
|
285
293
|
}, context.fetcher))());
|
|
286
294
|
}
|
|
287
295
|
//#endregion
|
|
288
|
-
export { getMe as a, sendChatAction as c, setWebhook as d, deleteWebhook as i, sendMessage as l, resolveZaloProxyFetch as n, getUpdates as o, ZaloApiError as r, getWebhookInfo as s, sendMessageZalo as t, sendPhoto as u };
|
|
296
|
+
export { getMe as a, sendChatAction as c, setWebhook as d, ZALO_OUTBOUND_MEDIA_TTL_MS as f, deleteWebhook as i, sendMessage as l, resolveZaloProxyFetch as n, getUpdates as o, ZaloApiError as r, getWebhookInfo as s, sendMessageZalo as t, sendPhoto as u };
|
package/dist/setup-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-
|
|
1
|
+
import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-BBA-V-j0.js";
|
|
2
2
|
import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-8qHRzDHx.js";
|
|
3
3
|
import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
4
4
|
//#region extensions/zalo/setup-api.ts
|
|
@@ -4,6 +4,7 @@ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runti
|
|
|
4
4
|
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core";
|
|
5
5
|
import { resolveAccountEntry } from "openclaw/plugin-sdk/routing";
|
|
6
6
|
import { buildSecretInputSchema, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
7
|
+
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
7
8
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, addWildcardAllowFrom, createDelegatedSetupWizardProxy, createPatchedAccountSetupAdapter, createSetupInputPresenceValidator, createSetupTranslator, formatDocsLink, mergeAllowFromEntries, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/setup";
|
|
8
9
|
//#region \0rolldown/runtime.js
|
|
9
10
|
var __defProp = Object.defineProperty;
|
|
@@ -184,16 +185,47 @@ async function promptZaloAllowFrom(params) {
|
|
|
184
185
|
//#region extensions/zalo/src/setup-core.ts
|
|
185
186
|
const t = createSetupTranslator();
|
|
186
187
|
const channel = "zalo";
|
|
187
|
-
const zaloSetupAdapter =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
188
|
+
const zaloSetupAdapter = {
|
|
189
|
+
...createPatchedAccountSetupAdapter({
|
|
190
|
+
channelKey: channel,
|
|
191
|
+
validateInput: createSetupInputPresenceValidator({
|
|
192
|
+
defaultAccountOnlyEnvError: "ZALO_BOT_TOKEN can only be used for the default account.",
|
|
193
|
+
whenNotUseEnv: [{
|
|
194
|
+
someOf: ["token", "tokenFile"],
|
|
195
|
+
message: "Zalo requires token or --token-file (or --use-env)."
|
|
196
|
+
}]
|
|
197
|
+
}),
|
|
198
|
+
buildPatch: (input) => input.useEnv ? {} : input.tokenFile ? { tokenFile: input.tokenFile } : input.token ? { botToken: input.token } : {}
|
|
195
199
|
}),
|
|
196
|
-
|
|
200
|
+
singleAccountKeysToMove: ["webhookSecret", "tokenFile"]
|
|
201
|
+
};
|
|
202
|
+
const zaloSetupContract = defineChannelSetupContract({
|
|
203
|
+
fields: {
|
|
204
|
+
token: {
|
|
205
|
+
kind: "string",
|
|
206
|
+
sensitive: true,
|
|
207
|
+
cli: {
|
|
208
|
+
flags: "--token <token>",
|
|
209
|
+
description: "Zalo bot token"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
tokenFile: {
|
|
213
|
+
kind: "string",
|
|
214
|
+
sensitive: true,
|
|
215
|
+
cli: {
|
|
216
|
+
flags: "--token-file <path>",
|
|
217
|
+
description: "Zalo bot token file"
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
useEnv: {
|
|
221
|
+
kind: "boolean",
|
|
222
|
+
cli: {
|
|
223
|
+
flags: "--use-env",
|
|
224
|
+
description: "Use ZALO_BOT_TOKEN"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
legacyAdapter: zaloSetupAdapter
|
|
197
229
|
});
|
|
198
230
|
const zaloDmPolicy = {
|
|
199
231
|
label: "Zalo",
|
|
@@ -284,4 +316,4 @@ function createZaloSetupWizardProxy(loadWizard) {
|
|
|
284
316
|
});
|
|
285
317
|
}
|
|
286
318
|
//#endregion
|
|
287
|
-
export {
|
|
319
|
+
export { noteZaloTokenHelp as a, listEnabledZaloAccounts as c, resolveZaloAccount as d, resolveZaloToken as f, zaloSetupContract as i, listZaloAccountIds as l, normalizeSecretInputString as m, zaloDmPolicy as n, promptZaloAllowFrom as o, buildSecretInputSchema as p, zaloSetupAdapter as r, accounts_exports as s, createZaloSetupWizardProxy as t, resolveDefaultZaloAccountId as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as noteZaloTokenHelp, d as resolveZaloAccount, n as zaloDmPolicy, o as promptZaloAllowFrom } from "./setup-core-BBA-V-j0.js";
|
|
2
2
|
import { DEFAULT_ACCOUNT_ID, buildSingleChannelSecretPromptState, createSetupTranslator, createStandardChannelSetupStatus, hasConfiguredSecretInput, promptSingleChannelSecretInput, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
|
|
3
3
|
//#region extensions/zalo/src/setup-surface.ts
|
|
4
4
|
const t = createSetupTranslator();
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { t as getZaloRuntime } from "./runtime-api-B53PGWBW.js";
|
|
2
|
+
import { r as ZaloApiError } from "./send-WPSy5EgF.js";
|
|
3
|
+
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
|
|
4
|
+
import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
5
|
+
//#region extensions/zalo/src/webhook-spool.ts
|
|
6
|
+
const ZALO_WEBHOOK_SPOOL_VERSION = 1;
|
|
7
|
+
const ZALO_WEBHOOK_DRAIN_INTERVAL_MS = 500;
|
|
8
|
+
const ZALO_WEBHOOK_MAX_CONCURRENT_DELIVERIES = 8;
|
|
9
|
+
const ZALO_WEBHOOK_PRUNE_INTERVAL_MS = 3600 * 1e3;
|
|
10
|
+
const ZALO_WEBHOOK_COMPLETED_TTL_MS = 720 * 60 * 6e4;
|
|
11
|
+
const ZALO_WEBHOOK_COMPLETED_MAX_ENTRIES = 2e4;
|
|
12
|
+
const ZALO_WEBHOOK_FAILED_TTL_MS = 720 * 60 * 6e4;
|
|
13
|
+
const ZALO_WEBHOOK_FAILED_MAX_ENTRIES = 5e3;
|
|
14
|
+
var ZaloWebhookPayloadError = class extends Error {
|
|
15
|
+
constructor(message, options) {
|
|
16
|
+
super(message, options);
|
|
17
|
+
this.name = "ZaloWebhookPayloadError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function nonEmptyString(value) {
|
|
24
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
25
|
+
}
|
|
26
|
+
function parseRawRecord(rawEvent) {
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(rawEvent);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new ZaloWebhookPayloadError("Zalo webhook body contains invalid JSON.", { cause: error });
|
|
32
|
+
}
|
|
33
|
+
if (!isRecord(parsed)) throw new ZaloWebhookPayloadError("Zalo webhook body must be a JSON object.");
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
function resolveUpdateRecord(envelope) {
|
|
37
|
+
if (envelope.ok === true && isRecord(envelope.result)) return envelope.result;
|
|
38
|
+
return envelope;
|
|
39
|
+
}
|
|
40
|
+
function inspectZaloWebhookEvent(rawEvent) {
|
|
41
|
+
const update = resolveUpdateRecord(parseRawRecord(rawEvent));
|
|
42
|
+
const message = isRecord(update.message) ? update.message : null;
|
|
43
|
+
const eventId = nonEmptyString(message?.message_id);
|
|
44
|
+
if (!eventId) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.message_id.");
|
|
45
|
+
const chatId = nonEmptyString((isRecord(message?.chat) ? message.chat : null)?.id);
|
|
46
|
+
if (!chatId) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.chat.id.");
|
|
47
|
+
return {
|
|
48
|
+
eventId,
|
|
49
|
+
laneKey: `chat:${chatId}`,
|
|
50
|
+
update
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function parseClaimedUpdate(payload, claimedId) {
|
|
54
|
+
if (payload.version !== ZALO_WEBHOOK_SPOOL_VERSION || typeof payload.rawEvent !== "string") throw new ZaloWebhookPayloadError("Zalo webhook spool payload is invalid.");
|
|
55
|
+
const facts = inspectZaloWebhookEvent(payload.rawEvent);
|
|
56
|
+
if (facts.eventId !== claimedId) throw new ZaloWebhookPayloadError("Zalo webhook message id changed after durable admission.");
|
|
57
|
+
const eventName = nonEmptyString(facts.update.event_name);
|
|
58
|
+
if (eventName !== "message.text.received" && eventName !== "message.image.received" && eventName !== "message.sticker.received" && eventName !== "message.unsupported.received") throw new ZaloWebhookPayloadError("Zalo webhook event_name is unsupported.");
|
|
59
|
+
const message = facts.update.message;
|
|
60
|
+
const from = isRecord(message.from) ? message.from : null;
|
|
61
|
+
const chat = isRecord(message.chat) ? message.chat : null;
|
|
62
|
+
if (!nonEmptyString(from?.id)) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.from.id.");
|
|
63
|
+
if (chat?.chat_type !== "PRIVATE" && chat?.chat_type !== "GROUP") throw new ZaloWebhookPayloadError("Zalo webhook message has an invalid chat type.");
|
|
64
|
+
if (typeof message.date !== "number" || !Number.isFinite(message.date)) throw new ZaloWebhookPayloadError("Zalo webhook message has an invalid date.");
|
|
65
|
+
if (eventName === "message.text.received" && typeof message.text !== "string") throw new ZaloWebhookPayloadError("Zalo text event is missing message.text.");
|
|
66
|
+
return facts.update;
|
|
67
|
+
}
|
|
68
|
+
function errorText(error) {
|
|
69
|
+
return error instanceof Error ? error.message : String(error);
|
|
70
|
+
}
|
|
71
|
+
function isZaloAuthenticationFailure(error) {
|
|
72
|
+
let current = error;
|
|
73
|
+
const seen = /* @__PURE__ */ new Set();
|
|
74
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
75
|
+
seen.add(current);
|
|
76
|
+
const candidate = current;
|
|
77
|
+
if (current instanceof ZaloApiError && (current.errorCode === 401 || current.errorCode === 403) || candidate.status === 401 || candidate.status === 403 || candidate.statusCode === 401 || candidate.statusCode === 403) return true;
|
|
78
|
+
current = candidate.cause;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
function createZaloWebhookIngress(options) {
|
|
83
|
+
const queue = options.queue ?? getZaloRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
|
|
84
|
+
const deferredClaims = /* @__PURE__ */ new Map();
|
|
85
|
+
const monitor = createChannelIngressMonitor({
|
|
86
|
+
queue,
|
|
87
|
+
inspect: (rawEvent) => inspectZaloWebhookEvent(rawEvent),
|
|
88
|
+
payload: {
|
|
89
|
+
storage: "raw-event",
|
|
90
|
+
version: ZALO_WEBHOOK_SPOOL_VERSION,
|
|
91
|
+
serialize: (rawEvent) => rawEvent,
|
|
92
|
+
deserialize: (rawEvent) => rawEvent,
|
|
93
|
+
createClaimError: (kind) => new ZaloWebhookPayloadError(kind === "invalid-version" ? "Zalo webhook spool payload is invalid." : "Zalo webhook identity changed after durable admission.")
|
|
94
|
+
},
|
|
95
|
+
deliver: async (_rawEvent, lifecycle, claim) => {
|
|
96
|
+
const update = parseClaimedUpdate(claim.payload, claim.id);
|
|
97
|
+
const boundLifecycle = bindIngressLifecycleToReplyOptions(lifecycle).turnAdoptionLifecycle;
|
|
98
|
+
let resolveDeferredClaim;
|
|
99
|
+
const deferredClaim = new Promise((resolve) => {
|
|
100
|
+
resolveDeferredClaim = resolve;
|
|
101
|
+
});
|
|
102
|
+
let deferredClaimSettled = false;
|
|
103
|
+
const settleDeferredClaim = () => {
|
|
104
|
+
if (deferredClaimSettled) return;
|
|
105
|
+
deferredClaimSettled = true;
|
|
106
|
+
if (deferredClaims.get(claim.id) === deferredClaim) deferredClaims.delete(claim.id);
|
|
107
|
+
resolveDeferredClaim();
|
|
108
|
+
};
|
|
109
|
+
await options.deliver(update, {
|
|
110
|
+
...boundLifecycle,
|
|
111
|
+
onAdopted: async () => {
|
|
112
|
+
try {
|
|
113
|
+
await boundLifecycle.onAdopted();
|
|
114
|
+
} finally {
|
|
115
|
+
settleDeferredClaim();
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
onDeferred: () => {
|
|
119
|
+
if (!deferredClaimSettled) deferredClaims.set(claim.id, deferredClaim);
|
|
120
|
+
boundLifecycle.onDeferred();
|
|
121
|
+
},
|
|
122
|
+
onAbandoned: () => {
|
|
123
|
+
Promise.resolve(boundLifecycle.onAbandoned()).finally(settleDeferredClaim);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return deferredClaims.has(claim.id) ? { kind: "deferred" } : { kind: "completed" };
|
|
127
|
+
},
|
|
128
|
+
pollIntervalMs: ZALO_WEBHOOK_DRAIN_INTERVAL_MS,
|
|
129
|
+
retention: {
|
|
130
|
+
pruneIntervalMs: ZALO_WEBHOOK_PRUNE_INTERVAL_MS,
|
|
131
|
+
completedTtlMs: ZALO_WEBHOOK_COMPLETED_TTL_MS,
|
|
132
|
+
completedMaxEntries: ZALO_WEBHOOK_COMPLETED_MAX_ENTRIES,
|
|
133
|
+
failedTtlMs: ZALO_WEBHOOK_FAILED_TTL_MS,
|
|
134
|
+
failedMaxEntries: ZALO_WEBHOOK_FAILED_MAX_ENTRIES
|
|
135
|
+
},
|
|
136
|
+
waitForDeliveryIdleBeforeRepump: false,
|
|
137
|
+
runPumpTask: runDetachedWebhookWork,
|
|
138
|
+
drain: {
|
|
139
|
+
adoptionStallTimeoutMs: DEFAULT_INGRESS_ADOPTION_STALL_MS,
|
|
140
|
+
startLimit: ZALO_WEBHOOK_MAX_CONCURRENT_DELIVERIES,
|
|
141
|
+
retryPolicy: {
|
|
142
|
+
maxAttempts: DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS,
|
|
143
|
+
deadLetterMinAgeMs: 0
|
|
144
|
+
},
|
|
145
|
+
resolveNonRetryableFailure: (error) => {
|
|
146
|
+
if (error instanceof ZaloWebhookPayloadError) return {
|
|
147
|
+
reason: "invalid-event",
|
|
148
|
+
message: error.message
|
|
149
|
+
};
|
|
150
|
+
if (isZaloAuthenticationFailure(error)) return {
|
|
151
|
+
reason: "authentication-failed",
|
|
152
|
+
message: errorText(error)
|
|
153
|
+
};
|
|
154
|
+
return null;
|
|
155
|
+
},
|
|
156
|
+
onLog: (message) => options.runtime.error?.(`zalo ingress: ${message}`)
|
|
157
|
+
},
|
|
158
|
+
createStoppedError: () => /* @__PURE__ */ new Error("Zalo ingress stopped."),
|
|
159
|
+
onError: (error) => options.runtime.error?.(`zalo ingress drain failed: ${errorText(error)}`)
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
accept: async (rawEvent) => {
|
|
163
|
+
await monitor.admit(rawEvent);
|
|
164
|
+
},
|
|
165
|
+
start: monitor.start,
|
|
166
|
+
stop: async () => {
|
|
167
|
+
await monitor.stop();
|
|
168
|
+
await Promise.allSettled(deferredClaims.values());
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const zaloWebhookIngressRuntime = { createZaloWebhookIngress };
|
|
173
|
+
//#endregion
|
|
174
|
+
export { ZaloWebhookPayloadError, zaloWebhookIngressRuntime };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/zalo",
|
|
3
|
-
"version": "2026.7.2-beta.
|
|
3
|
+
"version": "2026.7.2-beta.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/zalo",
|
|
9
|
-
"version": "2026.7.2-beta.
|
|
9
|
+
"version": "2026.7.2-beta.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"zod": "4.4.3"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"openclaw": ">=2026.7.2-beta.
|
|
14
|
+
"openclaw": ">=2026.7.2-beta.4"
|
|
15
15
|
},
|
|
16
16
|
"peerDependenciesMeta": {
|
|
17
17
|
"openclaw": {
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/zalo",
|
|
3
|
-
"version": "2026.7.2-beta.
|
|
3
|
+
"version": "2026.7.2-beta.4",
|
|
4
4
|
"description": "OpenClaw Zalo channel plugin for bot and webhook chats.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"peerDependencies": {
|
|
11
|
-
"openclaw": ">=2026.7.2-beta.
|
|
11
|
+
"openclaw": ">=2026.7.2-beta.4"
|
|
12
12
|
},
|
|
13
13
|
"peerDependenciesMeta": {
|
|
14
14
|
"openclaw": {
|
|
@@ -20,8 +20,19 @@
|
|
|
20
20
|
"./index.ts"
|
|
21
21
|
],
|
|
22
22
|
"setupEntry": "./dist/setup-entry.js",
|
|
23
|
+
"setupFeatures": {
|
|
24
|
+
"configPromotion": true
|
|
25
|
+
},
|
|
23
26
|
"channel": {
|
|
24
27
|
"id": "zalo",
|
|
28
|
+
"configuredState": {
|
|
29
|
+
"env": {
|
|
30
|
+
"anyOf": [
|
|
31
|
+
"ZALO_BOT_TOKEN",
|
|
32
|
+
"ZALO_WEBHOOK_SECRET"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
25
36
|
"label": "Zalo",
|
|
26
37
|
"selectionLabel": "Zalo (Bot API)",
|
|
27
38
|
"docsPath": "/channels/zalo",
|
|
@@ -31,7 +42,37 @@
|
|
|
31
42
|
"zl"
|
|
32
43
|
],
|
|
33
44
|
"order": 80,
|
|
34
|
-
"quickstartAllowFrom": true
|
|
45
|
+
"quickstartAllowFrom": true,
|
|
46
|
+
"setup": {
|
|
47
|
+
"fields": [
|
|
48
|
+
{
|
|
49
|
+
"key": "token",
|
|
50
|
+
"kind": "string",
|
|
51
|
+
"sensitive": true,
|
|
52
|
+
"cli": {
|
|
53
|
+
"flags": "--token <token>",
|
|
54
|
+
"description": "Zalo bot token"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"key": "tokenFile",
|
|
59
|
+
"kind": "string",
|
|
60
|
+
"sensitive": true,
|
|
61
|
+
"cli": {
|
|
62
|
+
"flags": "--token-file <path>",
|
|
63
|
+
"description": "Zalo bot token file"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"key": "useEnv",
|
|
68
|
+
"kind": "boolean",
|
|
69
|
+
"cli": {
|
|
70
|
+
"flags": "--use-env",
|
|
71
|
+
"description": "Use ZALO_BOT_TOKEN"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
35
76
|
},
|
|
36
77
|
"install": {
|
|
37
78
|
"npmSpec": "@openclaw/zalo",
|
|
@@ -39,10 +80,10 @@
|
|
|
39
80
|
"minHostVersion": ">=2026.4.10"
|
|
40
81
|
},
|
|
41
82
|
"compat": {
|
|
42
|
-
"pluginApi": ">=2026.7.2-beta.
|
|
83
|
+
"pluginApi": ">=2026.7.2-beta.4"
|
|
43
84
|
},
|
|
44
85
|
"build": {
|
|
45
|
-
"openclawVersion": "2026.7.2-beta.
|
|
86
|
+
"openclawVersion": "2026.7.2-beta.4"
|
|
46
87
|
},
|
|
47
88
|
"release": {
|
|
48
89
|
"publishToClawHub": true,
|