OpenClaw 中文社区版 Zalo 渠道接入实战指南:Bot API 插件安装、DM 配对控制与长轮询/Webhook 双模式详解
OpenClaw 中文社区版 Zalo 渠道接入实战指南:Bot API 插件安装、DM 配对控制与长轮询/Webhook 双模式详解
Zalo 是越南主流的即时通讯应用,其 Bot API 允许第三方机器人以 1:1 会话的方式接入用户。OpenClaw 中文社区版(openclaw-cn)通过独立的 @clawdbot/zalo 插件把 Zalo 变成 Gateway 的可路由渠道:入站消息统一归一化进共享的消息通道,回复则确定性地回到同一个 Zalo 会话,模型无需自行选择渠道。本文基于仓库中的 Zalo 渠道文档 与 extensions/zalo 插件源码,完整讲解插件安装、Token 配置、多账号、DM 访问控制、长轮询与 Webhook 双模式、消息类型处理及故障排查,读完即可把 Zalo 机器人接入你的个人 AI 助手。
当前功能定位与能力边界
Zalo 渠道目前的定位是 experimental(实验性):只支持私聊(direct messages),群聊按 Zalo 官方文档说明为 "coming soon",尚未开放。它是支撑类(support)或通知类场景的合适选择——因为路由是确定性的:回复永远发回 Zalo,模型永远不参与"选择渠道"。
从插件元数据可以看到该渠道的完整画像(见 extensions/zalo/src/channel.ts 与 package.json):
- 渠道 ID:
zalo,别名为zl; - 展示名:
Zalo (Bot API); - 简介:"Vietnam-focused messaging platform with Bot API";
- 能力声明:
chatTypes: ["direct"]、media: true、blockStreaming: true,且reactions / threads / polls / nativeCommands均为false。
能力矩阵如下(与官方文档一致):
| Feature | Status |
|---|---|
| Direct messages | ✅ Supported |
| Groups | ❌ Coming soon (per Zalo docs) |
| Media (images) | ✅ Supported |
| Reactions | ❌ Not supported |
| Threads | ❌ Not supported |
| Polls | ❌ Not supported |
| Native commands | ❌ Not supported |
| Streaming | ⚠️ Blocked (2000 char limit) |
安装 Zalo 插件
Zalo 以插件形式随仓库分发,不内置在核心安装包中。安装方式有三种:
- 通过 CLI 安装 npm 包:
openclaw-cn plugins install @clawdbot/zalo - 从源码 checkout 直接安装本地插件目录:
openclaw-cn plugins install ./extensions/zalo - 在 onboarding 向导中选择 Zalo 并确认安装提示,插件会被自动拉取(extensions/zalo/README.md 也记录了相同流程)。
插件本身是一个标准的 OpenClaw 插件,入口 extensions/zalo/index.ts 通过 Plugin SDK 做了三件事:注册 Zalo 渠道(registerChannel)、注册 Webhook HTTP 处理器(registerHttpHandler)、把运行时注入全局单例(setZaloRuntime)。关于插件机制可参考 插件文档。
快速配置入门
安装插件后,最快跑通的路径只有四步:
- 安装插件(见上节);
- 设置 Token:
- 环境变量方式:
ZALO_BOT_TOKEN=... - 配置文件方式:
channels.zalo.botToken: "..."
- 环境变量方式:
- 重启 Gateway(或完成 onboarding);
- 私聊默认走 pairing 流程:机器人首次被联系时,需要先批准配对码。
最小可用配置如下(JSON5 格式,可复制到你的配置文件):
{
channels: {
zalo: {
enabled: true,
botToken: "12345689:abc-xyz",
dmPolicy: "pairing"
}
}
}
创建 Bot Token(Zalo Bot Platform)
- 打开 https://bot.zaloplatforms.com 并登录;
- 创建一个新 bot 并配置其设置;
- 复制 bot token(格式形如
12345689:abc-xyz,即 "bot id : secret")。
Token 的有效性可以在接入前直接验证。源码层面,插件通过 getMe 接口校验 Token 并拉取 bot 信息(见 extensions/zalo/src/api.ts),其 API 基础地址为 https://bot-api.zaloplatforms.com。Gateway 启动账号时还会先做一次 2.5 秒的快速探测,拿到 bot 名称后写入启动日志 <a href="https://link.gitcode.com/i/d4e8ca85e131a028f8287e81741cfcbf" target="_blank">accountId] starting provider (name)(见 [channel.ts 的 startAccount)。
Token 的三种来源与解析优先级
插件支持从环境变量、配置项、Token 文件三种途径读取 Token。解析逻辑位于 extensions/zalo/src/token.ts,对默认账号(default)而言优先级是:
- 账号级配置
accounts.<id>.botToken; - 账号级配置
accounts.<id>.tokenFile(从文件读取并 trim); - 顶层配置
channels.zalo.botToken; - 顶层配置
channels.zalo.tokenFile; - 环境变量
ZALO_BOT_TOKEN(仅默认账号可用)。
重要限制:环境变量 ZALO_BOT_TOKEN 只对默认账号生效。这一约束在插件层面被强制校验——配置向导中,若 input.useEnv 且账号不是默认账号,会直接返回错误 "ZALO_BOT_TOKEN can only be used for the default account"(见 channel.ts)。
多账号支持
需要同时跑多个 Zalo 机器人时,使用 channels.zalo.accounts 声明每个账号,每个账号可独立配置 Token、开关、DM 策略与 Webhook:
{
channels: {
zalo: {
enabled: true,
accounts: {
support: {
botToken: "111111:aaa",
name: "Support Bot",
enabled: true
},
notify: {
botToken: "222222:bbb",
dmPolicy: "allowlist",
allowFrom: [123456789]
}
}
}
}
}
账号解析逻辑见 extensions/zalo/src/accounts.ts:账号级配置会与顶层配置合并(账号字段覆盖顶层),defaultAccount 可指定默认账号;账号的启用状态是顶层 enabled 与账号级 enabled 的"与"关系。配置结构本身由 config-schema.ts 中的 Zod schema 约束,accounts 是一个 catchall 映射,每个账号键的字段与顶层一致。
DM 访问控制:配对、白名单与开放模式
私聊消息的访问控制由 channels.zalo.dmPolicy 决定,可选值为 pairing | allowlist | open | disabled,默认 pairing。底层实现在 monitor.ts 的消息流水线中:
- pairing(默认):未知发送者会收到一个配对码,其消息在批准前被忽略;配对码 1 小时后过期。机器人首次被私聊时,会自动通过
sendMessage回发一段包含"你的 Zalo user id"与配对码的说明(buildPairingReply)。 - allowlist:只有
allowFrom中列出的数字用户 ID 才能触发会话,其余发送者被静默忽略。 - open:放开所有私聊,但配置
open时allowFrom必须包含"*"(向导也会引导填写)。 - disabled:完全拒绝所有私聊入站。
批准配对的操作命令:
openclaw-cn pairing list zalo
openclaw-cn pairing approve zalo <CODE>
注意:Zalo 没有用户名查询能力,channels.zalo.allowFrom 只接受数字用户 ID(见 onboarding.ts 的校验逻辑,非纯数字会提示 "Use a numeric Zalo user id")。用户 ID 支持 zalo: / zl: 前缀写法,解析时会自动剥离归一化(见 channel.ts)。更完整的配对机制可参考 配对指南。
长轮询 vs Webhook:两种事件接收模式
长轮询(默认,无需公网地址)
默认使用长轮询接收事件:getUpdates 以 30 秒超时发起请求,Zalo API 单次调用返回单个 update(与 Telegram 返回数组不同,见 api.ts 的注释);HTTP 408 被识别为轮询超时(无新消息)而非错误;出现真实错误时等待 5 秒重试(见 monitor.ts)。由于轮询模式下 Gateway 主动向外拉取,不需要公网 URL,适合内网或难以暴露端点的场景。
Webhook 模式
需要实时推送时,配置 channels.zalo.webhookUrl 与 channels.zalo.webhookSecret。源码层面的强约束如下(见 monitor.ts):
- Webhook URL 必须使用 HTTPS(非
https://前缀直接抛错); - Webhook secret 长度必须在 8–256 字符之间;
- Zalo 会以
X-Bot-Api-Secret-Token请求头携带 secret 用于校验,插件用timingSafeEqual做恒定时间比较(monitor.ts); - Gateway HTTP 服务在
channels.zalo.webhookPath上处理请求,缺省时自动取 webhookUrl 的路径(monitor.ts); - 仅接受
POST与 JSON Content-Type,请求体上限 1 MB; - 内置防护:按"路径+来源 IP"做 60 秒窗口 120 次限流,5 分钟内的重复事件(按
event_name:message_id)会被去重,避免推送重放(monitor.ts)。
Webhook 配置示例:
{
channels: {
zalo: {
webhookUrl: "https://example.com/zalo-webhook",
webhookSecret: "your-secret-8-plus-chars",
webhookPath: "/zalo-webhook"
}
}
}
关键约束:getUpdates(轮询)与 Webhook 在 Zalo API 层面是互斥的。插件在启用轮询前会先调用 deleteWebhook 清理残留的 webhook 注册,启用 Webhook 时则调用 setWebhook 注册(并在 Gateway 停止时通过 abort 监听自动注销,monitor.ts)。因此不要同时启用两种模式,否则会互相干扰。
支持的消息类型与媒体处理
入站事件按 event_name 分发(monitor.ts):
| 事件 | 处理方式 |
|---|---|
message.text.received |
完整处理:进入 Agent 流水线 |
message.image.received |
下载图片(受 mediaMaxMb 限制)后连同 caption 一并进入流水线 |
message.sticker.received |
仅记录日志,不触发 Agent 回复 |
message.unsupported.received |
仅记录日志(例如来自受保护用户的消息) |
图片入站链路(monitor.ts):先 fetchRemoteMedia 拉取 URL,再经 saveMediaBuffer 以 mediaMaxMb * 1024 * 1024 为上限落盘,失败则降级为纯文本处理。出站图片通过 sendPhoto 发送,多条媒体时第一条附带文字说明(monitor.ts)。
所有入站消息都会归一化进共享的 channel envelope(含媒体占位符),最终会话上下文里携带 From: zalo:<userId>、To: zalo:<chatId>、SessionKey、Provider: zalo、MediaPath 等字段(monitor.ts),与其余渠道完全一致,因此 Zalo 私聊会话与 Agent 的主会话共享上下文。
限制:2000 字符分块、媒体上限与流式屏蔽
- 出站文本按 2000 字符分块:
textChunkLimit: 2000是 Zalo API 的硬限制。插件内置按换行/空格边界断行的 chunker(channel.ts),出站时还会先做 Markdown 表格转换(convertMarkdownTables),再按 2000 字符分块逐条发送(monitor.ts)。 - 媒体大小上限:
channels.zalo.mediaMaxMb同时约束入站下载与出站媒体,默认 5 MB(常量DEFAULT_MEDIA_MAX_MB)。 - 流式输出被默认屏蔽:
blockStreaming: true。原因是 2000 字符限制会让流式逐段输出的意义大打折扣,插件干脆在能力声明中禁用流式。
投递目标:CLI 与定时任务
Zalo 作为投递渠道时,目标直接使用聊天 ID(chat id)。示例:
openclaw-cn message send --channel zalo --target 123456789 --message "hi"
出站目标会经过 normalizeZaloMessagingTarget 归一化(剥离 zalo: / zl: 前缀,channel.ts),targetResolver 用 /^\d{3,}$/ 判断是否形如 ID 并给出 <chatId> 提示,因此 cron 任务、定时通知等场景可以直接把 Zalo chat id 当作目标使用。
故障排查
机器人不响应:
- 用探测命令确认 Token 有效:
openclaw-cn channels status --probe(该命令会调用getMe实时探测,见 probe.ts); - 确认发送者已获批准(pairing 批准或已加入
allowFrom); - 查看 Gateway 日志:
clawdbot logs --follow。
Webhook 收不到事件:
- 确认 Webhook URL 是 HTTPS;
- 确认 secret 长度在 8–256 字符之间;
- 确认 Gateway HTTP 端点能在配置的路径上被外部访问;
- 确认没有同时运行
getUpdates轮询(两者互斥)。
配置参考(Zalo)
完整配置总览见 配置文档。以下是 Zalo 渠道的全部配置项:
Provider 选项:
channels.zalo.enabled:启用/禁用渠道启动。channels.zalo.botToken:Zalo Bot Platform 的 bot token。channels.zalo.tokenFile:从文件路径读取 token。channels.zalo.dmPolicy:pairing | allowlist | open | disabled(默认pairing)。channels.zalo.allowFrom:DM 白名单(用户 ID)。open模式必须包含"*";向导会要求填写数字 ID。channels.zalo.mediaMaxMb:入站/出站媒体上限(MB,默认 5)。channels.zalo.webhookUrl:启用 Webhook 模式(必须 HTTPS)。channels.zalo.webhookSecret:Webhook secret(8–256 字符)。channels.zalo.webhookPath:Gateway HTTP 服务器上的 Webhook 路径。channels.zalo.proxy:API 请求使用的代理 URL。
多账号选项(channels.zalo.accounts.<id>.*):
accounts.<id>.botToken:账号级 token。accounts.<id>.tokenFile:账号级 token 文件。accounts.<id>.name:显示名称。accounts.<id>.enabled:账号级开关。accounts.<id>.dmPolicy:账号级 DM 策略。accounts.<id>.allowFrom:账号级白名单。accounts.<id>.webhookUrl:账号级 Webhook URL。accounts.<id>.webhookSecret:账号级 Webhook secret。accounts.<id>.webhookPath:账号级 Webhook 路径。accounts.<id>.proxy:账号级代理 URL。
此外 schema 还支持 channels.zalo.defaultAccount(指定默认账号 ID)与账号级 markdown、responsePrefix 等扩展字段(见 config-schema.ts)。所有配置修改后均需重启 Gateway 生效(插件的 reload.configPrefixes 仅覆盖 channels.zalo 前缀)。排查 Zalo 渠道问题时,可结合 插件 README、API 客户端实现 与 监控/Webhook 实现 进一步定位。