OpenClaw Zalo 频道插件接入指南:Bot API 配置、Webhook 模式与群组访问控制
OpenClaw Zalo 频道插件接入指南:Bot API 配置、Webhook 模式与群组访问控制
Zalo 是越南市场主流的即时通讯平台,OpenClaw 通过 @openclaw/zalo 插件接入其 Bot API,让 Gateway 可以运行 Zalo 机器人,处理 1:1 私聊与群聊消息。本文基于 zalo/README.md 与仓库源码,系统讲解插件的安装、令牌配置、Webhook 模式、DM/群组访问控制、能力边界与常见排障,读完即可完成从零到可用的 Zalo 频道接入,并理解其底层运行机制。
插件定位与前置要求
Zalo 频道以插件形式提供,不随 OpenClaw 核心安装包内置。插件通过 OpenClaw 的 Channel Plugin SDK 注册,入口文件为 zalo/index.ts,其核心声明(id: "zalo"、别名 zl、能力列表等)定义在 zalo/src/channel.ts。接入前你需要:
- 一个可用的 OpenClaw Gateway 环境;
- 在 Zalo Bot Platform(
https://bot.zaloplatforms.com)创建 Bot 并取得 bot token(形如12345689:abc-xyz); - 决定消息接收方式:默认长轮询(long-polling),无需公网地址;若使用 Webhook 模式则需一个 HTTPS 公网地址。
从源码结构看,Zalo 在 OpenClaw 中的定位是"确定性路由"频道:回复始终回到同一 Zalo 会话,模型不会自行选择发往哪个渠道,适合做客服、通知等需要固定回路的场景。
插件安装
方式一:本地源码目录安装
在仓库源码检出的环境中执行:
openclaw plugins install ./extensions/zalo
方式二:npm 安装
openclaw plugins install @openclaw/zalo
方式三:Onboarding 引导安装
在 OpenClaw 的初始化引导(onboarding)流程中选择 Zalo,并确认安装提示,插件会被自动拉取。插件包元数据位于 zalo/package.json,其中声明了 openclaw.extensions、setupEntry 与频道注册信息;zalo/openclaw.plugin.json 声明了 channels: ["zalo"] 的频道归属。
基础配置与令牌解析
最小配置
在 OpenClaw 配置文件中加入以下片段(JSON5 格式):
{
channels: {
zalo: {
enabled: true,
botToken: "12345689:abc-xyz",
dmPolicy: "pairing",
proxy: "http://proxy.local:8080",
},
},
}
字段说明:
| 配置项 | 说明 | 默认值 |
|---|---|---|
enabled |
是否启动该频道 | true |
botToken |
Zalo Bot Platform 签发的 bot token | 无 |
dmPolicy |
私聊访问策略:pairing | allowlist | open | disabled |
pairing |
proxy |
走代理访问 Zalo API(如 http://proxy.local:8080) |
无 |
令牌的四种来源与解析优先级
Zalo 插件对令牌的解析集中在 zalo/src/token.ts,优先级依次为:
- 账户级配置
channels.zalo.accounts.<id>.botToken(config 来源); - 账户级令牌文件
channels.zalo.accounts.<id>.tokenFile(configFile 来源); - 频道级
channels.zalo.botToken/channels.zalo.tokenFile; - 环境变量
ZALO_BOT_TOKEN(仅默认账户可用,env 来源)。
令牌文件通过 tryReadSecretFileSync 读取,并带有 rejectSymlink: true 的安全校验(拒绝符号链接文件)。账户的合并逻辑见 zalo/src/accounts.ts:enabled 为频道级与账户级取与,令牌解析结果会记录 tokenSource(env | config | configFile | none),供状态面板展示与诊断。
多账户配置
插件支持多账户,使用 channels.zalo.accounts.<id> 为每个账户单独配置令牌、名称、策略与代理:
{
channels: {
zalo: {
enabled: true,
defaultAccount: "support",
accounts: {
support: {
name: "Support Bot",
botToken: "111:aaa",
dmPolicy: "allowlist",
allowFrom: [123456789],
},
notifier: {
botToken: "222:bbb",
dmPolicy: "pairing",
},
},
},
},
}
多账户能力由 createCatchallMultiAccountChannelSchema 支持(见 zalo/src/config-schema.ts),账户合并时跳过 defaultAccount 键。
通过环境变量配置
默认账户(default account)可以直接使用环境变量,无需写入配置文件:
export ZALO_BOT_TOKEN=12345689:abc-xyz
注意:ZALO_BOT_TOKEN 仅对默认账户生效;非默认账户必须使用 botToken 或 tokenFile 配置(见 zalo/src/setup-core.ts 中的校验逻辑)。
Webhook 模式
如果希望 Zalo 主动推送事件(而不是轮询),配置 Webhook 模式:
{
channels: {
zalo: {
webhookUrl: "https://example.com/zalo-webhook",
webhookSecret: "your-secret-8-plus-chars",
webhookPath: "/zalo-webhook",
},
},
}
约束与校验
从 zalo/src/monitor.ts 的运行时校验可以看到 Webhook 模式的硬性要求:
webhookUrl与webhookSecret必须同时提供,否则启动报错;- Webhook URL 必须以
https://开头(强制 HTTPS); webhookSecret长度必须在 8~256 字符之间;- 若省略
webhookPath,插件会从 Webhook URL 中推导路径(取 URL 的 pathname),推导失败则报错。
事件验证与安全防护
zalo/src/monitor.webhook.ts 实现了完整的 Webhook 请求管道:
- 只接受
POST请求,且要求Content-Type: application/json(或+json类型); - Zalo 通过
X-Bot-Api-Secret-Token请求头携带 secret,插件使用timingSafeEqual常量时间比较进行鉴权(monitor.webhook.ts),防止时序侧信道攻击; - 按
path + client IP做固定窗口限流(createFixedWindowRateLimiter),突发流量会返回 HTTP 429; - 对
event_name + message_id做 5 分钟去重窗口(createDedupeCache),忽略重复投递事件; - 异常状态码会被
webhookAnomalyTracker记录并告警; - Zalo 的更新体是直接形如
{ event_name, message, ... }的 JSON(并非 Telegram 的{ ok, result }包装),解析逻辑兼容两种形态(monitor.webhook.ts)。
Webhook 注册后,插件在启动时会调用 setWebhook 向 Zalo 登记,停止时调用 deleteWebhook 清理;切换到轮询模式时也会先检查并删除已有 Webhook(部分 Zalo 环境不支持 webhook 检查时按 404 容错继续,见 monitor.ts)。
Webhook 与长轮询互斥
getUpdates(轮询)与 Webhook 在 Zalo API 文档中是互斥的。配置了 webhookUrl 即进入 Webhook 模式,轮询模式启动前会清除已配置的 Webhook。
长轮询模式
默认模式为长轮询(ZaloApiError.isPollingTimeout,即 errorCode 408 表示"无更新")。轮询循环实现在 zalo/src/monitor.ts:以 30 秒超时调用 getUpdates,每次调用返回单个更新(与 Telegram 返回数组不同,见 zalo/src/api.ts 的注释),循环使用 setImmediate 连续调度,出错后延迟 5 秒重试。该模式无需公网地址,适合本地开发与内网环境。
消息处理流程与媒体支持
支持的入站消息类型
zalo/src/monitor.ts 按 event_name 分发:
| 事件 | 处理方式 |
|---|---|
message.text.received |
完整处理,进入回复管线 |
message.image.received |
下载图片后处理(受 mediaMaxMb 限制) |
message.sticker.received |
仅记录日志,不触发 Agent 回复 |
message.unsupported.received |
仅记录日志(如受保护用户的消息) |
图片收发
- 入站:通过
core.channel.media.fetchRemoteMedia下载photo_url,经saveMediaBuffer存入沙箱媒体工作区,大小上限为mediaMaxMb * 1024 * 1024字节;下载失败只记录错误,不阻断流程(monitor.ts)。 - 出站:图片通过
sendPhoto发送,支持 caption(zalo/src/send.ts)。
文本长度限制与分块
Zalo API 的文本上限为 2000 字符(常量 ZALO_TEXT_LIMIT,同时定义于 zalo/src/channel.ts 与 zalo/src/monitor.ts)。插件在出站时按 chunkMarkdownTextWithMode 分块发送,并依据 channels.zalo.markdown 配置将 Markdown 表格转换为代码块形式;回复前还会发送 typing 聊天动作(5 秒超时)。
能力边界
从 zalo/src/channel.ts 的 capabilities 声明可以确认:
| 能力 | 状态 |
|---|---|
| 私聊(DM) | ✅ 支持 |
| 群组 | ⚠️ 支持,默认 fail-closed(allowlist) |
| 图片媒体 | ✅ 支持 |
| Reactions | ❌ 不支持 |
| Threads / Polls | ❌ 不支持 |
| 原生命令 | ❌ 不支持 |
| 流式输出 | ⚠️ 默认阻断(受 2000 字符限制影响) |
访问控制
DM 私聊策略(dmPolicy)
默认值为 pairing,未知发送者会收到配对码,配对码 1 小时后过期,批准前消息被忽略。配对批准命令:
openclaw pairing list zalo
openclaw pairing approve zalo <CODE>
配对通过后,插件会向该用户发送确认消息(PAIRING_APPROVED_MESSAGE,见 zalo/src/channel.runtime.ts)。DM 策略可选值:
pairing:配对码交换(默认);allowlist:仅允许allowFrom中的用户 ID;open:任何用户可直聊(需在allowFrom中配置"*",且openclaw channels status会提示安全风险,见 zalo/src/status-issues.ts);disabled:禁止所有私聊。
allowFrom 只接受数字用户 ID(如 123456789),不支持用户名解析;配置引导向导会校验输入必须为纯数字(zalo/src/setup-surface.ts)。ID 可带 zalo: 或 zl: 前缀,运行时会被归一化剥离。
群组策略(groupPolicy)
- 默认值:
allowlist(fail-closed,未显式配置时按安全默认处理); groupAllowFrom用于限制群内可触发 Bot 的发送者 ID;未设置时回退到allowFrom;groupPolicy: "disabled":丢弃所有群消息;groupPolicy: "open":允许任意群成员触发(需 @ 提及 Bot,mention-gated);- 运行时兜底:即使
channels.zalo配置段完全缺失,群组访问仍回退为allowlist安全策略(zalo/src/group-access.ts)。
群组策略的告警收集逻辑在 zalo/src/channel.ts:当 groupPolicy="open" 且未配置任何 allowlist 时,会提示"任意群成员可触发(mention-gated)",并建议设置 channels.zalo.groupPolicy="allowlist" 与 channels.zalo.groupAllowFrom。
完整配置参考
以下是综合 zalo/src/types.ts 与配置文档得到的全部配置项:
{
channels: {
zalo: {
enabled: true,
botToken: "12345689:abc-xyz", // Bot Platform 令牌
tokenFile: "/path/to/token", // 或从文件读取令牌
name: "My Zalo Bot", // 账户显示名(CLI/UI 列表)
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: [123456789], // DM 白名单(数字用户 ID)
groupPolicy: "allowlist", // open | allowlist | disabled
groupAllowFrom: [123456789], // 群内发送者白名单,缺省回退 allowFrom
mediaMaxMb: 5, // 入站/出站媒体上限(MB,默认 5)
markdown: { tables: "code" }, // Markdown 表格输出模式
proxy: "http://proxy.local:8080",
responsePrefix: "Bot: ", // 出站回复前缀覆盖
// Webhook 模式:
webhookUrl: "https://example.com/zalo-webhook",
webhookSecret: "your-secret-8-plus-chars",
webhookPath: "/zalo-webhook",
},
},
}
多账户额外支持:
channels.zalo.accounts.<id>.botToken/tokenFile/name/enabled;channels.zalo.accounts.<id>.dmPolicy/allowFrom/groupPolicy/groupAllowFrom;channels.zalo.accounts.<id>.webhookUrl/webhookSecret/webhookPath/proxy;channels.zalo.defaultAccount:指定默认账户 ID。
所有配置修改后需要重启 Gateway 生效(插件声明了 reload: { configPrefixes: <a href="https://link.gitcode.com/i/371273daac589bea68fb3b7ca8fb0eef" target="_blank">"channels.zalo"] },见 [zalo/src/channel.ts)。
CLI 发送与投递目标
Zalo 频道支持通过 CLI/cron 直接投递消息,目标为聊天 ID:
openclaw message send --channel zalo --target 123456789 --message "hi"
目标解析支持 zalo: / zl: 前缀与 group: 前缀(群聊),路由构建见 zalo/src/session-route.ts。Bot 还会向 Agent 暴露 sendMessage 工具动作(to、message、media 参数,见 zalo/src/actions.ts),供模型在授权范围内主动发送 Zalo 消息。
故障排查
Bot 不回复:
- 校验令牌有效性:
openclaw channels status --probe(探测逻辑调用getMe,见 zalo/src/probe.ts); - 确认发送者已批准(配对或
allowFrom白名单); - 查看 Gateway 日志:
openclaw logs --follow。
Webhook 收不到事件:
- 确认 Webhook URL 为 HTTPS;
- 确认 secret 长度在 8~256 字符之间;
- 确认 Gateway HTTP 端点在配置的
webhookPath可达; - 确认没有同时运行
getUpdates轮询(两者互斥)。
启动异常排查: 观察启动日志中 mode=polling|webhook、mediaMaxMb 与探测耗时信息(zalo/src/channel.runtime.ts),探测失败会在 provider 启动前告警,但不会阻断启动。
总结
Zalo 插件为 OpenClaw 提供了完整的越南市场消息接入能力:安装可通过本地路径、npm 或 onboarding 三种方式;令牌支持环境变量、配置与文件三种来源并可按账户隔离;默认长轮询、可选 HTTPS Webhook,并内置常量时间比较、限流、去重与异常追踪等安全机制;DM 默认配对、群组默认 allowlist 的安全默认设计保证了接入即受控。按照本文的配置片段与 CLI 命令即可完成接入,后续可结合 openclaw channels status 与日志持续观测运行状态。