首页
/ OpenClaw Zalo 渠道插件:Bot API 凭证配置、Webhook 模式与源码级解析

OpenClaw Zalo 渠道插件:Bot API 凭证配置、Webhook 模式与源码级解析

2026-09-06 13:51:39作者:齐冠琰

OpenClaw 的 Zalo 渠道插件(@openclaw/zalo)基于 Zalo Bot API 接入越南主流即时通讯平台 Zalo,支持私聊与群聊两种会话形态。本文以插件自身的说明文档 README 为核心骨架,完整覆盖安装方式、Bot 凭证与代理配置、Webhook 模式接入三类关键配置,并结合仓库源码补充凭证解析顺序、长轮询 API 细节与群组访问控制策略,帮助你在生产环境正确落地该渠道并完成排障。

插件定位与安装方式

Zalo 插件在 OpenClaw 中以渠道插件(channel plugin)形式提供。其插件清单 openclaw.plugin.json 声明了渠道 id 为 zaloactivation.onStartupfalse(不随网关无条件自启动,需要配置后才启用),channels 字段登记为 ["zalo"]

README 给出了两种安装途径:

本地检出(local checkout)安装:

openclaw plugins install ./path/to/local/zalo-plugin

npm 安装:

openclaw plugins install @openclaw/zalo

此外,OpenClaw 的引导流程(Onboarding)中也内置了 Zalo 选项:在选择渠道时勾选 Zalo 并确认安装提示,即可自动拉取该插件。官方渠道文档 docs/channels/zalo.md 补充说明:当前版本的 OpenClaw 已将 Zalo 作为捆绑插件随发行版一起提供,打包构建无需单独安装;只有在旧版本构建或自定义安装中排除了 Zalo 时,才需要通过上述 npm 包方式显式安装。

基础配置:Bot 凭证、DM 策略与代理

README 给出的最小可用配置如下,这是轮询(long-polling)模式的典型写法:

{
  channels: {
    zalo: {
      enabled: true,
      botToken: "12345689:abc-xyz",
      dmPolicy: "pairing",
      proxy: "http://proxy.local:8080",
    },
  },
}

各字段含义:

配置项 说明
enabled 是否启用 Zalo 渠道启动
botToken Zalo Bot Platform 签发的 Bot Token,格式为 numeric_id:secret
dmPolicy 私聊访问策略,默认 pairing(配对码模式)
proxy API 请求走代的代理 URL

代理(proxy)的源码实现

proxy 字段并非简单透传,而是在 proxy.ts 中通过 resolveZaloProxyFetch 实现(L7-L19):对传入的代理 URL 做 trim 与去空格处理后,调用 openclaw/plugin-sdk/fetch-runtimemakeProxyFetch 构造一个带代理的 fetch 实现,并按 URL 字符串做进程内缓存(proxyCache)避免重复创建。这个带代理的 fetcher 会被注入到所有 Bot API 调用中,未配置时直接返回 undefined,走默认 fetch。

Bot Token 的解析顺序

Zalo Token 的解析逻辑集中在 token.tsresolveZaloToken(L34-L93),其优先级链条值得明确掌握:

  1. 账户级 accounts.<id>.botToken(多账户配置下优先生效);
  2. 账户级 accounts.<id>.tokenFile(从文件读取,且明确拒绝符号链接,见 readTokenFromFilerejectSymlink: true);
  3. 顶层 channels.zalo.botToken(扁平写法);
  4. 顶层 channels.zalo.tokenFile
  5. 环境变量 ZALO_BOT_TOKEN——仅对默认账户(default account)生效,非默认账户不会回落到该环境变量。

解析结果携带 sourceenv/config/configFile/none)与 statusavailable/configured_unavailable/missing)诊断信息,会展示在渠道状态快照中,便于排查凭证缺失问题。

Webhook 模式

当部署环境拥有公网可达的 HTTPS 端点时,可以改用 Webhook 推送替代长轮询。README 给出的配置为:

{
  channels: {
    zalo: {
      webhookUrl: "https://example.com/zalo-webhook",
      webhookSecret: "your-secret-8-plus-chars",
      webhookPath: "/zalo-webhook",
    },
  },
}

README 特别指出:如果省略 webhookPath,插件会直接使用 Webhook URL 自身的路径作为网关 HTTP 服务上的监听路径。并且任何配置变更后都需要重启网关(Restart the gateway after config changes)。

Webhook 注册与验证的源码链路

Webhook 的注册走 Zalo Bot API 的 setWebhook 方法,见 api.ts(L270-L299):setWebhook(token, { url, secret_token }) 将 URL 与密钥一同提交给 Zalo 平台;配套的 getWebhookInfo 可查询当前 Webhook 状态(含 updated_athas_custom_certificate),deleteWebhook 用于撤销。插件启动时根据 webhookUrl 是否存在决定运行模式——channel.ts 的账户快照中即以 account.config.webhookUrl ? "webhook" : "polling" 标记当前模式(L225)。

Webhook 模式的关键约束

结合仓库内官方渠道文档 docs/channels/zalo.md 的记载,Webhook 模式有以下必须满足的前提与行为细节:

  • Webhook URL 必须使用 HTTPS;密钥(webhookSecret)长度要求 8-256 字符
  • Zalo 推送事件携带 X-Bot-Api-Secret-Token 请求头,网关侧使用常量时间比较校验密钥,防止时序侧信道;
  • 请求体上限 1 MB,读取超时 30 秒;速率限制为 每路径+客户端 IP 120 次/60 秒,超限返回 HTTP 429;
  • 请求必须使用 Content-Type: application/json(或 +json 媒体类型);
  • 只有原始事件被持久化落盘成功后才返回 HTTP 200,落盘失败返回 HTTP 500;持久化成功对应的 200 响应头带有 x-openclaw-delivery-accepted: durable,反向代理可据此区分 OpenClaw 真实受理与通用 200;
  • 事件按消息 id 维护重放墓碑(tombstones):30 天、每账户最多 20,000 条已完成事件,用于幂等去重;
  • 按 Zalo API 文档约定,getUpdates 长轮询与 Webhook 二选一,互斥

长轮询模式与 Zalo Bot API 细节

未配置 webhookUrl 时,插件默认使用长轮询接收消息。API 客户端的核心封装在 api.ts

  • API 基地址默认为 https://bot-api.zaloplatforms.com(L14),可通过环境变量 ZALO_API_URL 覆盖;resolveZaloApiUrl(L112-L131)要求覆盖值必须是合法的 http/https URL,且不允许携带 query 或 fragment,尾部斜杠会被归一化去除;
  • 所有方法统一走 callZaloApi(L136-L176):请求形如 POST {apiBase}/bot{token}/{method},带默认请求超时控制(AbortController),响应体 { ok, result, error_code, description }ok 为假时抛出携带 errorCodeZaloApiError
  • getUpdates(L256-L265)timeout 参数以秒为单位、以字符串形式传给 API,默认 30 秒;客户端超时会额外放宽 5 秒((timeout + 5) * 1000 ms)避免误判。源码注释特别指出:Zalo 每次调用只返回单条 update,而非像 Telegram 那样返回数组,这是接入时最容易踩的差异点;
  • 轮询超时的识别ZaloApiError.isPollingTimeout(L106-L109)以 error_code === 408 判定"长时间等待无更新",监控循环据此将其视为正常的空转而非错误;
  • 消息类型:入站事件名包含 message.text.receivedmessage.image.receivedmessage.sticker.receivedmessage.unsupported.receivedZaloUpdate 定义,L55-L62);
  • 出站媒体sendPhoto(L203-L235)要求照片参数必须是绝对 HTTP(S) URL,并先经 SSRF 策略(resolvePinnedHostnameWithPolicy)固定主机名解析校验;其请求超时使用更长的 ZALO_SEND_PHOTO_REQUEST_TIMEOUT_MS,因为 Zalo 侧会先自行拉取该 URL 再回复。

访问控制:DM 配对与群组策略

私聊(DM)

  • dmPolicy 可选 pairing(默认,见 channel.ts 快照中 dmPolicy ?? "pairing")、allowlistopendisabled
  • 配对模式下,陌生发送者会收到配对码,批准前其消息被忽略,配对码 1 小时过期。常用命令:
    • openclaw pairing list zalo
    • openclaw pairing approve zalo <CODE>
  • allowFrom 接受数字 Zalo 用户 ID(不做用户名解析);dmPolicy: "open" 时必须显式写 "*"

群聊

群聊能力在插件元数据中显式声明:channel.tscapabilities.chatTypes["direct", "group"](L207),且 resolveRequireMention: () => true(L238)意味着群内必须 @提及才会触发,且不可关闭。群组访问控制由两级配置构成:

  • groupPolicyopen | allowlist | disabled
  • groupAllowFrom:限制群内哪些发送者 ID 可以触发机器人,未设置时回落到 DM 的 allowFrom
  • 解析规则:只要 channels.zalo 有配置而未显式设置 groupPolicy,就解析为 open;若 channels.zalo 整体缺失,则运行期按 allowlist 失败关闭(fail closed)。

源码中还有对应的安全告警收集器 collectZaloSecurityWarningschannel.ts L158-L197):当群策略为 open 且没有任何 groupAllowFrom/allowFrom 白名单时,会输出"任意群成员(提及门控)均可触发"的严重级别告警,并提示通过 groupPolicy="allowlist" + groupAllowFrom 收敛。

媒体与出站能力边界

插件的能力声明(channel.ts L206-L214)与实际行为:

能力 状态
私聊 / 群聊 均支持(群聊需 @提及)
媒体(入站/出站) 支持,受 mediaMaxMb 限制(默认 5 MB)
表情回应(reactions) 不支持
话题(threads) 不支持
投票(polls) 不支持
原生命令 不支持
引用回复(reply-to) 不使用,固定关闭(replyToMode 固定 off
流式输出 声明 block-streaming,但 Zalo 无专用出站队列/合并文本调优项

出站文本按 2000 字符分块(zaloTextChunkLimit = 2000channel.ts L94,为 Zalo API 的文本长度上限),发送前还会经过 sanitizeAssistantVisibleText 清理模型/工具 XML 痕迹。主动发送消息(CLI/cron 场景)使用聊天 ID 作为目标:

openclaw message send --channel zalo --target 123456789 --message "hi"

目标前缀 zalo:zl: 会被自动剥离(normalizeZaloMessagingTarget,L77-L83),group: 前缀的群目标可被推断为群组会话(L244-L247)。

故障排查

机器人不响应:

  • openclaw channels status --probe 探测 Token 与账户状态(快照会显示 tokenSourcetokenStatusmode);
  • 确认发送者已通过配对或 allowFrom 白名单;
  • 查看网关日志:openclaw logs --follow

Webhook 收不到事件:

  • 确认 URL 为 HTTPS、密钥长度 8-256 字符;
  • 确认网关 HTTP 端点在 webhookPath 上可达;
  • 确认没有同时运行 getUpdates 轮询(二者互斥);
  • 瞬时突发可能被 429 限速(120 次/60s/路径+IP),需退避重试。

注意:部分 Marketplace bot 存在平台侧限制导致无法被拉入群聊,这属于 Zalo 平台约束而非 OpenClaw 策略,需要在 Bot Platform 侧核实。

完整配置参考

以下为 docs/channels/zalo.md 给出的完整配置项(以 channels.zalo.accounts.<id>.* 为准;README 中的扁平顶层写法 channels.zalo.botToken 等属于遗留单账户简写,两种形式均受支持):

配置项 说明 默认值
channels.zalo.enabled 启用/禁用渠道启动 true
channels.zalo.accounts.<id>.botToken Zalo Bot Platform 签发的 Bot Token -
channels.zalo.accounts.<id>.tokenFile 从文件读取 Token(拒绝符号链接) -
channels.zalo.accounts.<id>.name 展示名称 -
channels.zalo.accounts.<id>.enabled 启用/禁用该账户 true
channels.zalo.accounts.<id>.dmPolicy 账户级私聊策略 pairing
channels.zalo.accounts.<id>.allowFrom 私聊白名单(数字用户 ID) -
channels.zalo.accounts.<id>.groupPolicy 账户级群组策略 见上文群组规则
channels.zalo.accounts.<id>.groupAllowFrom 群内发送者白名单,回落 allowFrom -
channels.zalo.accounts.<id>.mediaMaxMb 入站/出站媒体上限(MB) 5
channels.zalo.accounts.<id>.webhookUrl 启用 Webhook 模式(必须 HTTPS) -
channels.zalo.accounts.<id>.webhookSecret Webhook 密钥(8-256 字符) -
channels.zalo.accounts.<id>.webhookPath 网关 HTTP 上的 Webhook 路径 Webhook URL 的路径
channels.zalo.accounts.<id>.proxy API 请求代理 URL -
channels.zalo.accounts.<id>.responsePrefix 出站回复前缀覆盖 -
channels.zalo.defaultAccount 多账户时的默认账户 default

环境变量 ZALO_BOT_TOKEN=... 仅解析默认账户的 Token。配置变更生效的最后一步始终是重启网关;多账户场景下,在 accounts.<id> 下为每个 Bot 各自配置独立的 botToken/name 即可。

登录后查看全文
热门项目推荐
相关项目推荐