消息平台
Status: 可通过 WhatsApp Web(Baileys)投入生产使用。Gateway 网关拥有已关联会话。
安装(按需)
- 新手引导(
openclaw onboard)和openclaw channels add --channel whatsapp会在你第一次选择 WhatsApp 插件时提示安装它。 - 当插件尚未存在时,
openclaw channels login --channel whatsapp也会提供安装流程。 - Dev 渠道 + git checkout:默认使用本地插件路径。
- Stable/Beta:在当前官方发布标签上使用 npm 包
@openclaw/whatsapp。
仍可手动安装:
openclaw plugins install @openclaw/whatsapp
使用裸包名会跟随当前官方发布标签。只有在需要可复现安装时,才固定精确版本。
在 Windows 上,WhatsApp 插件在 npm install 期间需要 PATH 中有 Git,因为它的一个 Baileys/libsignal 依赖是从 git URL 获取的。安装 Git for Windows,然后重启 shell 并重新运行安装:
winget install --id Git.Git -e
如果 Portable Git 的 bin 目录在 PATH 中,也可以使用。
快速设置
Configure WhatsApp access policy
{
channels: {
whatsapp: {
dmPolicy: "pairing",
allowFrom: ["+15551234567"],
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
},
}
Link WhatsApp (QR)
openclaw channels login --channel whatsapp
对于特定账号:
openclaw channels login --channel whatsapp --account work
要在登录前附加现有/自定义 WhatsApp Web 认证目录:
openclaw channels add --channel whatsapp --account work --auth-dir /path/to/wa-auth
openclaw channels login --channel whatsapp --account work
Start the gateway
openclaw gateway
Approve first pairing request (if using pairing mode)
openclaw pairing list whatsapp
openclaw pairing approve whatsapp <CODE>
配对请求会在 1 小时后过期。每个渠道的待处理请求最多为 3 个。
部署模式
Dedicated number (recommended)
这是最清晰的运维模式:
- 为 OpenClaw 使用单独的 WhatsApp 身份
- 更清晰的私信允许列表和路由边界
- 降低自聊混淆的可能性
最小策略模式:
{
channels: {
whatsapp: {
dmPolicy: "allowlist",
allowFrom: ["+15551234567"],
},
},
}
Personal-number fallback
新手引导支持个人号码模式,并会写入适合自聊的基线配置:
dmPolicy: "allowlist"allowFrom包含你的个人号码selfChatMode: true
在运行时,自聊保护会根据已关联的自身号码和 allowFrom 生效。
WhatsApp Web-only channel scope
在当前 OpenClaw 渠道架构中,消息平台渠道基于 WhatsApp Web(Baileys)。
内置聊天渠道注册表中没有单独的 Twilio WhatsApp 消息渠道。
运行时模型
- Gateway 网关拥有 WhatsApp socket 和重连循环。
- 重连看门狗使用 WhatsApp Web 传输活动,而不只是入站应用消息量,因此安静的已关联设备会话不会仅仅因为最近没人发送消息而被重启。如果传输帧持续到达,但在看门狗窗口内没有处理任何应用消息,更长的应用静默上限仍会强制重连;对于最近活跃会话的临时重连,在第一个恢复窗口内,该应用静默检查会使用正常消息超时。
- Baileys socket 时序在
web.whatsapp.*下显式配置:keepAliveIntervalMs控制 WhatsApp Web 应用 ping,connectTimeoutMs控制打开握手超时,defaultQueryTimeoutMs控制 Baileys 查询超时。 - 出站发送要求目标账号有活跃的 WhatsApp 监听器。
- 当文本和媒体标题中的
@+<digits>与@<digits>令牌匹配当前 WhatsApp 参与者元数据(包括 LID 支持的群组)时,群组发送会附加原生 mention 元数据。 - Status 和广播聊天会被忽略(
@status、@broadcast)。 - 重连看门狗遵循 WhatsApp Web 传输活动,而不只是入站应用消息量:只要传输帧持续,安静的已关联设备会话就会保持在线,但传输停滞会在后续远端断开路径之前强制重连。
- 直接聊天使用私信会话规则(
session.dmScope;默认main会将私信折叠到智能体主会话)。 - 群组会话相互隔离(
agent:<agentId>:whatsapp:group:<jid>)。 - WhatsApp Channels/Newsletters 可以用其原生
@newsletterJID 作为显式出站目标。出站 newsletter 发送使用渠道会话元数据(agent:<agentId>:whatsapp:channel:<jid>),而不是私信会话语义。 - WhatsApp Web 传输会遵循 Gateway 网关主机上的标准代理环境变量(
HTTPS_PROXY、HTTP_PROXY、NO_PROXY/ 小写变体)。优先使用主机级代理配置,而不是 WhatsApp 渠道专用代理设置。 - 启用
messages.removeAckAfterReply时,OpenClaw 会在可见回复送达后清除 WhatsApp ack 反应。
插件钩子和隐私
WhatsApp 入站消息可能包含个人消息内容、电话号码、群组标识符、发送者姓名和会话关联字段。因此,除非你显式选择加入,否则 WhatsApp 不会向插件广播入站 message_received 钩子载荷:
{
channels: {
whatsapp: {
pluginHooks: {
messageReceived: true,
},
},
},
}
你可以将选择加入限定到一个账号:
{
channels: {
whatsapp: {
accounts: {
work: {
pluginHooks: {
messageReceived: true,
},
},
},
},
},
}
仅对你信任其接收 WhatsApp 入站消息内容和标识符的插件启用此功能。
访问控制和激活
DM policy
channels.whatsapp.dmPolicy 控制直接聊天访问:
pairing(默认)allowlistopen(要求allowFrom包含"*")disabled
allowFrom 接受 E.164 风格号码(内部会规范化)。
allowFrom 是私信发送者访问控制列表。它不会限制显式出站发送到 WhatsApp 群组 JID 或 @newsletter 渠道 JID。
多账号覆盖:channels.whatsapp.accounts.<id>.dmPolicy(以及 allowFrom)会优先于该账号的渠道级默认值。
运行时行为详情:
- 配对会持久化到渠道允许存储,并与配置的
allowFrom合并 - 定时自动化和 Heartbeat 接收者回退会使用显式投递目标或配置的
allowFrom;私信配对批准不会隐式成为 cron 或 Heartbeat 接收者 - 如果未配置允许列表,则默认允许已关联的自身号码
- OpenClaw 永远不会自动配对出站
fromMe私信(你从已关联设备发给自己的消息)
Group policy + allowlists
群组访问有两层:
-
群组成员允许列表(
channels.whatsapp.groups)- 如果省略
groups,所有群组都符合条件 - 如果存在
groups,它会作为群组允许列表(允许"*")
- 如果省略
-
群组发送者策略(
channels.whatsapp.groupPolicy+groupAllowFrom)open:绕过发送者允许列表allowlist:发送者必须匹配groupAllowFrom(或*)disabled:阻止所有群组入站
发送者允许列表回退:
- 如果未设置
groupAllowFrom,运行时会在可用时回退到allowFrom - 发送者允许列表会在 mention/回复激活之前评估
注意:如果完全不存在 channels.whatsapp 块,即使设置了 channels.defaults.groupPolicy,运行时群组策略回退也会是 allowlist(并记录警告日志)。
Mentions + /activation
群组回复默认需要 mention。
mention 检测包括:
- 对 bot 身份的显式 WhatsApp mention
- 配置的 mention 正则模式(
agents.list[].groupChat.mentionPatterns,回退到messages.groupChat.mentionPatterns) - 已授权群组消息的入站语音便笺转录
- 隐式 reply-to-bot 检测(回复发送者匹配 bot 身份)
安全注意事项:
- 引用/回复只满足 mention 门控;它不会授予发送者授权
- 使用
groupPolicy: "allowlist"时,即使非允许列表发送者回复允许列表用户的消息,仍会被阻止
会话级激活命令:
/activation mention/activation always
activation 会更新会话状态(而不是全局配置)。它受所有者门控。
个人号码和自聊行为
当已关联的自身号码也存在于 allowFrom 中时,WhatsApp 自聊保护会激活:
- 跳过自聊轮次的已读回执
- 忽略否则会 ping 你自己的 mention-JID 自动触发行为
- 如果未设置
messages.responsePrefix,自聊回复默认使用[{identity.name}]或[openclaw]
消息规范化和上下文
Inbound envelope + reply context
传入的 WhatsApp 消息会包装在共享入站信封中。
如果存在引用回复,上下文会按此形式追加:
[Replying to <sender> id:<stanzaId>]
<quoted body or media placeholder>
[/Replying]
可用时也会填充回复元数据字段(ReplyToId、ReplyToBody、ReplyToSender、发送者 JID/E.164)。
当引用回复目标是可下载媒体时,OpenClaw 会通过正常的入站媒体存储保存它,并将其暴露为 MediaPath/MediaType,以便智能体检查被引用的图片,而不是只能看到 <media:image>。
Media placeholders and location/contact extraction
仅媒体入站消息会使用如下占位符规范化:
<media:image><media:video><media:audio><media:document><media:sticker>
当正文只有 <media:audio> 时,已授权群组语音便笺会在 mention 门控前转录,因此在语音便笺中说出 bot mention 可以触发回复。如果转录仍未 mention bot,则转录会保留在待处理群组历史中,而不是保留原始占位符。
位置正文使用简短的坐标文本。位置标签/评论和联系人/vCard 详情会呈现为围栏式不可信元数据,而不是内联提示文本。
Pending group history injection
对于群组,未处理消息可以被缓冲,并在 bot 最终被触发时作为上下文注入。
- 默认限制:
50 - 配置:
channels.whatsapp.historyLimit - 回退:
messages.groupChat.historyLimit 0禁用
注入标记:
[Chat messages since your last reply - for context][Current message - respond to this]
已读回执
对于已接受的 WhatsApp 入站消息,默认启用已读回执。
全局禁用:
{
channels: {
whatsapp: {
sendReadReceipts: false,
},
},
}
按账号覆盖:
{
channels: {
whatsapp: {
accounts: {
work: {
sendReadReceipts: false,
},
},
},
},
}
即使全局启用,自聊轮次也会跳过已读回执。
送达、分块和媒体
文本分块
- 默认分块限制:
channels.whatsapp.textChunkLimit = 4000 channels.whatsapp.chunkMode = "length" | "newline"newline模式优先使用段落边界(空行),然后回退到长度安全的分块
出站媒体行为
- 支持图片、视频、音频(PTT 语音便笺)和文档载荷
- 音频媒体通过 Baileys
audio载荷并带有ptt: true发送,因此 WhatsApp 客户端会将其渲染为一条按住说话语音便笺 - 回复载荷会保留
audioAsVoice;即使提供商返回 MP3 或 WebM,WhatsApp 的 TTS 语音便笺输出也会保留在此 PTT 路径上 - 原生 Ogg/Opus 音频会以
audio/ogg; codecs=opus发送,以兼容语音便笺 - 非 Ogg 音频(包括 Microsoft Edge TTS MP3/WebM 输出)会在 PTT 送达前使用
ffmpeg转码为 48 kHz 单声道 Ogg/Opus /tts latest会将最新的助手回复作为一条语音便笺发送,并抑制同一回复的重复发送;/tts chat on|off|default控制当前 WhatsApp 聊天的自动 TTS- 通过视频发送上的
gifPlayback: true支持动画 GIF 播放 - 发送多媒体回复载荷时,标题会应用到第一个媒体项;但 PTT 语音便笺会先发送音频并单独发送可见文本,因为 WhatsApp 客户端无法一致地渲染语音便笺标题
- 媒体来源可以是 HTTP(S)、
file://或本地路径
媒体大小限制和回退行为
- 入站媒体保存上限:
channels.whatsapp.mediaMaxMb(默认50) - 出站媒体发送上限:
channels.whatsapp.mediaMaxMb(默认50) - 按账号覆盖使用
channels.whatsapp.accounts.<accountId>.mediaMaxMb - 图片会自动优化(调整大小/质量扫描)以符合限制
- 媒体发送失败时,第一项回退会发送文本警告,而不是静默丢弃响应
回复引用
WhatsApp 支持原生回复引用,出站回复会明显引用入站消息。使用 channels.whatsapp.replyToMode 控制它。
| 值 | 行为 |
|---|---|
"off" |
从不引用;作为普通消息发送 |
"first" |
仅引用第一个出站回复分块 |
"all" |
引用每个出站回复分块 |
"batched" |
引用已排队的批量回复,同时让即时回复不带引用 |
默认值是 "off"。按账号覆盖使用 channels.whatsapp.accounts.<id>.replyToMode。
{
channels: {
whatsapp: {
replyToMode: "first",
},
},
}
反应级别
channels.whatsapp.reactionLevel 控制智能体在 WhatsApp 上使用表情反应的范围:
| 级别 | 确认反应 | 智能体发起的反应 | 描述 |
|---|---|---|---|
"off" |
否 | 否 | 完全不使用反应 |
"ack" |
是 | 否 | 仅确认反应(回复前回执) |
"minimal" |
是 | 是(保守) | 确认 + 带保守指导的智能体反应 |
"extensive" |
是 | 是(鼓励) | 确认 + 带鼓励指导的智能体反应 |
默认值:"minimal"。
按账号覆盖使用 channels.whatsapp.accounts.<id>.reactionLevel。
{
channels: {
whatsapp: {
reactionLevel: "ack",
},
},
}
确认反应
WhatsApp 支持通过 channels.whatsapp.ackReaction 在入站回执时立即发送确认反应。
确认反应受 reactionLevel 限制:当 reactionLevel 为 "off" 时会被抑制。
{
channels: {
whatsapp: {
ackReaction: {
emoji: "👀",
direct: true,
group: "mentions", // always | mentions | never
},
},
},
}
行为说明:
- 在入站被接受后立即发送(回复前)
- 失败会被记录,但不会阻止正常回复送达
- 群组模式
mentions会在提及触发的轮次上发送反应;群组激活always会作为绕过此检查的方式 - WhatsApp 使用
channels.whatsapp.ackReaction(这里不使用旧版messages.ackReaction)
多账号和凭证
账号选择和默认值
- 账号 ID 来自
channels.whatsapp.accounts - 默认账号选择:如果存在则使用
default,否则使用第一个已配置的账号 ID(排序后) - 账号 ID 会在内部规范化用于查找
凭证路径和旧版兼容性
- 当前认证路径:
~/.openclaw/credentials/whatsapp/<accountId>/creds.json - 备份文件:
creds.json.bak - 仍会识别/迁移
~/.openclaw/credentials/中的旧版默认认证,用于默认账号流程
登出行为
openclaw channels logout --channel whatsapp [--account <id>] 会清除该账号的 WhatsApp 认证状态。
当 Gateway 网关可达时,登出会先停止所选账号的实时 WhatsApp 监听器,这样已关联的会话在下次重启前不会继续接收消息。openclaw channels remove --channel whatsapp 也会在禁用或删除账号配置前停止实时监听器。
在旧版认证目录中,会保留 oauth.json,同时移除 Baileys 认证文件。
工具、操作和配置写入
- 智能体工具支持包括 WhatsApp 反应操作(
react)。 - 操作门控:
channels.whatsapp.actions.reactionschannels.whatsapp.actions.polls
- 默认启用渠道发起的配置写入(通过
channels.whatsapp.configWrites=false禁用)。
故障排除
未关联(需要 QR)
症状:渠道状态报告未关联。
修复:
openclaw channels login --channel whatsapp
openclaw channels status
已关联但断开连接/重连循环
症状:已关联账号反复断开连接或尝试重连。
安静账号可以在正常消息超时后保持连接;当 WhatsApp Web 传输活动停止、套接字关闭,或应用级活动在更长的安全窗口内保持静默时,watchdog 会重启。
如果日志显示反复出现 status=408 Request Time-out Connection was lost,请调整 web.whatsapp 下的 Baileys 套接字时序。先将 keepAliveIntervalMs 缩短到低于你的网络空闲超时,并在慢速或有丢包的链路上增加 connectTimeoutMs:
{
web: {
whatsapp: {
keepAliveIntervalMs: 15000,
connectTimeoutMs: 60000,
defaultQueryTimeoutMs: 60000,
},
},
}
修复:
openclaw doctor
openclaw logs --follow
如果 ~/.openclaw/logs/whatsapp-health.log 显示 Gateway inactive,但 openclaw gateway status 和 openclaw channels status --probe 显示 Gateway 网关和 WhatsApp 都健康,请运行 openclaw doctor。在 Linux 上,Doctor 会警告仍调用 ~/.openclaw/bin/ensure-whatsapp.sh 的旧版 crontab 条目;请使用 crontab -e 移除这些过时条目,因为 cron 可能缺少 systemd 用户总线环境,导致旧脚本误报 Gateway 网关健康状态。
如有需要,请使用 channels login 重新关联。
代理后面的 QR 登录超时
症状:openclaw channels login --channel whatsapp 在显示可用 QR 码前失败,并出现 status=408 Request Time-out 或 TLS 套接字断开连接。
WhatsApp Web 登录使用 Gateway 网关主机的标准代理环境(HTTPS_PROXY、HTTP_PROXY、小写变体和 NO_PROXY)。验证 Gateway 网关进程继承了代理环境变量,并且 NO_PROXY 不匹配 mmg.whatsapp.net。
发送时没有活动监听器
当目标账号没有活动 Gateway 网关监听器时,出站发送会快速失败。
确保 Gateway 网关正在运行,并且账号已关联。
回复出现在转录中但不在 WhatsApp 中
转录行记录智能体生成的内容。WhatsApp 送达会单独检查:只有在 Baileys 针对至少一次可见文本或媒体发送返回出站消息 ID 后,OpenClaw 才会将自动回复视为已发送。
确认反应是独立的回复前回执。成功的反应并不能证明后续文本或媒体回复已被 WhatsApp 接受。
检查 Gateway 网关日志中的 auto-reply delivery failed 或 auto-reply was not accepted by WhatsApp provider。
群组消息被意外忽略
按以下顺序检查:
groupPolicygroupAllowFrom/allowFromgroups允许列表条目- 提及门控(
requireMention+ 提及模式) openclaw.json中的重复键(JSON5):后面的条目会覆盖前面的条目,因此每个作用域只保留一个groupPolicy
Bun 运行时警告
WhatsApp Gateway 网关运行时应使用 Node。Bun 被标记为不兼容稳定的 WhatsApp/Telegram Gateway 网关运行。
系统提示
WhatsApp 通过 groups 和 direct 映射支持适用于群组和直接聊天的 Telegram 风格系统提示。
群组消息的解析层级:
首先确定有效的 groups 映射:如果账号定义了自己的 groups,它会完全替换根 groups 映射(不进行深度合并)。然后在生成的单一映射上执行提示查找:
- 群组专用系统提示(
groups["<groupId>"].systemPrompt):当映射中存在特定群组条目且定义了其systemPrompt键时使用。如果systemPrompt为空字符串(""),则会抑制通配符,并且不应用系统提示。 - 群组通配符系统提示(
groups["*"].systemPrompt):当特定群组条目完全不存在于映射中,或存在但未定义systemPrompt键时使用。
直接消息的解析层级:
首先确定有效的 direct 映射:如果账号定义了自己的 direct,它会完全替换根 direct 映射(不进行深度合并)。然后在生成的单一映射上执行提示查找:
- Direct 专属系统提示词(
direct["<peerId>"].systemPrompt):当映射中存在特定对等方条目且其systemPrompt键已定义时使用。如果systemPrompt是空字符串(""),则会抑制通配符,并且不会应用系统提示词。 - Direct 通配符系统提示词(
direct["*"].systemPrompt):当映射中完全不存在特定对等方条目,或条目存在但未定义systemPrompt键时使用。
**与 Telegram 多账号行为的区别:**在 Telegram 中,多账号设置会有意抑制根级 groups 对所有账号的继承,即使某些账号没有定义自己的 groups,也是如此,以防止机器人接收它不属于的群组中的群组消息。WhatsApp 不应用此防护:对于没有定义账号级覆盖的账号,无论配置了多少个账号,根级 groups 和根级 direct 都始终会被继承。在多账号 WhatsApp 设置中,如果你想要按账号设置群组或 direct 提示词,请在每个账号下显式定义完整映射,而不是依赖根级默认值。
重要行为:
channels.whatsapp.groups既是按群组配置映射,也是聊天级群组允许列表。在根级或账号作用域中,groups["*"]表示该作用域“允许所有群组”。- 只有在你已经希望该作用域允许所有群组时,才添加通配符群组
systemPrompt。如果你仍然只希望一组固定的群组 ID 符合条件,请不要使用groups["*"]作为提示词默认值。改为在每个显式允许列表中的群组条目上重复该提示词。 - 群组准入和发送者授权是两个独立检查。
groups["*"]会扩大可进入群组处理的群组集合,但它本身不会授权这些群组中的每个发送者。发送者访问仍由channels.whatsapp.groupPolicy和channels.whatsapp.groupAllowFrom单独控制。 channels.whatsapp.direct对私信没有相同的副作用。direct["*"]只会在私信已通过dmPolicy加allowFrom或配对存储规则准入后,提供默认 direct 聊天配置。
示例:
{
channels: {
whatsapp: {
groups: {
// Use only if all groups should be admitted at the root scope.
// Applies to all accounts that do not define their own groups map.
"*": { systemPrompt: "Default prompt for all groups." },
},
direct: {
// Applies to all accounts that do not define their own direct map.
"*": { systemPrompt: "Default prompt for all direct chats." },
},
accounts: {
work: {
groups: {
// This account defines its own groups, so root groups are fully
// replaced. To keep a wildcard, define "*" explicitly here too.
"[email protected]": {
requireMention: false,
systemPrompt: "Focus on project management.",
},
// Use only if all groups should be admitted in this account.
"*": { systemPrompt: "Default prompt for work groups." },
},
direct: {
// This account defines its own direct map, so root direct entries are
// fully replaced. To keep a wildcard, define "*" explicitly here too.
"+15551234567": { systemPrompt: "Prompt for a specific work direct chat." },
"*": { systemPrompt: "Default prompt for work direct chats." },
},
},
},
},
},
}
配置参考指针
主要参考:
高信号 WhatsApp 字段:
- 访问:
dmPolicy、allowFrom、groupPolicy、groupAllowFrom、groups - 投递:
textChunkLimit、chunkMode、mediaMaxMb、sendReadReceipts、ackReaction、reactionLevel - 多账号:
accounts.<id>.enabled、accounts.<id>.authDir、账号级覆盖 - 运维:
configWrites、debounceMs、web.enabled、web.heartbeatSeconds、web.reconnect.*、web.whatsapp.* - 会话行为:
session.dmScope、historyLimit、dmHistoryLimit、dms.<id>.historyLimit - 提示词:
groups.<id>.systemPrompt、groups["*"].systemPrompt、direct.<id>.systemPrompt、direct["*"].systemPrompt