OpenClaw Zalo 渠道插件接入指南:Bot API 配置、长轮询/Webhook 双模式与访问控制实战

原创2026-10-15 13:19:541,713 阅读
文章标签:人工智能大模型AI 应用后端LLM 网关即时通讯AI Agent插件系统本地部署MCP Clients

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 解析链路,解析优先级为:

  1. 账户级配置 Token:channels.zalo.accounts.<id>.botToken(多账户时);
  2. 账户级 Token 文件:channels.zalo.accounts.<id>.tokenFile;
  3. 顶层配置 Token:channels.zalo.botToken;
  4. 顶层 Token 文件:channels.zalo.tokenFile;
  5. 环境变量: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 不回复:

  1. 校验 Token 是否有效:openclaw channels status --probe(探针实现见 src/probe.ts,调用 getMe 校验并返回 Bot 信息与耗时);
  2. 确认发送者已获授权(配对已批准或已加入 allowFrom);
  3. 查看网关日志:openclaw logs --follow。

Webhook 收不到事件:

  1. 确认 webhookUrl 为 HTTPS;
  2. 确认 webhookSecret 长度在 8–256 字符之间;
  3. 确认网关 HTTP 服务在 webhookPath 上可达(被反向代理/防火墙拦截时注意路径透传);
  4. 确认长轮询没有同时运行(两者在 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 到首次回复通常只需要几分钟。

登录后查看全文
openclaw-zero-token