@openclaw/signal)。网关通过 HTTP 与 signal-cli 通信:要么使用原生守护进程(JSON-RPC + SSE),要么使用 bbernhard/signal-cli-rest-api 容器(REST + WebSocket)。OpenClaw 不包含 libsignal。
号码模型(请先阅读)
- 网关连接到一个 Signal 设备:
signal-cli账户。 - 在 你的个人 Signal 账户 上运行机器人会使其忽略你自己的消息(循环保护)。
- 如果想实现“我给机器人发短信,它会回复我”,请使用一个单独的机器人号码。
安装
openclaw plugins install clawhub:@openclaw/signal 或 npm:@openclaw/signal 强制指定来源。plugins install 会注册并启用插件;无需单独执行 enable 步骤。有关通用安装规则,请参见 插件。
快速设置
1
选择一个号码
为机器人使用一个单独的 Signal 号码(推荐)。
2
安装插件
3
运行引导式设置
signal-cli 是否在 PATH 中;如果缺失,它会提供安装方式:在 Linux x86-64 上下载官方原生 GraalVM 构建版本,或在 macOS 和其他架构上通过 Homebrew 安装。随后会提示输入机器人号码和 signal-cli 路径。对于非交互式设置,openclaw channels add --channel signal 也接受 --signal-number <e164> 作为机器人电话号码,以及 --http-host <host> 和 --http-port <port> 作为 Signal 守护进程端点(默认 127.0.0.1:8080)。4
5
验证并配对
openclaw pairing approve signal <CODE>。
多账号支持:使用带有每个账号配置和可选
name 的 channels.signal.accounts。每个命名账号都拥有自己的 transport;它不会继承顶层的 transport。顶层 transport 仅属于隐式的 default 账号。有关共享模式,请参见 多账号通道。
它是什么
- 确定性路由:回复始终返回 Signal。
- 私信共享代理的主会话;群组彼此隔离(
agent:<agentId>:signal:group:<groupId>)。 - 默认情况下,Signal 可能会写入由
/config set|unset触发的配置更新(需要commands.config: true)。可通过将channels.signal.configWrites设置为false来禁用。
设置路径 A:链接现有的 Signal 账户(二维码)
- 安装
signal-cli(JVM 或 native build 版本),或者让openclaw channels add为你安装它。 - 链接一个机器人账户:
signal-cli link -n "OpenClaw",然后在 Signal 中扫描二维码。 - 配置 Signal 并启动网关。
设置路径 B:注册专用机器人号码(SMS,Linux)
如果你要使用专用机器人号码,而不是绑定现有的 Signal 应用账户,请使用此方式。下面的流程已在 Ubuntu 24 上测试。- 获取一个可以接收 SMS(或者对于座机可接收语音验证)的号码。专用机器人号码可以避免账户/会话冲突。
- 在网关主机上安装
signal-cli:
signal-cli-${VERSION}.tar.gz),请先安装 JRE。请保持 signal-cli 为最新版本;上游说明旧版本可能会因 Signal 服务器 API 变更而失效。
- 注册并验证该号码:
- 打开
https://signalcaptchas.org/registration/generate.html。 - 完成验证码,从 “Open Signal” 中复制
signalcaptcha://...链接目标。 - 尽可能使用与浏览器会话相同的外部 IP 运行(验证码令牌会很快过期)。
- 立即注册并验证:
- 配置 OpenClaw,重启网关,验证通道:
- 绑定你的 DM 发送者:
- 向机器人号码发送任意消息。
- 在服务器上批准:
openclaw pairing approve signal <PAIRING_CODE>。 - 将机器人号码保存为手机中的联系人,以避免显示为“未知联系人”。
signal-cliREADME:https://github.com/AsamK/signal-cli- 验证码流程:
https://github.com/AsamK/signal-cli/wiki/Registration-with-captcha - 绑定流程:
https://github.com/AsamK/signal-cli/wiki/Linking-other-devices-(Provisioning)
外部原生守护进程模式
要自行管理signal-cli(JVM 冷启动慢、容器初始化、共享 CPU 等场景),请将守护进程单独运行,并让 OpenClaw 指向它:
对于非交互式设置,在需要时请显式选择端点类型:
channels.signal.transport.startupTimeoutMs。
容器模式(bbernhard/signal-cli-rest-api)
不要原生运行signal-cli,而是使用 bbernhard/signal-cli-rest-api Docker 容器,它通过 REST + WebSocket 接口对 signal-cli 进行封装。
- 容器 必须 以
MODE=json-rpc运行,才能实时接收消息。 - 在连接 OpenClaw 之前,先在容器内注册或绑定你的 Signal 账户。
docker-compose.yml 服务:
transport.kind 控制 OpenClaw 使用的协议和进程生命周期:
setup 和 openclaw doctor --fix 可能会探测一次现有端点以识别其具体类型。运行时操作不会自动检测或切换协议。
只要容器暴露了相应的匹配 API,容器模式就支持与原生模式相同的 Signal 操作:发送、接收、附件、正在输入指示、已读/已查看回执、反应、群组以及富文本。OpenClaw 会将原生 Signal RPC 调用转换为容器的 REST 负载,包括 group.{base64(internal_id)} 群组 ID 以及用于格式化文本的 text_mode: "styled"。
运行说明:
- 使用
MODE=json-rpc以接收消息。MODE=normal可能会让/v1/about看起来正常,但/v1/receive/{account}不会进行 WebSocket 升级,因此容器接收流会在探测时失败。 - 对 bbernhard REST API 使用
kind: "container",对原生signal-cliJSON-RPC/SSE 使用kind: "external-native"。 - 容器附件下载在媒体字节限制方面与原生模式保持一致。如果服务器发送了
Content-Length,则在完全缓冲之前会拒绝超出大小的响应;否则在流式传输过程中拒绝。
访问控制(DM + 群组)
直接消息:- 默认:
channels.signal.dmPolicy = "pairing"。 - 未知发送者会收到配对码;在批准之前,消息会被忽略(配对码 1 小时后过期)。
- 通过
openclaw pairing list signal和openclaw pairing approve signal <CODE>进行批准。 - 配对是 Signal 直接消息的默认令牌交换方式。详情:配对
- 仅 UUID 的发送者(来自
sourceUuid)会以uuid:<id>的形式存储在channels.signal.allowFrom中。
channels.signal.groupPolicy = open | allowlist | disabled。- 当设置为
allowlist时,channels.signal.groupAllowFrom控制哪些群组或发送者可以触发群组回复;条目可以是 Signal 群组 ID(原始形式、group:<id>或signal:group:<id>)、发送者电话号码、uuid:<id>值,或*。 channels.signal.groups["<group-id>" | "*"]可通过requireMention、tools和toolsBySender覆盖群组行为。- 在多账户设置中,使用
channels.signal.accounts.<id>.groups进行按账户覆盖。 - 通过
groupAllowFrom将 Signal 群组添加到允许列表,并不会自动禁用提及门控。专门配置的channels.signal.groups["<group-id>"]条目将处理该群组的每条消息,除非设置了requireMention=true。 - 当
requireMention=true时,Signal 原生 @提及会通过结构化提及元数据,匹配机器人账户电话号码或accountUuid。已配置的mentionPatterns仍作为纯文本回退方案。 - 运行时说明:如果
channels.signal完全缺失,运行时会对群组检查回退到groupPolicy="allowlist"(即使已设置channels.defaults.groupPolicy)。
工作原理(行为)
- 原生模式:
signal-cli作为守护进程运行;网关通过 SSE 读取事件。 - 容器模式:网关通过 REST API 发送,并通过 WebSocket 接收。
- 入站消息会被规范化为共享的频道信封。
- 回复始终路由回同一个号码或群组。
- 对入站消息的回复会在后端接受入站时间戳和作者时包含原生 Signal 引用元数据;如果引用元数据缺失或被拒绝,OpenClaw 会将回复作为普通消息发送。
- 使用
channels.signal.replyToMode = off | first | all | batched配置原生引用使用,或使用channels.signal.replyToModeByChatType.direct/group进行按聊天类型覆盖。channels.signal.accounts.<id>下的账号级别值优先。
媒体 + 限制
- 出站文本会按
channels.signal.textChunkLimit分块(默认 4000)。 - 可选的换行分块:将
channels.signal.streaming.chunkMode="newline"设置为按空行(段落边界)拆分,然后再进行长度分块。 - 支持附件(从
signal-cli获取 base64)。 - 当语音备忘录附件缺少
contentType时,会使用signal-cli的文件名作为 MIME 回退值,因此语音转写仍然可以对 AAC 语音备忘录进行分类。 - 默认媒体上限:
channels.signal.mediaMaxMb(默认 8)。 - 使用
channels.signal.ignoreAttachments可跳过为任何传输下载媒体。 - 群组历史上下文使用
channels.signal.historyLimit(或channels.signal.accounts.*.historyLimit),并回退到messages.groupChat.historyLimit。设为0可禁用(默认 50)。
显示正在输入 + 已读回执
- 输入中指示:OpenClaw 通过
signal-cli sendTyping发送正在输入信号,并在回复生成期间持续刷新。 - 已读回执:当
channels.signal.sendReadReceipts为 true 时,OpenClaw 会转发允许的私信的已读回执。 signal-cli不会为群组公开已读回执。
生命周期状态反应
设置messages.statusReactions.enabled: true 以让 Signal 在传入消息轮次中显示共享的 queued/thinking/tool/compaction/done/error 反应生命周期。Signal 使用传入消息的时间戳作为反应目标;群组反应会使用 Signal 群组 ID 加上原始发送者作为目标作者发送。
状态反应还需要一个 ack 反应以及匹配的 messages.ackReactionScope(direct、group-all、group-mentions 或 all)。设置 channels.signal.reactionLevel: "off" 可禁用 Signal 状态反应。
Signal 在最终的 done/error 状态后会恢复初始的 ack 反应。
Reactions(消息工具)
使用channel=signal 的 message action=react。
- Targets: sender E.164 或 UUID(使用 pairing 输出中的
uuid:<id>;直接使用裸 UUID 也可以)。 messageId是你要进行反应的消息的 Signal 时间戳。- 群组反应需要
targetAuthor或targetAuthorUuid。
channels.signal.actions.reactions: 启用/禁用 reaction actions(默认 true)。channels.signal.reactionLevel:off | ack | minimal | extensive(默认minimal)。off/ack禁用 agent reactions(message toolreact会报错)。minimal/extensive启用 agent reactions 并设置指导级别。
- 按账户覆盖:
channels.signal.accounts.<id>.actions.reactions、channels.signal.accounts.<id>.reactionLevel。
批准反应
Signal 执行和插件批准提示使用顶层的approvals.exec 和 approvals.plugin 路由块。Signal 没有 channels.signal.execApprovals 块。
👍批准一次。👎拒绝。- 当请求持久批准时,使用
/approve <id> allow-always。
channels.signal.allowFrom、channels.signal.defaultTo 或匹配账户级字段的明确 Signal 批准者。直接的同聊天执行批准提示即使没有明确的批准者,也仍然可以抑制重复的本地 /approve 回退;没有批准者的群组批准将保持本地回退可见。
问题反应
对于一个包含一个非机密、单选问题以及一到四个选项的ask_user 提示,Signal 会在选项标签旁显示 1️⃣ 到 4️⃣。使用对应的数字对已发送的提示作出反应即可回答。OpenClaw 会验证该反应是否针对机器人生成的消息,然后通过 Gateway 将该数字映射为规范选项。过期或重复的点击会被忽略。多问题、多选项以及自由文本提示仍然只能通过文本回复;正常的 Signal DM/群组准入规则会授权发送者。
投递目标(CLI/cron)
- 私信:
signal:+15551234567(或直接使用 E.164)。 - UUID 私信:
uuid:<id>(或裸 UUID)。 - 群组:
signal:group:<groupId>。 - 用户名:
username:<name>(如果你的 Signal 账户支持)。
别名
为可重复使用的 Signal 目标配置稳定名称。别名仅用于 OpenClaw 侧配置;它们不会创建或编辑 Signal 联系人。openclaw directory peers list --channel signal 和 openclaw directory groups list --channel signal 会列出已配置的别名。Signal 目录由配置驱动;它不会实时查询 Signal 联系人,也不会修改 Signal 账户。
故障排查
先运行这组检查:- Daemon 可达但没有回复:请检查
account、transport.kind、传输 URL,以及接收模式。 - 私信被忽略:发送方处于待配对审批状态。
- 群消息被忽略:群发送方/提及门控阻止了投递。
- 编辑后出现配置校验错误:运行
openclaw doctor --fix。 - 诊断中缺少 Signal:确认
channels.signal.enabled: true。
安全说明
signal-cli会将账户密钥存储在本地(通常位于~/.local/share/signal-cli/data/)。- 在服务器迁移或重建之前,请先备份 Signal 账户状态。
- 保持
channels.signal.dmPolicy: "pairing",除非你明确希望更广泛的 DM 访问。 - SMS 验证仅在注册或恢复流程中需要,但如果失去对号码/账户的控制,可能会使重新注册变得复杂。
配置参考(Signal)
完整配置:配置 提供程序选项:channels.signal.enabled: 启用/禁用频道启动。channels.signal.account: 机器人账户的 E.164。channels.signal.accountUuid: 可选的机器人账户 UUID,用于原生 @mention 检测和循环保护。channels.signal.transport: 账户自有传输。对于托管原生默认值可省略。channels.signal.transport.kind:managed-native | external-native | container。channels.signal.transport.url:external-native和container必填;当managed-native的连接端点不同于守护进程绑定时可选。channels.signal.transport.cliPath:signal-cli的托管原生路径。channels.signal.transport.configPath: 可选的托管原生signal-cli --config目录。channels.signal.transport.httpHost,channels.signal.transport.httpPort: 托管原生守护进程绑定(默认127.0.0.1:8080)。channels.signal.transport.startupTimeoutMs: 托管原生启动等待时间,单位为毫秒(最小 1000,最大 120000;默认 30000)。channels.signal.transport.receiveMode: 托管原生on-start | manual。channels.signal.ignoreAttachments: 跳过此账户的入站附件下载。channels.signal.transport.ignoreStories: 托管原生故事开关。channels.signal.sendReadReceipts: 转发已读回执。channels.signal.dmPolicy:pairing | allowlist | open | disabled(默认:pairing)。channels.signal.allowFrom: DM 允许列表(E.164 或uuid:<id>)。open需要"*"。Signal 没有用户名;请使用电话/UUID ID。channels.signal.aliases: OpenClaw 侧用于 DM 或群组投递目标的别名。channels.signal.groupPolicy:open | allowlist | disabled(默认:allowlist)。channels.signal.groupAllowFrom: 群组允许列表;接受 Signal 群组 ID(原始形式、group:<id>或signal:group:<id>)、发送者 E.164 号码或uuid:<id>值。channels.signal.groups: 以 Signal 群组 ID(或"*")为键的每个群组覆盖项。支持字段:requireMention、tools、toolsBySender。channels.signal.accounts.<id>.groups:channels.signal.groups的按账户版本,用于多账户设置。channels.signal.accounts.<id>.aliases: 按账户别名,与顶层别名合并。channels.signal.replyToMode: 原生回复引用模式,off | first | all | batched(默认:all)。channels.signal.replyToModeByChatType.direct,channels.signal.replyToModeByChatType.group: 按聊天类型的原生回复引用覆盖项。channels.signal.accounts.<id>.replyToMode,channels.signal.accounts.<id>.replyToModeByChatType.direct,channels.signal.accounts.<id>.replyToModeByChatType.group: 按账户回复引用覆盖项。channels.signal.historyLimit: 作为上下文包含的群组消息最大数量(0 表示禁用)。channels.signal.dmHistoryLimit: 用户轮次中的 DM 历史限制。按用户覆盖:channels.signal.dms["<phone_or_uuid>"].historyLimit。channels.signal.textChunkLimit: 出站分块大小,单位字符(默认 4000)。channels.signal.streaming.chunkMode:length(默认)或newline,用于在按长度分块之前按空行(段落边界)拆分。channels.signal.mediaMaxMb: 入站/出站媒体上限,单位 MB(默认 8)。channels.signal.reactionLevel:off | ack | minimal | extensive(默认minimal)。请参见反应。channels.signal.reactionNotifications:off | own | all | allowlist(默认own)- 当代理收到来自他人的入站反应时的通知方式。channels.signal.reactionAllowlist: 当reactionNotifications: "allowlist"时,会通知代理的反应发送者。channels.signal.streaming.block.enabled,channels.signal.streaming.block.coalesce: 跨频道共享的块模式流式控制。请参见流式传输。
agents.entries.*.groupChat.mentionPatterns(纯文本回退;当配置了机器人账户身份时,Signal 原生 @mentions 会从结构化元数据中检测)。messages.groupChat.mentionPatterns(全局回退)。channels.signal.responsePrefix或账户级别的responsePrefix。