@openclaw/zalo 2026.9.6 → 2026.9.7

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/assets/icon.png CHANGED
Binary file
@@ -1,6 +1,6 @@
1
- import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-CCMLSh4Q.mjs";
2
- import { t as zaloPlugin } from "./channel-C4-ovULW.mjs";
3
- import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-8qHRzDHx.mjs";
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-BE-EaCnS.mjs";
2
+ import { t as zaloPlugin } from "./channel-COgJOEix.mjs";
3
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-Blsyu-ot.mjs";
4
4
  import { zaloSetupWizard } from "../setup-api.js";
5
5
  import "../api.js";
6
6
  export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
@@ -1,4 +1,4 @@
1
- import { c as isZaloAccountConfigured, d as resolveZaloAccount, i as zaloSetupContract, l as listZaloAccountIds, p as buildSecretInputSchema, s as inspectZaloAccount, t as createZaloSetupWizardProxy, u as resolveDefaultZaloAccountId } from "./setup-core-CCMLSh4Q.mjs";
1
+ import { c as isZaloAccountConfigured, d as resolveZaloAccount, i as zaloSetupContract, l as listZaloAccountIds, p as buildSecretInputSchema, s as inspectZaloAccount, t as createZaloSetupWizardProxy, u as resolveDefaultZaloAccountId } from "./setup-core-BE-EaCnS.mjs";
2
2
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Cpsqe5Cv.mjs";
3
3
  import { describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
4
4
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
@@ -24,7 +24,7 @@ import { z } from "zod";
24
24
  import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
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-B7EqgwH6.mjs"), "zaloActionsRuntime");
27
+ const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./send-sCQINy2G.mjs"), "sendMessageZalo");
28
28
  const providerId = "zalo";
29
29
  const ZALO_ACTIONS = /* @__PURE__ */ new Set(["send"]);
30
30
  function listEnabledAccounts(cfg, accountId) {
@@ -54,8 +54,7 @@ const zaloMessageActions = {
54
54
  allowEmpty: true
55
55
  });
56
56
  const mediaUrl = readStringParam(params, "media", { trim: false });
57
- const { sendMessageZalo } = await loadZaloActionsRuntime();
58
- const result = await sendMessageZalo(to ?? "", content ?? "", {
57
+ const result = await (await loadZaloActionsRuntime())(to ?? "", content ?? "", {
59
58
  accountId: accountId ?? void 0,
60
59
  mediaUrl: mediaUrl ?? void 0,
61
60
  cfg,
@@ -187,8 +186,8 @@ function looksLikeZaloChatId(raw, normalized) {
187
186
  const target = normalizeZaloMessagingTarget(normalized ?? raw);
188
187
  return Boolean(target);
189
188
  }
190
- const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-Bm9k5qqB.mjs"));
191
- const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-CNyPRzlv.mjs").then((n) => n.t)).zaloSetupWizard);
189
+ const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-F1ScBwNC.mjs"));
190
+ const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-DMW4dLWX.mjs").then((n) => n.t)).zaloSetupWizard);
192
191
  const zaloTextChunkLimit = 2e3;
193
192
  async function sendZaloDelivery(ctx) {
194
193
  const result = await (await loadZaloChannelRuntime()).sendZaloText({
@@ -1,5 +1,5 @@
1
- import { m as normalizeSecretInputString } from "./setup-core-CCMLSh4Q.mjs";
2
- import { a as getMe, n as resolveZaloProxyFetch, r as ZaloApiError, t as sendMessageZalo } from "./send-Div0xZFE.mjs";
1
+ import { m as normalizeSecretInputString } from "./setup-core-BE-EaCnS.mjs";
2
+ import { i as getMe, n as ZaloApiError, sendMessageZalo, t as resolveZaloProxyFetch } from "./send-sCQINy2G.mjs";
3
3
  import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-outbound";
4
4
  import { runChannelProbe } from "openclaw/plugin-sdk/text-utility-runtime";
5
5
  //#region extensions/zalo/src/probe.ts
@@ -62,7 +62,7 @@ async function startZaloGatewayAccount(ctx) {
62
62
  setStatus: ctx.setStatus
63
63
  });
64
64
  ctx.log?.info(`[${account.accountId}] starting provider${zaloBotLabel} mode=${mode}`);
65
- const { monitorZaloProvider } = await import("./monitor-BXzlUPAd.mjs");
65
+ const { monitorZaloProvider } = await import("./monitor-BbW2wx_3.mjs");
66
66
  return monitorZaloProvider({
67
67
  token,
68
68
  account,
@@ -0,0 +1,8 @@
1
+ import { resolveOpenProviderRuntimeGroupPolicy as resolveZaloRuntimeGroupPolicy } from "openclaw/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
+ //#endregion
8
+ export { resolveZaloRuntimeGroupPolicy as n, normalizeZaloAllowEntry as t };
@@ -1,18 +1,18 @@
1
- import { n as resolveZaloRuntimeGroupPolicy, t as normalizeZaloAllowEntry } from "./group-access-8qHRzDHx.mjs";
1
+ import { n as resolveZaloRuntimeGroupPolicy, t as normalizeZaloAllowEntry } from "./group-access-Blsyu-ot.mjs";
2
2
  import { t as getZaloRuntime } from "./runtime-Dfhzt7qb.mjs";
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-Div0xZFE.mjs";
3
+ import { a as getUpdates, c as sendMessage, d as ZALO_OUTBOUND_MEDIA_TTL_MS, l as sendPhoto, n as ZaloApiError, o as getWebhookInfo, r as deleteWebhook, s as sendChatAction, t as resolveZaloProxyFetch, u as setWebhook } from "./send-sCQINy2G.mjs";
4
4
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
5
5
  import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
6
6
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
7
7
  import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
8
8
  import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
9
- import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
10
- import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
11
- import { registerPluginHttpRoute, resolveWebhookPath } from "openclaw/plugin-sdk/webhook-ingress";
12
9
  import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
10
+ import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
13
11
  import { createChannelPartialDeliveryError, formatInboundMediaUnavailableText, resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
12
+ import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
14
13
  import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
15
14
  import { sleepWithAbort, waitForAbortSignal } from "openclaw/plugin-sdk/runtime-env";
15
+ import { registerPluginHttpRoute, resolveWebhookPath } from "openclaw/plugin-sdk/webhook-ingress";
16
16
  import { createHostedOutboundMediaStore } from "openclaw/plugin-sdk/outbound-media";
17
17
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
18
18
  //#region extensions/zalo/src/monitor-durable.ts
@@ -172,8 +172,8 @@ function resolveZaloTimestampMs(date) {
172
172
  if (!date) return;
173
173
  return date >= UNIX_MILLISECONDS_THRESHOLD ? date : date * 1e3;
174
174
  }
175
- const loadZaloWebhookRuntime = createLazyRuntimeNamedExport(() => import("./monitor.webhook-B_9qJI4O.mjs"), "zaloWebhookRuntime");
176
- const loadZaloWebhookIngressRuntime = createLazyRuntimeNamedExport(() => import("./webhook-spool-BSTKDQ8X.mjs"), "zaloWebhookIngressRuntime");
175
+ const loadZaloWebhookRuntime = createLazyRuntimeNamedExport(() => import("./monitor.webhook-CaknDJfy.mjs"), "zaloWebhookRuntime");
176
+ const loadZaloWebhookIngressRuntime = createLazyRuntimeNamedExport(() => import("./webhook-spool-CTCugbD_.mjs"), "zaloWebhookIngressRuntime");
177
177
  const loadZaloWebhookModule = createLazyRuntimeModule(async () => ({
178
178
  ...await loadZaloWebhookRuntime(),
179
179
  ...await loadZaloWebhookIngressRuntime()
@@ -216,26 +216,10 @@ function normalizeWebhookUrl(url) {
216
216
  function logVerbose(core, runtime, message) {
217
217
  if (core.logging.shouldLogVerbose()) runtime.log?.(`[zalo] ${message}`);
218
218
  }
219
- async function handleZaloWebhookRequest(req, res) {
220
- const { handleZaloWebhookRequest: handleZaloWebhookRequestInternal } = await loadZaloWebhookModule();
221
- return await handleZaloWebhookRequestInternal(req, res);
222
- }
223
219
  function startPollingLoop(params) {
224
- const { token, account, config, runtime, core, mediaMaxMb, canHostMedia, webhookUrl, webhookPath, abortSignal, isStopped, statusSink, fetcher } = params;
220
+ const { abortSignal, isStopped, ...processingContext } = params;
221
+ const { token, account, runtime, statusSink, fetcher } = processingContext;
225
222
  const pollTimeout = 30;
226
- const processingContext = {
227
- token,
228
- account,
229
- config,
230
- runtime,
231
- core,
232
- mediaMaxMb,
233
- canHostMedia,
234
- webhookUrl,
235
- webhookPath,
236
- statusSink,
237
- fetcher
238
- };
239
223
  runtime.log?.(`[${account.accountId}] Zalo polling loop started timeout=${String(pollTimeout)}s`);
240
224
  const poll = async () => {
241
225
  if (isStopped() || abortSignal.aborted) return;
@@ -269,22 +253,9 @@ function startPollingLoop(params) {
269
253
  poll();
270
254
  }
271
255
  async function processUpdate(params) {
272
- const { update, token, account, config, runtime, core, mediaMaxMb, statusSink, fetcher } = params;
256
+ const { update, ...sharedContext } = params;
257
+ const { account, core, runtime } = sharedContext;
273
258
  const { event_name, message } = update;
274
- const sharedContext = {
275
- token,
276
- account,
277
- config,
278
- runtime,
279
- core,
280
- mediaMaxMb,
281
- canHostMedia: params.canHostMedia,
282
- webhookUrl: params.webhookUrl,
283
- webhookPath: params.webhookPath,
284
- statusSink,
285
- fetcher,
286
- turnAdoptionLifecycle: params.turnAdoptionLifecycle
287
- };
288
259
  if (!message) return;
289
260
  switch (event_name) {
290
261
  case "message.text.received":
@@ -296,8 +267,7 @@ async function processUpdate(params) {
296
267
  case "message.image.received":
297
268
  await handleImageMessage({
298
269
  message,
299
- ...sharedContext,
300
- mediaMaxMb
270
+ ...sharedContext
301
271
  });
302
272
  break;
303
273
  case "message.sticker.received":
@@ -312,10 +282,7 @@ async function handleTextMessage(params) {
312
282
  if (!text?.trim()) return;
313
283
  await processMessageWithPipeline({
314
284
  ...params,
315
- text,
316
- mediaKind: void 0,
317
- mediaPath: void 0,
318
- mediaType: void 0
285
+ text
319
286
  });
320
287
  }
321
288
  async function handleImageMessage(params) {
@@ -324,9 +291,7 @@ async function handleImageMessage(params) {
324
291
  const authorization = await authorizeZaloMessage({
325
292
  ...params,
326
293
  text: caption,
327
- mediaKind: "image",
328
- mediaPath: void 0,
329
- mediaType: void 0
294
+ mediaKind: "image"
330
295
  });
331
296
  if (!authorization) return;
332
297
  let mediaPath;
@@ -404,8 +369,7 @@ async function authorizeZaloMessage(params) {
404
369
  groupAllowFrom: normalizeStringEntries(account.config.groupAllowFrom),
405
370
  command: shouldComputeAuth ? {} : void 0
406
371
  });
407
- const access = await resolveChannelIngress();
408
- const senderAccess = access.senderAccess;
372
+ const senderAccess = (await resolveChannelIngress()).senderAccess;
409
373
  if (isGroup) {
410
374
  warnMissingProviderGroupPolicyFallbackOnce({
411
375
  providerMissingFallbackApplied: senderAccess.providerMissingFallbackApplied,
@@ -447,10 +411,8 @@ async function authorizeZaloMessage(params) {
447
411
  return;
448
412
  }
449
413
  return {
450
- channelIngress: access,
451
414
  resolveChannelIngress,
452
415
  chatId,
453
- commandAuthorized: access.commandAccess.requested ? access.commandAccess.authorized : void 0,
454
416
  isGroup,
455
417
  rawBody,
456
418
  senderId,
@@ -460,11 +422,7 @@ async function authorizeZaloMessage(params) {
460
422
  async function processMessageWithPipeline(params) {
461
423
  const { message, token, account, config, runtime, core, mediaPath, mediaKind, mediaType, statusSink, fetcher, agentBody: agentBodyOverride, authorization: authorizationOverride } = params;
462
424
  const { message_id, date } = message;
463
- const authorization = authorizationOverride ?? await authorizeZaloMessage({
464
- ...params,
465
- mediaPath,
466
- mediaType
467
- });
425
+ const authorization = authorizationOverride ?? await authorizeZaloMessage(params);
468
426
  if (!authorization) return;
469
427
  const { isGroup, chatId, senderId, senderName, rawBody } = authorization;
470
428
  const agentBody = agentBodyOverride ?? rawBody;
@@ -714,7 +672,7 @@ async function monitorZaloProvider(options) {
714
672
  stopHandlers.push(unregisterHostedMediaRoute);
715
673
  }
716
674
  if (useWebhook) {
717
- const { createZaloWebhookIngress, registerZaloWebhookTarget } = await loadZaloWebhookModule();
675
+ const { createZaloWebhookIngress, registerZaloWebhookTarget, handleZaloWebhookRequest } = await loadZaloWebhookModule();
718
676
  if (!effectiveWebhookUrl || !webhookSecret) throw new Error("Zalo webhookUrl and webhookSecret are required for webhook mode");
719
677
  if (!effectiveWebhookUrl.startsWith("https://")) throw new Error("Zalo webhook URL must use HTTPS");
720
678
  if (webhookSecret.length < 8 || webhookSecret.length > 256) throw new Error("Zalo webhook secret must be 8-256 characters");
@@ -794,20 +752,16 @@ async function monitorZaloProvider(options) {
794
752
  }
795
753
  runtime.log?.(`[${account.accountId}] Zalo polling mode: clearing webhook before startup`);
796
754
  try {
797
- try {
798
- const currentWebhookUrl = normalizeWebhookUrl((await getWebhookInfo(token, fetcher)).result?.url);
799
- if (!currentWebhookUrl) runtime.log?.(`[${account.accountId}] Zalo polling mode ready (no webhook configured)`);
800
- else {
801
- runtime.log?.(`[${account.accountId}] Zalo polling mode disabling existing webhook ${describeWebhookTarget(currentWebhookUrl)}`);
802
- await deleteWebhook(token, fetcher);
803
- runtime.log?.(`[${account.accountId}] Zalo polling mode ready (webhook disabled)`);
804
- }
805
- } catch (err) {
806
- if (err instanceof ZaloApiError && err.errorCode === 404) runtime.log?.(`[${account.accountId}] Zalo polling mode webhook inspection unavailable; continuing without webhook cleanup`);
807
- else throw err;
755
+ const currentWebhookUrl = normalizeWebhookUrl((await getWebhookInfo(token, fetcher)).result?.url);
756
+ if (!currentWebhookUrl) runtime.log?.(`[${account.accountId}] Zalo polling mode ready (no webhook configured)`);
757
+ else {
758
+ runtime.log?.(`[${account.accountId}] Zalo polling mode disabling existing webhook ${describeWebhookTarget(currentWebhookUrl)}`);
759
+ await deleteWebhook(token, fetcher);
760
+ runtime.log?.(`[${account.accountId}] Zalo polling mode ready (webhook disabled)`);
808
761
  }
809
762
  } catch (err) {
810
- runtime.error?.(`[${account.accountId}] Zalo polling startup could not clear webhook: ${formatZaloError(err)}`);
763
+ if (err instanceof ZaloApiError && err.errorCode === 404) runtime.log?.(`[${account.accountId}] Zalo polling mode webhook inspection unavailable; continuing without webhook cleanup`);
764
+ else runtime.error?.(`[${account.accountId}] Zalo polling startup could not clear webhook: ${formatZaloError(err)}`);
811
765
  }
812
766
  startPollingLoop({
813
767
  token,
@@ -1,5 +1,6 @@
1
- import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveClientIp, resolveWebhookTargetWithAuthOrRejectSync, withResolvedWebhookRequestPipeline } from "../runtime-api.js";
2
- import { ZaloWebhookPayloadError } from "./webhook-spool-BSTKDQ8X.mjs";
1
+ import { ZaloWebhookPayloadError } from "./webhook-spool-CTCugbD_.mjs";
2
+ import { resolveClientIp } from "openclaw/plugin-sdk/core";
3
+ import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveWebhookTargetWithAuthOrRejectSync, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-ingress";
3
4
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
4
5
  import { readWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
5
6
  //#region extensions/zalo/src/monitor.webhook.ts
@@ -1,12 +1,12 @@
1
- import { d as resolveZaloAccount, f as resolveZaloToken } from "./setup-core-CCMLSh4Q.mjs";
1
+ import { d as resolveZaloAccount, f as resolveZaloToken } from "./setup-core-BE-EaCnS.mjs";
2
2
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
3
3
  import { stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
4
- import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
4
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
6
5
  import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
7
6
  import { assertOkOrThrowProviderError, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
8
7
  import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
9
8
  import { makeProxyFetch } from "openclaw/plugin-sdk/fetch-runtime";
9
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
10
10
  //#region extensions/zalo/src/timeouts.ts
11
11
  const ZALO_DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
12
12
  const ZALO_OUTBOUND_MEDIA_TTL_MS = 12e4;
@@ -179,25 +179,6 @@ function createZaloSendReceipt(params) {
179
179
  kind: params.kind
180
180
  });
181
181
  }
182
- function toZaloSendResult(response, params) {
183
- if (response.ok && response.result) return {
184
- ok: true,
185
- messageId: response.result.message_id,
186
- receipt: createZaloSendReceipt({
187
- messageId: response.result.message_id,
188
- chatId: params.chatId,
189
- kind: params.kind
190
- })
191
- };
192
- return {
193
- ok: false,
194
- error: "Failed to send message",
195
- receipt: createZaloSendReceipt({
196
- chatId: params.chatId,
197
- kind: params.kind
198
- })
199
- };
200
- }
201
182
  async function runZaloSend(failureMessage, params, assertDirectAdapterHandoff, send) {
202
183
  let handoffRejected = false;
203
184
  let handoffError;
@@ -211,11 +192,20 @@ async function runZaloSend(failureMessage, params, assertDirectAdapterHandoff, s
211
192
  }
212
193
  } : void 0;
213
194
  try {
214
- const result = toZaloSendResult(await send(assertCurrent), params);
215
- return result.ok ? result : {
195
+ const response = await send(assertCurrent);
196
+ const messageId = response.ok && response.result ? response.result.message_id : void 0;
197
+ const receipt = createZaloSendReceipt({
198
+ ...params,
199
+ messageId
200
+ });
201
+ return response.ok && response.result ? {
202
+ ok: true,
203
+ messageId,
204
+ receipt
205
+ } : {
216
206
  ok: false,
217
207
  error: failureMessage,
218
- receipt: result.receipt
208
+ receipt
219
209
  };
220
210
  } catch (err) {
221
211
  if (handoffRejected && Object.is(handoffError, err)) throw err;
@@ -247,69 +237,44 @@ function resolveSendContext(options) {
247
237
  fetcher: resolveZaloProxyFetch(proxy)
248
238
  };
249
239
  }
250
- function resolveValidatedSendContext(chatId, options) {
240
+ async function sendMessageZalo(chatId, text, options = {}) {
251
241
  const { token, fetcher } = resolveSendContext(options);
252
- if (!token) return {
253
- ok: false,
254
- error: "No Zalo bot token configured"
255
- };
256
- const trimmedChatId = normalizeZaloSendChatId(chatId);
257
- if (!trimmedChatId) return {
242
+ const normalizedChatId = token ? stripTargetKindPrefix(stripChannelTargetPrefix(chatId, "zalo", "zl")) : "";
243
+ if (!token || !normalizedChatId) return {
258
244
  ok: false,
259
- error: "No chat_id provided"
260
- };
261
- return {
262
- ok: true,
263
- chatId: trimmedChatId,
264
- token,
265
- fetcher
266
- };
267
- }
268
- function normalizeZaloSendChatId(chatId) {
269
- return stripTargetKindPrefix(stripChannelTargetPrefix(chatId, "zalo", "zl"));
270
- }
271
- function resolveSendContextOrFailure(chatId, options) {
272
- const context = resolveValidatedSendContext(chatId, options);
273
- return context.ok ? { context } : { failure: {
274
- ok: false,
275
- error: context.error,
245
+ error: token ? "No chat_id provided" : "No Zalo bot token configured",
276
246
  receipt: createZaloSendReceipt({
277
247
  chatId,
278
248
  kind: "unknown"
279
249
  })
280
- } };
281
- }
282
- async function sendMessageZalo(chatId, text, options = {}) {
283
- const resolved = resolveSendContextOrFailure(chatId, options);
284
- if ("failure" in resolved) return resolved.failure;
285
- const { context } = resolved;
250
+ };
286
251
  if (options.mediaUrl && (options.mediaUrl.trim() || !text)) {
287
252
  const photoUrl = options.mediaUrl.trim();
288
253
  if (!photoUrl) return {
289
254
  ok: false,
290
255
  error: "No photo URL provided",
291
256
  receipt: createZaloSendReceipt({
292
- chatId: context.chatId,
257
+ chatId: normalizedChatId,
293
258
  kind: "media"
294
259
  })
295
260
  };
296
261
  const caption = text || options.caption;
297
262
  return await runZaloSend("Failed to send photo", {
298
- chatId: context.chatId,
263
+ chatId: normalizedChatId,
299
264
  kind: "media"
300
- }, options.assertDirectAdapterHandoff, (assertCurrent) => sendPhoto(context.token, {
301
- chat_id: context.chatId,
265
+ }, options.assertDirectAdapterHandoff, (assertCurrent) => sendPhoto(token, {
266
+ chat_id: normalizedChatId,
302
267
  photo: photoUrl,
303
268
  caption
304
- }, context.fetcher, assertCurrent));
269
+ }, fetcher, assertCurrent));
305
270
  }
306
271
  return await runZaloSend("Failed to send message", {
307
- chatId: context.chatId,
272
+ chatId: normalizedChatId,
308
273
  kind: "text"
309
- }, options.assertDirectAdapterHandoff, (assertCurrent) => sendMessage(context.token, {
310
- chat_id: context.chatId,
274
+ }, options.assertDirectAdapterHandoff, (assertCurrent) => sendMessage(token, {
275
+ chat_id: normalizedChatId,
311
276
  text: truncateUtf16Safe(text, 2e3)
312
- }, context.fetcher, assertCurrent));
277
+ }, fetcher, assertCurrent));
313
278
  }
314
279
  //#endregion
315
- 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 };
280
+ export { getUpdates as a, sendMessage as c, ZALO_OUTBOUND_MEDIA_TTL_MS as d, getMe as i, sendPhoto as l, ZaloApiError as n, getWebhookInfo as o, deleteWebhook as r, sendChatAction as s, sendMessageZalo, resolveZaloProxyFetch as t, setWebhook as u };
@@ -227,7 +227,7 @@ const zaloDmPolicy = createChannelDmPolicy({
227
227
  resolveAccount: (cfg, accountId) => {
228
228
  return resolveZaloAccount({
229
229
  cfg,
230
- accountId: accountId && normalizeAccountId$1(accountId) ? normalizeAccountId$1(accountId) ?? DEFAULT_ACCOUNT_ID$1 : resolveDefaultZaloAccountId(cfg)
230
+ accountId: accountId ? normalizeAccountId$1(accountId) : resolveDefaultZaloAccountId(cfg)
231
231
  });
232
232
  },
233
233
  applyPatch: ({ cfg, account, patch }) => patchScopedAccountConfig({
@@ -1,4 +1,4 @@
1
- import { a as noteZaloTokenHelp, n as zaloDmPolicy, o as promptZaloAllowFrom, s as inspectZaloAccount } from "./setup-core-CCMLSh4Q.mjs";
1
+ import { a as noteZaloTokenHelp, n as zaloDmPolicy, o as promptZaloAllowFrom, s as inspectZaloAccount } from "./setup-core-BE-EaCnS.mjs";
2
2
  import { DEFAULT_ACCOUNT_ID, buildSingleChannelSecretPromptState, createSetupTranslator, createStandardChannelSetupStatus, hasConfiguredSecretInput, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
3
3
  //#region \0rolldown/runtime.js
4
4
  var __defProp = Object.defineProperty;
@@ -125,15 +125,15 @@ const zaloSetupWizard = {
125
125
  return "/zalo-webhook";
126
126
  }
127
127
  })();
128
- let webhookSecretResult = await promptSingleChannelSecretInput({
128
+ const promptWebhookSecret = (hasWebhookSecret) => promptSingleChannelSecretInput({
129
129
  cfg: next,
130
130
  prompter,
131
131
  providerHint: "zalo-webhook",
132
132
  credentialLabel: t("wizard.zalo.webhookSecret"),
133
133
  secretInputMode: options?.secretInputMode,
134
134
  ...buildSingleChannelSecretPromptState({
135
- accountConfigured: hasConfiguredSecretInput(resolvedAccount.config.webhookSecret),
136
- hasConfigToken: hasConfiguredSecretInput(resolvedAccount.config.webhookSecret),
135
+ accountConfigured: hasWebhookSecret,
136
+ hasConfigToken: hasWebhookSecret,
137
137
  allowEnv: false
138
138
  }),
139
139
  envPrompt: "",
@@ -141,24 +141,10 @@ const zaloSetupWizard = {
141
141
  inputPrompt: t("wizard.zalo.webhookSecretInput"),
142
142
  preferredEnvVar: "ZALO_WEBHOOK_SECRET"
143
143
  });
144
+ let webhookSecretResult = await promptWebhookSecret(hasConfiguredSecretInput(resolvedAccount.config.webhookSecret));
144
145
  while (webhookSecretResult.action === "set" && typeof webhookSecretResult.value === "string" && (webhookSecretResult.value.length < 8 || webhookSecretResult.value.length > 256)) {
145
146
  await prompter.note(t("wizard.zalo.webhookSecretLength"), t("wizard.zalo.webhookTitle"));
146
- webhookSecretResult = await promptSingleChannelSecretInput({
147
- cfg: next,
148
- prompter,
149
- providerHint: "zalo-webhook",
150
- credentialLabel: t("wizard.zalo.webhookSecret"),
151
- secretInputMode: options?.secretInputMode,
152
- ...buildSingleChannelSecretPromptState({
153
- accountConfigured: false,
154
- hasConfigToken: false,
155
- allowEnv: false
156
- }),
157
- envPrompt: "",
158
- keepPrompt: t("wizard.zalo.webhookSecretKeep"),
159
- inputPrompt: t("wizard.zalo.webhookSecretInput"),
160
- preferredEnvVar: "ZALO_WEBHOOK_SECRET"
161
- });
147
+ webhookSecretResult = await promptWebhookSecret(false);
162
148
  }
163
149
  const webhookSecret = webhookSecretResult.action === "set" ? webhookSecretResult.value : resolvedAccount.config.webhookSecret;
164
150
  const webhookPath = (await prompter.text({
@@ -1,5 +1,5 @@
1
1
  import { t as getZaloRuntime } from "./runtime-Dfhzt7qb.mjs";
2
- import { r as ZaloApiError } from "./send-Div0xZFE.mjs";
2
+ import { n as ZaloApiError } from "./send-sCQINy2G.mjs";
3
3
  import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
4
4
  import { normalizeNullableString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { z } from "zod";
package/dist/api.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./.setup/setup-core-CCMLSh4Q.mjs";
2
- import { t as zaloPlugin } from "./.setup/channel-C4-ovULW.mjs";
3
- import { n as resolveZaloRuntimeGroupPolicy } from "./.setup/group-access-8qHRzDHx.mjs";
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./.setup/setup-core-BE-EaCnS.mjs";
2
+ import { t as zaloPlugin } from "./.setup/channel-COgJOEix.mjs";
3
+ import { n as resolveZaloRuntimeGroupPolicy } from "./.setup/group-access-Blsyu-ot.mjs";
4
4
  import { zaloSetupWizard } from "./setup-api.js";
5
5
  export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
@@ -1,2 +1,2 @@
1
- import { t as zaloPlugin } from "./.setup/channel-C4-ovULW.mjs";
1
+ import { t as zaloPlugin } from "./.setup/channel-COgJOEix.mjs";
2
2
  export { zaloPlugin };
@@ -1,2 +1,2 @@
1
- import { n as resolveZaloRuntimeGroupPolicy } from "./.setup/group-access-8qHRzDHx.mjs";
1
+ import { n as resolveZaloRuntimeGroupPolicy } from "./.setup/group-access-Blsyu-ot.mjs";
2
2
  export { resolveZaloRuntimeGroupPolicy };
@@ -1,16 +1,2 @@
1
1
  import { n as setZaloRuntime } from "./.setup/runtime-Dfhzt7qb.mjs";
2
- import { formatAllowFromLowercase, isNormalizedSenderAllowed } from "openclaw/plugin-sdk/allow-from";
3
- import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-outbound";
4
- import { PAIRING_APPROVED_MESSAGE, buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/channel-status";
5
- import { deliverTextOrMediaReply, isNumericTargetId, sendPayloadWithChunkedTextAndMedia } from "openclaw/plugin-sdk/reply-payload";
6
- import { buildBaseAccountStatusSnapshot } from "openclaw/plugin-sdk/status-helpers";
7
- import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
8
- import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
9
- import { DEFAULT_ACCOUNT_ID, buildChannelConfigSchema, createDedupeCache, formatPairingApproveHint, jsonResult, normalizeAccountId, readStringParam, resolveClientIp } from "openclaw/plugin-sdk/core";
10
- import { addWildcardAllowFrom, applyAccountNameToChannelSection, applySetupAccountConfigPatch, buildSingleChannelSecretPromptState, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, promptSingleChannelSecretInput, runSingleChannelSecretStep, setTopLevelChannelDmPolicyWithAllowFrom } from "openclaw/plugin-sdk/setup";
11
- import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
12
- import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
13
- import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
14
- import { waitForAbortSignal } from "openclaw/plugin-sdk/runtime";
15
- import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, readJsonWebhookBodyOrReject, registerPluginHttpRoute, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveWebhookPath, resolveWebhookTargetWithAuthOrRejectSync, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-ingress";
16
- 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 };
2
+ export { setZaloRuntime };
package/dist/setup-api.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./.setup/setup-core-CCMLSh4Q.mjs";
2
- import { n as resolveZaloRuntimeGroupPolicy } from "./.setup/group-access-8qHRzDHx.mjs";
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./.setup/setup-core-BE-EaCnS.mjs";
2
+ import { n as resolveZaloRuntimeGroupPolicy } from "./.setup/group-access-Blsyu-ot.mjs";
3
3
  import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
4
4
  //#region extensions/zalo/setup-api.ts
5
5
  function createLazyObjectValue(load) {
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  var setup_entry_default = defineBundledChannelSetupEntry({
5
5
  importMetaUrl: import.meta.url,
6
6
  plugin: {
7
- specifier: fileURLToPath(new URL(".setup/api-BDqk_OyX.mjs", import.meta.url).href),
7
+ specifier: fileURLToPath(new URL(".setup/api-YaSbmx07.mjs", import.meta.url).href),
8
8
  exportName: "zaloPlugin"
9
9
  },
10
10
  secrets: {
@@ -1,3 +1,3 @@
1
- import { n as zaloSetupWizard } from "./.setup/setup-surface-CNyPRzlv.mjs";
2
- import { r as zaloSetupAdapter } from "./.setup/setup-core-CCMLSh4Q.mjs";
1
+ import { n as zaloSetupWizard } from "./.setup/setup-surface-DMW4dLWX.mjs";
2
+ import { r as zaloSetupAdapter } from "./.setup/setup-core-BE-EaCnS.mjs";
3
3
  export { zaloSetupAdapter, zaloSetupWizard };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/zalo",
3
- "version": "2026.9.6",
3
+ "version": "2026.9.7",
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.9.6"
11
+ "openclaw": ">=2026.9.7"
12
12
  },
13
13
  "peerDependenciesMeta": {
14
14
  "openclaw": {
@@ -83,10 +83,10 @@
83
83
  "minHostVersion": ">=2026.4.10"
84
84
  },
85
85
  "compat": {
86
- "pluginApi": ">=2026.9.6"
86
+ "pluginApi": ">=2026.9.7"
87
87
  },
88
88
  "build": {
89
- "openclawVersion": "2026.9.6"
89
+ "openclawVersion": "2026.9.7"
90
90
  },
91
91
  "release": {
92
92
  "publishToClawHub": true,
@@ -1,5 +0,0 @@
1
- import { t as sendMessageZalo } from "./send-Div0xZFE.mjs";
2
- //#region extensions/zalo/src/actions.runtime.ts
3
- const zaloActionsRuntime = { sendMessageZalo };
4
- //#endregion
5
- export { zaloActionsRuntime };
@@ -1,15 +0,0 @@
1
- import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/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 };