OpenClaw Zalo 渠道插件接入指南:Bot API 配置、长轮询/Webhook 双模式与访问控制实战
OpenClaw Zalo 渠道插件接入指南:Bot API 配置、长轮询/Webhook 双模式与访问控制实战
Zalo 是越南地区最流行的即时通讯平台之一,其官方 Bot API 允许开发者以机器人身份在私聊与群聊中收发消息。在 OpenClaw 中,Zalo 以一个独立渠道插件(@openclaw/zalo)的形式接入,让 Agent 能够稳定地把回复路由回 Zalo 会话。本篇指南以 extensions/zalo/README.md 为核心骨架,结合该插件的完整源码实现,系统讲解插件的安装方式、完整配置参数、Token 解析机制、长轮询与 Webhook 两种消息接收模式、私聊/群组访问控制策略以及多账户支持,读完即可独立完成 Zalo Bot 从零接入 OpenClaw 的整个流程,并理解底层工作原理。
插件概览:Zalo 渠道在 OpenClaw 中的定位
Zalo 渠道插件是一个典型的"渠道插件"(Channel Plugin),其定位信息集中声明在 package.json 与 openclaw.plugin.json 中:
- 插件 ID:
zalo,渠道名zalo,别名zl; - 展示标签:
Zalo (Bot API),官方描述为"面向越南市场的消息平台,提供 Bot API"; - 依赖
undici作为 HTTP 客户端,运行时仅需 OpenClaw 宿主(peerDependencies.openclaw >= 2026.3.28)。
从插件注册入口 index.ts 与渠道主实现 src/channel.ts 可以看到,Zalo 渠道声明的能力如下:
- 会话类型:私聊(direct)与群聊(group)均支持;
- 媒体:支持图片收发;
- 不支持:Reactions(表情回应)、Threads(子线程)、Polls(投票)、原生命令(nativeCommands);
- 流式输出(blockStreaming):声明支持,但由于 Zalo 单条消息 2000 字符的 API 限制,流式分块价值有限,官方文档标记为"默认阻断"。
消息路由是确定性的:入站消息被归一化进共享渠道信封(envelope),回复始终回到同一个 Zalo 会话,模型不会自行选择渠道。这种"确定性路由"特性使 Zalo 非常适合客服、通知推送等需要稳定回路的场景。
安装:本地检出、npm 与 Onboarding 三种方式
Zalo 插件不随核心安装包内置,需要单独安装。安装方式有三种:
1. 从源码检出(本地 checkout)安装
在 OpenClaw 源码仓库的根目录执行:
openclaw plugins install ./extensions/zalo
2. 从 npm 安装
openclaw plugins install @openclaw/zalo
package.json 中的 openclaw.install 段声明了 npmSpec: "@openclaw/zalo"、localPath: "extensions/zalo",且 defaultChoice: "npm",即默认推荐走 npm 安装;同时要求宿主版本 minHostVersion >= 2026.3.28。
3. 通过 Onboarding 引导安装
在 OpenClaw 初始化引导(onboarding)过程中直接选择 Zalo,并确认安装提示,系统会自动拉取该插件。
安装完成后,重启网关(gateway)使配置生效。插件的能力入口(openclaw 的 extensions 声明指向 index.ts),而设置向导入口指向 setup-entry.ts,二者共同构成插件完整的运行时与配置界面。
最小配置:一条 Bot 消息跑通全流程
在 OpenClaw 配置文件中(openclaw.json5 或等价配置位置),为 channels.zalo 提供如下最小配置即可启动:
{
channels: {
zalo: {
enabled: true,
botToken: "12345689:abc-xyz",
dmPolicy: "pairing",
proxy: "http://proxy.local:8080",
},
},
}
字段说明:
enabled:是否启动该 Zalo 渠道(默认true);botToken:在 Zalo Bot Platform 创建机器人后获得的 Bot Token,格式形如12345689:abc-xyz;dmPolicy:私聊访问策略,默认pairing(配对模式),详见下文"访问控制";proxy:可选,代理 URL,用于 API 请求(如公司内网环境)。
Zalo 启动条件是"能解析到 Token"(来自环境变量或配置均可)。最小配置下,一旦 Bot Token 就绪,网关重启后渠道即自动运行。
配置参数全解:从 Schema 到运行时
插件的配置校验基于 src/config-schema.ts 中的 ZaloConfigSchema,运行时读取结构定义在 src/types.ts。完整参数如下:
| 参数 | 类型/取值 | 默认值 | 说明 |
|---|---|---|---|
enabled |
boolean | true |
是否启动该 Zalo 账户 |
name |
string | — | 账户显示名(CLI/UI 列表用) |
botToken |
SecretInput | — | Zalo Bot Creator 签发的 Bot Token,支持 SecretRef 引用 |
tokenFile |
string | — | 存放 Bot Token 的文件路径 |
webhookUrl |
string | — | Webhook 接收 URL,必须 HTTPS |
webhookSecret |
SecretInput | — | Webhook 校验密钥,8–256 字符,支持 SecretRef |
webhookPath |
string | — | 网关 HTTP 服务器上的回调路径,省略时取 Webhook URL 的路径 |
dmPolicy |
pairing / allowlist / open / disabled |
pairing |
私聊访问策略 |
allowFrom |
string/number 数组 | — | 私聊发送者白名单(Zalo 用户 ID) |
groupPolicy |
open / allowlist / disabled |
allowlist |
群聊入站策略 |
groupAllowFrom |
string/number 数组 | — | 群聊发送者白名单,未设置时回退到 allowFrom |
mediaMaxMb |
number | 5 |
入站/出站媒体大小上限(MB) |
proxy |
string | — | API 请求代理 URL |
responsePrefix |
string | — | 该渠道/账户的出站回复前缀覆盖 |
关于 dmPolicy: "open" 的注意点
官方渠道文档 docs/channels/zalo.md 特别强调:若将 dmPolicy 设为 open,allowFrom 必须显式填写 "*" 才生效。同时,插件内置的状态检查器 src/status-issues.ts 会对 dmPolicy: "open" 产生告警项,建议改用 pairing 或 allowlist 以限制访问——这也是"安全默认"设计的一部分。
完整的多参数配置示例
{
channels: {
zalo: {
enabled: true,
botToken: "12345689:abc-xyz",
dmPolicy: "allowlist",
allowFrom: [123456789],
groupPolicy: "allowlist",
groupAllowFrom: [123456789],
mediaMaxMb: 10,
proxy: "http://proxy.local:8080",
},
},
}
Token 解析机制:环境变量、配置与文件三种来源
botToken 并非只能硬编码在配置里。插件在 src/token.ts 中实现了完整的 Token 解析链路,解析优先级为:
- 账户级配置 Token:
channels.zalo.accounts.<id>.botToken(多账户时); - 账户级 Token 文件:
channels.zalo.accounts.<id>.tokenFile; - 顶层配置 Token:
channels.zalo.botToken; - 顶层 Token 文件:
channels.zalo.tokenFile; - 环境变量:
ZALO_BOT_TOKEN(仅默认账户可用,多账户场景下会给出明确报错"ZALO_BOT_TOKEN can only be used for the default account",见 src/setup-core.ts)。
若以上均未命中,Token 解析结果为 source: "none",账户被视为未配置。解析结果(token 与 tokenSource)会被注入状态摘要,方便诊断 Token 来自哪里。
值得注意的是,botToken 与 webhookSecret 均支持 SecretRef(如 { source: "env", provider: "default", id: "ZALO_BOT_TOKEN" }),这一能力由 src/config-schema.test.ts 的测试用例验证,且支持在顶层与账户级同时使用——适合将密钥托管到密钥管理系统,而不是明文写入配置。
消息接收模式(一):长轮询(默认)
Zalo 渠道默认使用**长轮询(long-polling)**接收消息,无需公网 URL。其实现位于 src/monitor.ts:
- 轮询循环调用
getUpdates(token, { timeout: 30 }),每次调用返回单个更新事件(注意:与 Telegram 返回数组不同,Zalo 每次只返回一条 update,见 src/api.ts 中getUpdates的注释); - 轮询超时(Zalo API 返回 408)被视为"暂无新消息"的正常情况,不产生错误日志;
- 其他异常会记录错误并延迟 5 秒后继续轮询;
- 进入轮询模式前,插件会先尝试清理已配置的 Webhook(调用
getWebhookInfo/deleteWebhook),避免与 Webhook 模式冲突;若当前环境不支持 Webhook 查询(部分轮询专用 Bot),则跳过清理并继续。
支持的事件类型(event_name)包括:message.text.received(文本)、message.image.received(图片)、message.sticker.received(贴纸,仅记录)、message.unsupported.received(不支持类型,仅记录)。
消息接收模式(二):Webhook(推荐生产环境)
当需要更低延迟或不想依赖轮询时,可切换到 Webhook 模式:
{
channels: {
zalo: {
webhookUrl: "https://example.com/zalo-webhook",
webhookSecret: "your-secret-8-plus-chars",
webhookPath: "/zalo-webhook",
},
},
}
配置约束与校验
插件启动时(见 src/monitor.ts 的 monitorZaloProvider)会对 Webhook 配置做硬校验:
webhookUrl与webhookSecret二者必须同时提供,否则报错"Zalo webhookUrl and webhookSecret are required for webhook mode";webhookUrl必须以https://开头,否则报错;webhookSecret长度必须在 8–256 字符之间;webhookPath可省略——省略时插件会自动取 Webhook URL 的路径部分(resolveWebhookPath),例如 URL 为https://example.com/zalo-webhook则路径为/zalo-webhook。
Webhook 请求校验与安全防护
Webhook 入口实现在 src/monitor.webhook.ts,安全机制相当完备:
- 密钥校验:Zalo 通过
X-Bot-Api-Secret-Token请求头携带密钥,插件使用timingSafeEqual进行恒定时间比较,防止时序侧信道攻击; - 方法限制:仅接受
POST; - 内容类型:要求
Content-Type: application/json(或+json媒体类型); - 限流:按
path:clientIp维度做固定窗口限流,突发流量可能返回 HTTP 429; - 防重放:以
event_name + message_id为键做 5 分钟去重缓存,重复事件直接返回200 ok忽略; - 异常追踪:内置 Webhook 异常计数器(
webhookAnomalyTracker),异常状态码会记录日志; - 请求体上限:1MB,超限拒绝;
- 客户端 IP 解析:支持
X-Forwarded-For/X-Real-IP,并遵循网关trustedProxies配置。
启动 Webhook 模式时,插件会调用 setWebhook 将回调 URL 与密钥注册到 Zalo Bot API;网关停止时会调用 deleteWebhook 清理(带 5 秒超时保护)。轮询与 Webhook 两种模式在 Zalo API 侧是互斥的,不要同时启用。
访问控制:私聊配对、白名单与群组策略
私聊(DM)策略
channels.zalo.dmPolicy 支持四个取值,默认 pairing:
| 取值 | 行为 |
|---|---|
pairing |
未知发送者会收到一条含配对码的回复,消息被忽略直至批准;配对码约 1 小时后过期 |
allowlist |
仅 allowFrom 中的用户 ID 可触发 Bot |
open |
任何用户可私聊(需 allowFrom: ["*"],并会产生安全告警) |
disabled |
关闭所有私聊入站 |
配对流程的底层实现在 src/monitor.ts 的 authorizeZaloMessage 中:当发送者未授权且策略为 pairing 时,通过 createChannelPairingController 发起配对挑战,向用户回复包含其 Zalo 用户 ID 的配对说明。批准操作通过 CLI 完成:
openclaw pairing list zalo
openclaw pairing approve zalo <CODE>
配对成功后,该用户 ID 会被加入授权存储,后续消息正常进入 Agent 处理管线。
群组(Group)策略
channels.zalo.groupPolicy 支持 open / allowlist / disabled,默认 allowlist(fail-closed):
allowlist:仅groupAllowFrom中的发送者可触发 Bot;groupAllowFrom未设置时自动回退使用allowFrom;open:任何群成员可触发,但受提及(mention)门控约束(群聊中需 @Bot);disabled:忽略所有群消息。
groupPolicy: "open" 且未配置任何白名单时,插件会通过 createOpenProviderGroupPolicyWarningCollector 产生安全告警(见 src/channel.ts),提示设置 channels.zalo.groupPolicy="allowlist" 并配合 groupAllowFrom。
发送者 ID 的归一化
由于 Zalo 用户 ID 为纯数字,allowFrom / groupAllowFrom 接受 string | number 混合数组;插件在匹配前会剥离可选的 zalo: / zl: 前缀(见 src/group-access.ts),因此配置 "123456789" 与 123456789 等价。
多账户支持:一个网关挂多个 Zalo Bot
Zalo 插件支持多账户(multi-account)。账户级配置放在 channels.zalo.accounts.<id> 下:
{
channels: {
zalo: {
enabled: true,
defaultAccount: "work",
accounts: {
work: {
name: "Work Bot",
botToken: "111111:aaa",
dmPolicy: "allowlist",
allowFrom: [123456789],
},
support: {
name: "Support Bot",
botToken: "222222:bbb",
webhookUrl: "https://example.com/support-webhook",
webhookSecret: "another-secret-8-plus",
webhookPath: "/support-webhook",
},
},
},
},
}
顶层字段作为默认值/默认账户配置,defaultAccount 指定默认账户 ID。账户解析逻辑在 src/accounts.ts 中通过 resolveMergedAccountConfig 实现:账户级配置会与顶层配置合并(账户级优先),并剔除 defaultAccount 等仅顶层有效的字段。每个账户可以独立配置 Token、DM/群组策略、Webhook 甚至代理,账户启停通过 enabled 控制。配置向导(src/setup-surface.ts)也完整支持多账户场景。
消息类型与能力边界
| 消息类型 | 处理方式 |
|---|---|
| 文本消息 | 完整支持,出站按 2000 字符分块发送(Zalo API 限制) |
| 图片消息 | 入站下载并进入媒体处理管线(受 mediaMaxMb 限制,默认 5MB);出站经 sendPhoto 发送 |
| 贴纸(sticker) | 仅记录日志,不触发 Agent 回复 |
| 不支持类型 | 仅记录日志(如受保护用户的消息) |
出站分块逻辑位于 src/send.ts:文本截断到 2000 字符,图片 caption 同样截断;长文本回复通过 chunkTextForOutbound 按 Markdown 模式分块投递。流式输出因 2000 字符限制价值有限,官方文档标记为"默认阻断"。
外发消息:CLI 直发与模型工具调用
CLI 定向发送
Zalo 渠道支持确定性定向投递,可直接向指定聊天 ID 发送消息:
openclaw message send --channel zalo --target 123456789 --message "hi"
目标解析(src/session-route.ts)支持 zalo:<id> / zl:<id> 前缀(可省略)以及 group:<id> 群聊前缀;channel.ts 中的 normalizeZaloMessagingTarget 会在匹配前剥离 zalo: / zl: 前缀。
模型侧消息动作
渠道注册了 sendMessage 动作(见 src/actions.ts),Agent 可通过工具调用主动向 Zalo 用户发送文本或图片,参数为 to(目标)、message(文本)、media(媒体 URL)。CLI/cron 场景下同样复用 src/send.ts 的 sendMessageZalo 实现,并支持按账户(accountId)选择发送身份。
网络代理支持
当运行环境无法直连 Zalo API 时,通过 proxy 参数指定代理 URL(支持顶层与账户级)。底层实现在 src/proxy.ts:使用 undici 的 ProxyAgent 创建带代理的 fetch 封装,并按 URL 缓存复用代理实例,避免重复创建连接。
排障速查
Bot 不回复:
- 校验 Token 是否有效:
openclaw channels status --probe(探针实现见 src/probe.ts,调用getMe校验并返回 Bot 信息与耗时); - 确认发送者已获授权(配对已批准或已加入
allowFrom); - 查看网关日志:
openclaw logs --follow。
Webhook 收不到事件:
- 确认
webhookUrl为 HTTPS; - 确认
webhookSecret长度在 8–256 字符之间; - 确认网关 HTTP 服务在
webhookPath上可达(被反向代理/防火墙拦截时注意路径透传); - 确认长轮询没有同时运行(两者在 Zalo API 侧互斥,轮询模式启动时会自动清理 Webhook)。
dmPolicy: "open" 产生告警: 这是插件内置的主动安全提示(src/status-issues.ts),建议改为 pairing 或 allowlist。
源码结构与测试验证
插件源码结构清晰,便于深入阅读:
extensions/zalo/
├── index.ts # 插件注册入口(defineChannelPluginEntry)
├── setup-entry.ts # 设置向导入口
├── openclaw.plugin.json # 插件清单(id、渠道、configSchema)
├── package.json # npm 包声明(@openclaw/zalo)
└── src/
├── channel.ts # 渠道主装配(能力、安全、配对、出站)
├── channel.runtime.ts # 网关账户启动、配对批准通知、探针
├── monitor.ts # 长轮询/Webhook 主循环与消息管线
├── monitor.webhook.ts # Webhook 请求校验、限流、防重放
├── api.ts # Zalo Bot API 客户端(getMe/sendMessage/sendPhoto/setWebhook...)
├── send.ts # 出站发送(分块、媒体、代理)
├── token.ts # Token 多来源解析
├── accounts.ts # 多账户解析与合并
├── config-schema.ts # zod 配置校验
├── group-access.ts # 群组访问判定
├── session-route.ts # 外发会话路由
├── proxy.ts # undici 代理封装
├── probe.ts # Token 探针(getMe)
├── status-issues.ts # 状态告警收集
├── setup-core.ts # 安装参数校验
└── setup-surface.ts # 交互式配置向导
配套测试覆盖了 Webhook 密钥校验、限流与防重放(src/monitor.webhook.test.ts)、配对生命周期(src/monitor.pairing.lifecycle.test.ts)、配置 Schema 的 SecretRef 兼容(src/config-schema.test.ts)等关键路径,是理解插件行为边界的绝佳参考。
小结
从安装、最小配置到长轮询/Webhook 双模式、私聊配对与群组策略、多账户与出站发送,@openclaw/zalo 提供了一条完整且安全默认的 Zalo Bot 接入路径。核心要点回顾:默认长轮询免公网、Webhook 模式需 HTTPS + 8–256 位密钥;DM 默认 pairing 配对、群组默认 allowlist fail-closed;文本出站 2000 字符分块、媒体默认 5MB 上限;Token 支持环境变量、配置、文件与 SecretRef 四种来源。按 docs/channels/zalo.md 的快速配置路径操作,从创建 Bot 到首次回复通常只需要几分钟。