[OpenClaw 文档]消息渠道--消息平台

[OpenClaw 文档]消息渠道--消息平台

本文档汇总了 OpenClaw 官方文档站 消息渠道 > 消息平台 子模块下的全部 18 篇内容,源自 docs.openclaw.ai/zh-CN

📄 Discord

原文:https://docs.openclaw.ai/zh-CN/channels/discord

已可通过官方 Discord Gateway 网关用于私信和服务器频道。



Discord 私信默认使用配对模式。


原生命令行为和命令目录。


跨渠道诊断和修复流程。

快速设置

你需要创建一个带有机器人的新应用,将该机器人添加到你的服务器,并将其配对到 OpenClaw。我们建议将机器人添加到你自己的私有服务器。如果你还没有服务器,请先创建一个(选择 Create My Own > For me and my friends)。



前往 Discord Developer Portal,然后点击 New Application。将其命名为类似 “OpenClaw” 的名称。

点击侧边栏中的 **Bot**。将 **Username** 设置为你给 OpenClaw 智能体起的名称。


仍在 Bot 页面上,向下滚动到 Privileged Gateway Intents 并启用:

- **Message Content Intent**(必需)
- **Server Members Intent**(推荐;角色允许列表和名称到 ID 匹配需要它)
- **Presence Intent**(可选;仅状态更新需要)


Bot 页面上向上滚动并点击 Reset Token

<Note>
尽管名称如此,这会生成你的第一个令牌,并没有任何内容被“重置”。
</Note>

复制该令牌并保存到某处。这是你的 **Bot Token**,稍后会用到。


点击侧边栏中的 OAuth2。你将生成一个带有正确权限的邀请 URL,用于将机器人添加到你的服务器。

向下滚动到 **OAuth2 URL Generator** 并启用:

- `bot`
- `applications.commands`

下方会出现 **Bot Permissions** 部分。至少启用:

**General Permissions**
  - 查看频道
**Text Permissions**
  - 发送消息
  - 读取消息历史
  - 嵌入链接
  - 附加文件
  - 添加回应(可选)

这是普通文本频道的基线权限集。如果你计划在 Discord 话题中发帖,包括创建或继续话题的论坛或媒体频道工作流,还要启用 **Send Messages in Threads**
复制底部生成的 URL,将其粘贴到浏览器中,选择你的服务器,然后点击 **Continue** 进行连接。现在你应该能在 Discord 服务器中看到你的机器人。


回到 Discord 应用中,你需要启用 Developer Mode,才能复制内部 ID。

1. 点击 **User Settings**头像旁边的齿轮图标)→ **Advanced**  打开 **Developer Mode**
2. 右键点击侧边栏中的 **服务器图标**  **Copy Server ID**
3. 右键点击你**自己的头像**  **Copy User ID**

将你的 **Server ID**  **User ID**  Bot Token 一起保存下一步你会把这三项都发送给 OpenClaw


要让配对工作,Discord 需要允许你的机器人向你发送私信。右键点击你的服务器图标Privacy Settings → 打开 Direct Messages

这会允许服务器成员(包括机器人)向你发送私信。如果你想通过 Discord 私信使用 OpenClaw,请保持此项启用。如果你只计划使用服务器频道,可以在配对后禁用私信。


你的 Discord 机器人令牌是机密(类似密码)。在向智能体发消息之前,请先在运行 OpenClaw 的机器上设置它。

export DISCORD_BOT_TOKEN="YOUR_BOT_TOKEN"
cat > discord.patch.json5 <<'JSON5'
{
  channels: {
    discord: {
      enabled: true,
      token: { source: "env", provider: "default", id: "DISCORD_BOT_TOKEN" },
    },
  },
}
JSON5
openclaw config patch --file ./discord.patch.json5 --dry-run
openclaw config patch --file ./discord.patch.json5
openclaw gateway
如果 OpenClaw 已作为后台服务运行,请通过 OpenClaw Mac 应用重启它,或停止并重启 `openclaw gateway run` 进程。
对于托管服务安装,请在存在 `DISCORD_BOT_TOKEN`  shell 中运行 `openclaw gateway install`,或将该变量存储在 `~/.openclaw/.env` 中,这样服务在重启后就能解析 env SecretRef。
如果你的主机被 Discord 的启动应用查询阻止或限速,请从 Developer Portal 设置 Discord 应用/客户端 ID,以便启动时跳过该 REST 调用。默认账户使用 `channels.discord.applicationId`;运行多个 Discord 机器人时,使用 `channels.discord.accounts.<accountId>.applicationId`

<Tabs>
  <Tab title="询问你的智能体">
    在任何现有渠道(例如 Telegram)中与你的 OpenClaw 智能体聊天并告诉它。如果 Discord 是你的第一个渠道,请改用 CLI / 配置标签页。

    > “我已经在配置中设置了 Discord 机器人令牌。请使用 User ID `<user_id>`  Server ID `<server_id>` 完成 Discord 设置。”
  </Tab>
  <Tab title="CLI / 配置">
    如果你更喜欢基于文件的配置,请设置:
{
  channels: {
    discord: {
      enabled: true,
      token: {
        source: "env",
        provider: "default",
        id: "DISCORD_BOT_TOKEN",
      },
    },
  },
}
    默认账户的环境变量回退:
DISCORD_BOT_TOKEN=...
    对于脚本化或远程设置,请使用 `openclaw config patch --file ./discord.patch.json5 --dry-run` 写入同一个 JSON5 块,然后在不带 `--dry-run` 的情况下重新运行。支持明文 `token` 值。对于 `channels.discord.token`,也支持跨 env/file/exec 提供商的 SecretRef 值。参见[密钥管理](/zh-CN/gateway/secrets)

    对于多个 Discord 机器人,请将每个机器人令牌和应用 ID 放在各自账户下。顶层 `channels.discord.applicationId` 会被账户继承,因此只有在每个账户都应使用同一个应用 ID 时,才在那里设置它。
{
  channels: {
    discord: {
      enabled: true,
      accounts: {
        personal: {
          token: { source: "env", provider: "default", id: "DISCORD_PERSONAL_TOKEN" },
          applicationId: "111111111111111111",
        },
        work: {
          token: { source: "env", provider: "default", id: "DISCORD_WORK_TOKEN" },
          applicationId: "222222222222222222",
        },
      },
    },
  },
}
  </Tab>
</Tabs>


等到 Gateway 网关运行后,在 Discord 中向你的机器人发送私信。它会回复一个配对码。

<Tabs>
  <Tab title="询问你的智能体">
    将配对码发送到现有渠道中的智能体:

    > “批准这个 Discord 配对码:`<CODE>`”
  </Tab>
  <Tab title="CLI">
openclaw pairing list discord
openclaw pairing approve discord <CODE>
  </Tab>
</Tabs>

配对码会在 1 小时后过期。

现在你应该可以通过私信在 Discord 中与你的智能体聊天。



令牌解析会感知账户。配置令牌值优先于环境变量回退。DISCORD_BOT_TOKEN 仅用于默认账户。
如果两个已启用的 Discord 账户解析到同一个机器人令牌,OpenClaw 只会为该令牌启动一个 Gateway 网关监控器。来自配置的令牌优先于默认环境变量回退;否则第一个已启用账户胜出,重复账户会被报告为已禁用。
对于高级出站调用(消息工具/渠道操作),显式的逐调用 token 会用于该调用。这适用于发送和读取/探测类操作(例如 read/search/fetch/thread/pins/permissions)。账户策略/重试设置仍来自活动运行时快照中所选的账户。

推荐:设置服务器工作区

私信可用后,你可以将 Discord 服务器设置为完整工作区,其中每个频道都有自己的智能体会话和独立上下文。对于只有你和你的机器人的私有服务器,推荐这样做。



这会让你的智能体能够在服务器上的任何频道中响应,而不仅是私信。

<Tabs>
  <Tab title="询问你的智能体">
    > “将我的 Discord Server ID `<server_id>` 添加到服务器允许列表”
  </Tab>
  <Tab title="配置">
{
  channels: {
    discord: {
      groupPolicy: "allowlist",
      guilds: {
        YOUR_SERVER_ID: {
          requireMention: true,
          users: ["YOUR_USER_ID"],
        },
      },
    },
  },
}
  </Tab>
</Tabs>


默认情况下,只有在服务器频道中被 @mentioned 时,你的智能体才会响应。对于私有服务器,你可能希望它响应每条消息。

在服务器频道中,普通助手最终回复默认保持私密。可见的 Discord 输出必须使用 `message` 工具显式发送,因此智能体默认可以旁观,并且只有在它判断频道回复有用时才发帖。

这意味着所选模型必须可靠地调用工具。如果 Discord 显示正在输入,日志也显示 token 用量,但没有发布消息,请检查会话日志中是否有带 `didSendViaMessagingTool: false` 的助手文本。这意味着模型生成了私密最终答案,而不是调用 `message(action=send)`。切换到更强的工具调用模型,或使用下面的配置恢复旧版自动最终回复。

<Tabs>
  <Tab title="询问你的智能体">
    > “允许我的智能体在此服务器上无需被 @mentioned 即可响应”
  </Tab>
  <Tab title="配置">
    在你的服务器配置中设置 `requireMention: false`
{
  channels: {
    discord: {
      guilds: {
        YOUR_SERVER_ID: {
          requireMention: false,
        },
      },
    },
  },
}
    要为群组/频道房间恢复旧版自动最终回复,请设置 `messages.groupChat.visibleReplies: "automatic"`。

  </Tab>
</Tabs>


默认情况下,长期记忆(MEMORY.md)只会在私信会话中加载。服务器频道不会自动加载 MEMORY.md。

<Tabs>
  <Tab title="询问你的智能体">
    > “当我在 Discord 频道中提问时,如果你需要来自 MEMORY.md 的长期上下文,请使用 memory_search  memory_get。”
  </Tab>
  <Tab title="手动">
    如果你需要在每个频道中共享上下文,请将稳定指令放入 `AGENTS.md`  `USER.md`(它们会被注入每个会话)。将长期笔记保存在 `MEMORY.md` 中,并在需要时通过记忆工具访问它们。
  </Tab>
</Tabs>


现在在你的 Discord 服务器上创建一些频道并开始聊天。你的智能体可以看到频道名称,并且每个频道都会获得自己的隔离会话,因此你可以设置 #coding#home#research,或任何适合你工作流的内容。

运行时模型

  • Gateway 网关拥有 Discord 连接。
  • 回复路由是确定性的:Discord 入站回复会返回到 Discord。
  • Discord 服务器/频道元数据会作为不受信任的上下文加入模型提示中,
    而不是作为用户可见的回复前缀。如果模型把该封套复制回来,
    OpenClaw 会从出站回复和未来的重放上下文中剥离复制的元数据。
  • 默认情况下(session.dmScope=main),直接聊天共享智能体主会话(agent:main:main)。
  • 服务器频道使用隔离的会话键(agent:<agentId>:discord:channel:<channelId>)。
  • 群组私信默认会被忽略(channels.discord.dm.groupEnabled=false)。
  • 原生斜杠命令在隔离的命令会话中运行(agent:<agentId>:discord:slash:<userId>),同时仍携带 CommandTargetSessionKey 指向路由后的对话会话。
  • 面向 Discord 的纯文本 cron/heartbeat 公告投递会使用最终的
    助手可见答案一次。媒体和结构化组件载荷在智能体发出多个可投递载荷时,
    仍保持多消息形式。

论坛频道

Discord 论坛和媒体频道只接受主题帖。OpenClaw 支持两种创建方式:

  • 向论坛父级(channel:<forumId>)发送消息以自动创建主题。主题标题使用你的消息中第一行非空内容。
  • 使用 openclaw message thread create 直接创建主题。不要为论坛频道传递 --message-id

示例:发送到论坛父级以创建主题

openclaw message send --channel discord --target channel:<forumId> \
  --message "Topic title\nBody of the post"

示例:显式创建论坛主题

openclaw message thread create --channel discord --target channel:<forumId> \
  --thread-name "Topic title" --message "Body of the post"

论坛父级不接受 Discord 组件。如果你需要组件,请发送到主题本身(channel:<threadId>)。

交互式组件

OpenClaw 支持用于智能体消息的 Discord 组件 v2 容器。使用带 components 载荷的消息工具。交互结果会作为普通入站消息路由回智能体,并遵循现有 Discord replyToMode 设置。

支持的块:

  • textsectionseparatoractionsmedia-galleryfile
  • 操作行最多允许 5 个按钮或一个选择菜单
  • 选择类型:stringuserrolementionablechannel

默认情况下,组件只能使用一次。设置 components.reusable=true 可允许按钮、选择和表单在过期前被多次使用。

要限制谁可以点击按钮,请在该按钮上设置 allowedUsers(Discord 用户 ID、标签或 *)。配置后,不匹配的用户会收到一条仅自己可见的拒绝消息。

/model/models 斜杠命令会打开交互式模型选择器,其中包含提供商、模型和兼容运行时下拉菜单,以及一个提交步骤。/models add 已弃用,现在会返回弃用消息,而不是从聊天中注册模型。选择器回复仅自己可见,并且只有调用用户可以使用。Discord 选择菜单限制为 25 个选项,因此当你希望选择器只为所选提供商(例如 openai-codexvllm)显示动态发现的模型时,请向 agents.defaults.models 添加 provider/* 条目。

文件附件:

  • file 块必须指向附件引用(attachment://<filename>
  • 通过 media/path/filePath 提供附件(单个文件);多个文件使用 media-gallery
  • 当上传名称应与附件引用匹配时,使用 filename 覆盖上传名称

模态表单:

  • 添加最多包含 5 个字段的 components.modal
  • 字段类型:textcheckboxradioselectrole-selectuser-select
  • OpenClaw 会自动添加触发按钮

示例:

{
  channel: "discord",
  action: "send",
  to: "channel:123456789012345678",
  message: "Optional fallback text",
  components: {
    reusable: true,
    text: "Choose a path",
    blocks: [
      {
        type: "actions",
        buttons: [
          {
            label: "Approve",
            style: "success",
            allowedUsers: ["123456789012345678"],
          },
          { label: "Decline", style: "danger" },
        ],
      },
      {
        type: "actions",
        select: {
          type: "string",
          placeholder: "Pick an option",
          options: [
            { label: "Option A", value: "a" },
            { label: "Option B", value: "b" },
          ],
        },
      },
    ],
    modal: {
      title: "Details",
      triggerLabel: "Open form",
      fields: [
        { type: "text", label: "Requester" },
        {
          type: "select",
          label: "Priority",
          options: [
            { label: "Low", value: "low" },
            { label: "High", value: "high" },
          ],
        },
      ],
    },
  },
}

访问控制和路由



channels.discord.dmPolicy 控制私信访问。channels.discord.allowFrom 是规范的私信允许列表。

- `pairing`(默认)
- `allowlist`
- `open`(要求 `channels.discord.allowFrom` 包含 `"*"`
- `disabled`

如果私信策略不是开放的,未知用户会被阻止(或在 `pairing` 模式下被提示进行配对)。

多账号优先级:

- `channels.discord.accounts.default.allowFrom` 仅适用于 `default` 账号。
- 对于单个账号,`allowFrom` 优先于旧版 `dm.allowFrom`
- 当命名账号自己的 `allowFrom` 和旧版 `dm.allowFrom` 均未设置时,会继承 `channels.discord.allowFrom`
- 命名账号不会继承 `channels.discord.accounts.default.allowFrom`

旧版 `channels.discord.dm.policy`  `channels.discord.dm.allowFrom` 仍会读取以保持兼容。`openclaw doctor --fix` 会在不改变访问权限的前提下尽可能将它们迁移到 `dmPolicy`  `allowFrom`

用于投递的私信目标格式:

- `user:<id>`
- `<@id>` 提及

当频道默认值处于活动状态时,裸数字 ID 通常会解析为频道 ID,但为了兼容性,列在账号有效私信 `allowFrom` 中的 ID 会被视为用户私信目标。


Discord 私信和文本命令授权可以使用 channels.discord.allowFrom 中的动态 accessGroup:<name> 条目。

访问组名称会在消息渠道之间共享。对于成员以各渠道普通 `allowFrom` 语法表示的静态组,使用 `type: "message.senders"`;当 Discord 频道当前的 `ViewChannel` 受众应动态定义成员资格时,使用 `type: "discord.channelAudience"`。共享访问组行为记录在这里:[访问组](/zh-CN/channels/access-groups)
{
  accessGroups: {
    operators: {
      type: "message.senders",
      members: {
        "*": ["global-owner-id"],
        discord: ["discord:123456789012345678"],
        telegram: ["987654321"],
      },
    },
  },
  channels: {
    discord: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:operators"],
    },
  },
}
Discord 文本频道没有单独的成员列表。`type: "discord.channelAudience"` 将成员资格建模为:私信发送者是配置服务器的成员,并且在应用角色和频道覆盖后,当前对配置频道拥有有效的 `ViewChannel` 权限。

示例:允许任何能看到 `#maintainers` 的人向机器人发送私信,同时对其他所有人关闭私信。
{
  accessGroups: {
    maintainers: {
      type: "discord.channelAudience",
      guildId: "1456350064065904867",
      channelId: "1456744319972282449",
      membership: "canViewChannel",
    },
  },
  channels: {
    discord: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:maintainers"],
    },
  },
}
你可以混合动态和静态条目:
{
  accessGroups: {
    maintainers: {
      type: "discord.channelAudience",
      guildId: "1456350064065904867",
      channelId: "1456744319972282449",
    },
  },
  channels: {
    discord: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:maintainers", "discord:123456789012345678"],
    },
  },
}
查询会失败即关闭。如果 Discord 返回 `Missing Access`、成员查询失败,或频道属于不同服务器,则该私信发送者会被视为未授权。

使用频道受众访问组时,请在 Discord Developer Portal 中为机器人启用 **Server Members Intent**。私信不包含服务器成员状态,因此 OpenClaw 会在授权时通过 Discord REST 解析成员。


服务器处理由 channels.discord.groupPolicy 控制:

- `open`
- `allowlist`
- `disabled`

存在 `channels.discord` 时,安全基线是 `allowlist`

`allowlist` 行为:

- 服务器必须匹配 `channels.discord.guilds`(优先使用 `id`,也接受 slug)
- 可选发送者允许列表:`users`(推荐稳定 ID)和 `roles`(仅角色 ID);如果配置了任一项,发送者匹配 `users`  `roles` 时会被允许
- 默认禁用直接名称/标签匹配;仅在破窗兼容模式下启用 `channels.discord.dangerouslyAllowNameMatching: true`
- `users` 支持名称/标签,但 ID 更安全;使用名称/标签条目时,`openclaw security audit` 会发出警告
- 如果服务器配置了 `channels`,未列出的频道会被拒绝
- 如果服务器没有 `channels` 块,则该允许列表服务器中的所有频道都会被允许

示例:
{
  channels: {
    discord: {
      groupPolicy: "allowlist",
      guilds: {
        "123456789012345678": {
          requireMention: true,
          ignoreOtherMentions: true,
          users: ["987654321098765432"],
          roles: ["123456789012345678"],
          channels: {
            general: { allow: true },
            help: { allow: true, requireMention: true },
          },
        },
      },
    },
  },
}
如果你只设置 `DISCORD_BOT_TOKEN`,但没有创建 `channels.discord` 块,则运行时回退为 `groupPolicy="allowlist"`(日志中会有警告),即使 `channels.defaults.groupPolicy`  `open`


服务器消息默认通过提及门控。

提及检测包括:

- 显式提及机器人
- 配置的提及模式(`agents.list[].groupChat.mentionPatterns`,回退到 `messages.groupChat.mentionPatterns`
- 受支持情况下的隐式回复机器人行为

编写出站 Discord 消息时,使用规范的提及语法:用户使用 `<@USER_ID>`,频道使用 `<#CHANNEL_ID>`,角色使用 `<@&ROLE_ID>`。不要使用旧版 `<@!USER_ID>` 昵称提及形式。

`requireMention` 按服务器/频道配置(`channels.discord.guilds...`)。
`ignoreOtherMentions` 可选择丢弃提及其他用户/角色但未提及机器人的消息(不包括 @everyone/@here)。

群组私信:

- 默认:忽略(`dm.groupEnabled=false`
- 可选允许列表通过 `dm.groupChannels` 设置(频道 ID  slug)


基于角色的智能体路由

使用 bindings[].match.roles 按角色 ID 将 Discord 服务器成员路由到不同智能体。基于角色的绑定只接受角色 ID,并且会在对等或父对等绑定之后、仅服务器绑定之前求值。如果绑定还设置了其他匹配字段(例如 peer + guildId + roles),则所有配置字段都必须匹配。

{
  bindings: [
    {
      agentId: "opus",
      match: {
        channel: "discord",
        guildId: "123456789012345678",
        roles: ["111111111111111111"],
      },
    },
    {
      agentId: "sonnet",
      match: {
        channel: "discord",
        guildId: "123456789012345678",
      },
    },
  ],
}

原生命令和命令授权

  • commands.native 默认为 "auto",并为 Discord 启用。
  • 按渠道覆盖:channels.discord.commands.native
  • commands.native=false 会在启动期间跳过 Discord 斜杠命令注册和清理。此前注册的命令可能仍会在 Discord 中可见,直到你从 Discord 应用中移除它们。
  • 原生命令认证使用与普通消息处理相同的 Discord 允许列表/策略。
  • 对未授权用户,命令可能仍会在 Discord UI 中可见;执行时仍会强制应用 OpenClaw 认证,并返回 “not authorized”。

请参阅斜杠命令,了解命令目录和行为。

默认斜杠命令设置:

  • ephemeral: true

功能详情



Discord 支持智能体输出中的回复标签:

- `[[reply_to_current]]`
- `[[reply_to:<id>]]`

 `channels.discord.replyToMode` 控制:

- `off`(默认)
- `first`
- `all`
- `batched`

注意:`off` 会禁用隐式回复线程。显式 `[[reply_to_*]]` 标签仍会被遵循。
`first` 始终会把隐式原生回复引用附加到该轮次的第一条出站 Discord 消息。
`batched` 仅在入站轮次是多条消息的防抖批处理时,才会附加 Discord 的隐式原生回复引用。这在你希望主要针对含义不明确的突发聊天使用原生回复,而不是每个单消息轮次都使用时很有用。

消息 ID 会在上下文/历史记录中暴露,以便智能体可以定位特定消息。


OpenClaw 可以通过发送临时消息并在文本到达时编辑它来流式传输回复草稿。channels.discord.streaming 接受 off | partial | block | progress(默认)。progress 会保留一条可编辑的状态草稿,并用工具进度更新它,直到最终投递;共享的起始标签是一条滚动行,因此一旦出现足够多的工作内容,它就会像其余内容一样滚动离开。streamMode 是旧版运行时别名。运行 openclaw doctor --fix 可将持久化配置重写为规范键。

 `channels.discord.streaming.mode` 设置为 `off` 可禁用 Discord 预览编辑。如果明确启用了 Discord 分块流式传输,OpenClaw 会跳过预览流,以避免重复流式传输。
{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          label: "auto",
          maxLines: 8,
          toolProgress: true,
        },
      },
    },
  },
}
- `partial` 会在令牌到达时编辑同一条预览消息。
- `block` 会发出草稿大小的分块(使用 `draftChunk` 调整大小和断点,并限制在 `textChunkLimit` 内)。
- 媒体、错误和显式回复的最终消息会取消待处理的预览编辑。
- `streaming.preview.toolProgress`(默认 `true`)控制工具/进度更新是否复用预览消息。
- 工具/进度行会在可用时渲染为紧凑的表情符号 + 标题 + 详情,例如 `🛠️ Bash: run tests`  `🔎 Web Search: for "query"`
- `streaming.preview.commandText` / `streaming.progress.commandText` 控制紧凑进度行中的命令/执行详情:`raw`(默认)或 `status`(仅工具标签)。

隐藏原始命令/执行文本,同时保留紧凑进度行:

```json
{
  "channels": {
    "discord": {
      "streaming": {
        "mode": "progress",
        "progress": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}
```

预览流式传输仅支持文本;媒体回复会回退到正常投递。当明确启用 `block` 流式传输时,OpenClaw 会跳过预览流,以避免重复流式传输。


服务器历史记录上下文:

- `channels.discord.historyLimit` 默认 `20`
- 回退:`messages.groupChat.historyLimit`
- `0` 会禁用

私信历史记录控制:

- `channels.discord.dmHistoryLimit`
- `channels.discord.dms["<user_id>"].historyLimit`

线程行为:

- Discord 线程会作为渠道会话路由,并继承父渠道配置,除非被覆盖。
- 线程会话会继承父渠道的会话级 `/model` 选择,作为仅模型回退;线程本地 `/model` 选择仍优先,且父转录历史不会被复制,除非启用了转录继承。
- `channels.discord.thread.inheritParent`(默认 `false`)会让新的自动线程从父转录播种。按账号覆盖位于 `channels.discord.accounts.<id>.thread.inheritParent` 下。
- 消息工具反应可以解析 `user:<id>` 私信目标。
- `guilds.<guild>.channels.<channel>.requireMention: false` 会在回复阶段激活回退期间保留。

渠道主题会作为**不受信任的**上下文注入。允许列表用于限制谁可以触发智能体,而不是完整的补充上下文脱敏边界。


Discord 可以将线程绑定到会话目标,因此该线程中的后续消息会继续路由到同一会话(包括子智能体会话)。

命令:

- `/focus <target>` 将当前/新线程绑定到子智能体/会话目标
- `/unfocus` 移除当前线程绑定
- `/agents` 显示活动运行和绑定状态
- `/session idle <duration|off>` 查看/更新已聚焦绑定的不活动自动取消聚焦
- `/session max-age <duration|off>` 查看/更新已聚焦绑定的硬性最大存续时间

配置:
{
  session: {
    threadBindings: {
      enabled: true,
      idleHours: 24,
      maxAgeHours: 0,
    },
  },
  channels: {
    discord: {
      threadBindings: {
        enabled: true,
        idleHours: 24,
        maxAgeHours: 0,
        spawnSessions: true,
        defaultSpawnContext: "fork",
      },
    },
  },
}
注意

- `session.threadBindings.*` 设置全局默认值
- `channels.discord.threadBindings.*` 覆盖 Discord 行为
- `spawnSessions` 控制通过 `sessions_spawn({ thread: true })`  ACP 线程生成来自动创建/绑定线程默认:`true`。
- `defaultSpawnContext` 控制线程绑定生成的原生子智能体上下文默认:`"fork"`。
- 已弃用的 `spawnSubagentSessions`/`spawnAcpSessions` 键会由 `openclaw doctor --fix` 迁移
- 如果账号禁用了线程绑定,`/focus` 和相关线程绑定操作将不可用

请参阅[子智能体](/zh-CN/tools/subagents)[ACP Agents](/zh-CN/tools/acp-agents) [配置参考](/zh-CN/gateway/configuration-reference)


对于稳定的“始终在线”ACP 工作区,请配置顶层类型化 ACP 绑定,目标指向 Discord 对话。

配置路径

- `bindings[]`,带有 `type: "acp"`  `match.channel: "discord"`

示例
{
  agents: {
    list: [
      {
        id: "codex",
        runtime: {
          type: "acp",
          acp: {
            agent: "codex",
            backend: "acpx",
            mode: "persistent",
            cwd: "/workspace/openclaw",
          },
        },
      },
    ],
  },
  bindings: [
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "discord",
        accountId: "default",
        peer: { kind: "channel", id: "222222222222222222" },
      },
      acp: { label: "codex-main" },
    },
  ],
  channels: {
    discord: {
      guilds: {
        "111111111111111111": {
          channels: {
            "222222222222222222": {
              requireMention: false,
            },
          },
        },
      },
    },
  },
}
注意

- `/acp spawn codex --bind here` 会在原位置绑定当前渠道或线程并让未来消息保持在同一 ACP 会话上线程消息会继承父渠道绑定
- 在已绑定渠道或线程中,`/new`  `/reset` 会在原位置重置同一 ACP 会话临时线程绑定可以在活动期间覆盖目标解析
- `spawnSessions` 通过 `--thread auto|here` 控制子线程创建/绑定

请参阅 [ACP Agents](/zh-CN/tools/acp-agents)了解绑定行为详情


按服务器的反应通知模式:

- `off`
- `own`(默认)
- `all`
- `allowlist`(使用 `guilds.<id>.users`

反应事件会被转换为系统事件,并附加到路由后的 Discord 会话。


ackReaction 会在 OpenClaw 处理入站消息时发送一个确认表情符号。

解析顺序:

- `channels.discord.accounts.<accountId>.ackReaction`
- `channels.discord.ackReaction`
- `messages.ackReaction`
- 智能体身份表情符号回退(`agents.list[].identity.emoji`,否则为 "👀")

注意:

- Discord 接受 unicode 表情符号或自定义表情符号名称。
- 使用 `""` 可禁用渠道或账号的反应。


渠道发起的配置写入默认启用。

这会影响 `/config set|unset` 流程(当命令功能启用时)。

禁用:
{
  channels: {
    discord: {
      configWrites: false,
    },
  },
}


通过带有 channels.discord.proxy 的 HTTP(S) 代理路由 Discord gateway WebSocket 流量和启动 REST 查询(应用 ID + 允许列表解析)。

{
  channels: {
    discord: {
      proxy: "http://proxy.example:8080",
    },
  },
}
按账号覆盖:
{
  channels: {
    discord: {
      accounts: {
        primary: {
          proxy: "http://proxy.example:8080",
        },
      },
    },
  },
}


启用 PluralKit 解析,将代理消息映射到系统成员身份:

{
  channels: {
    discord: {
      pluralkit: {
        enabled: true,
        token: "pk_live_...", // optional; needed for private systems
      },
    },
  },
}
注意:

- 允许列表可以使用 `pk:<memberId>`
- 仅当 `channels.discord.dangerouslyAllowNameMatching: true` 时,成员显示名称才会按名称/slug 匹配
- 查询使用原始消息 ID,并受时间窗口约束
- 如果查询失败,代理消息会被视为机器人消息并丢弃,除非 `allowBots=true`


当智能体需要对已知 Discord 用户进行确定性的出站提及时,使用 mentionAliases。键是不带前导 @ 的 handle;值是 Discord 用户 ID。未知 handle、@everyone@here 以及 Markdown 代码跨度中的提及会保持不变。

{
  channels: {
    discord: {
      mentionAliases: {
        Vladislava: "123456789012345678",
      },
      accounts: {
        ops: {
          mentionAliases: {
            OpsLead: "234567890123456789",
          },
        },
      },
    },
  },
}


当你设置状态或活动字段,或启用自动在线状态时,会应用在线状态更新。

仅状态示例:
{
  channels: {
    discord: {
      status: "idle",
    },
  },
}
活动示例(自定义状态是默认活动类型):
{
  channels: {
    discord: {
      activity: "Focus time",
      activityType: 4,
    },
  },
}
流式传输示例:
{
  channels: {
    discord: {
      activity: "Live coding",
      activityType: 1,
      activityUrl: "https://twitch.tv/openclaw",
    },
  },
}
活动类型映射:

- 0: 正在玩
- 1: 直播(需要 `activityUrl`
- 2: 正在听
- 3: 正在观看
- 4: 自定义(使用活动文本作为状态 state;emoji 可选)
- 5: 正在竞赛

自动在线状态示例(运行时健康信号):
{
  channels: {
    discord: {
      autoPresence: {
        enabled: true,
        intervalMs: 30000,
        minUpdateIntervalMs: 15000,
        exhaustedText: "token exhausted",
      },
    },
  },
}
自动在线状态会将运行时可用性映射到 Discord Status:healthy => online,degraded 或 unknown => idle,exhausted 或 unavailable => dnd。可选文本覆盖项:

- `autoPresence.healthyText`
- `autoPresence.degradedText`
- `autoPresence.exhaustedText`(支持 `{reason}` 占位符)


Discord 支持在私信中基于按钮的批准处理,也可以选择在发起的渠道中发布批准提示。

配置路径:

- `channels.discord.execApprovals.enabled`
- `channels.discord.execApprovals.approvers`(可选;可行时回退到 `commands.ownerAllowFrom`
- `channels.discord.execApprovals.target``dm` | `channel` | `both`,默认值:`dm`
- `agentFilter``sessionFilter``cleanupAfterResolve`

 `enabled` 未设置或为 `"auto"`,并且至少可以从 `execApprovals.approvers`  `commands.ownerAllowFrom` 解析出一个批准者时,Discord 会自动启用原生 exec 批准。Discord 不会从渠道 `allowFrom`、旧版 `dm.allowFrom` 或直接消息 `defaultTo` 推断 exec 批准者。设置 `enabled: false` 可显式禁用 Discord 作为原生批准客户端。

对于 `/diagnostics`  `/export-trajectory` 等敏感的仅所有者群组命令,OpenClaw 会私下发送批准提示和最终结果。当调用命令的所有者有 Discord 所有者路由时,它会优先尝试 Discord 私信;如果不可用,则回退到 `commands.ownerAllowFrom` 中第一个可用的所有者路由,例如 Telegram。

 `target`  `channel`  `both` 时,批准提示会在渠道中可见。只有已解析的批准者可以使用按钮;其他用户会收到临时拒绝。批准提示会包含命令文本,因此仅应在受信任渠道中启用渠道投递。如果无法从会话键推导出渠道 ID,OpenClaw 会回退到私信投递。

Discord 还会渲染其他聊天渠道使用的共享批准按钮。原生 Discord 适配器主要添加批准者私信路由和渠道扇出。
当这些按钮存在时,它们就是主要批准 UX;OpenClaw
仅应在工具结果表示
聊天批准不可用或手动批准是唯一路径时,才包含手动 `/approve` 命令。
如果 Discord 原生批准运行时未激活,OpenClaw 会保留
本地确定性的 `/approve <id> <decision>` 提示可见。如果
运行时已激活,但原生卡片无法投递到任何目标,
OpenClaw 会在同一聊天中发送回退通知,其中包含待处理批准里的确切 `/approve`
命令。

Gateway 网关身份验证和批准解析遵循共享 Gateway 网关客户端契约(`plugin:` ID 通过 `plugin.approval.resolve` 解析;其他 ID 通过 `exec.approval.resolve` 解析)。批准默认在 30 分钟后过期。

参见 [Exec 批准](/zh-CN/tools/exec-approvals)


工具和操作门控

Discord 消息操作包括消息、渠道管理、审核、在线状态和元数据操作。

核心示例:

  • 消息:sendMessagereadMessageseditMessagedeleteMessagethreadReply
  • 反应:reactreactionsemojiList
  • 审核:timeoutkickban
  • 在线状态:setPresence

event-create 操作接受可选的 image 参数(URL 或本地文件路径),用于设置定时事件封面图像。

操作门控位于 channels.discord.actions.* 下。

默认门控行为:

操作组 默认值
reactions, messages, threads, pins, polls, search, memberInfo, roleInfo, channelInfo, channels, voiceStatus, events, stickers, emojiUploads, stickerUploads, permissions enabled
roles disabled
moderation disabled
presence disabled

Components v2 UI

OpenClaw 将 Discord components v2 用于 exec 批准和跨上下文标记。Discord 消息操作也可以接受 components 来实现自定义 UI(高级;需要通过 discord 工具构造组件 payload),旧版 embeds 仍可使用,但不推荐。

  • channels.discord.ui.components.accentColor 设置 Discord 组件容器使用的强调色(十六进制)。
  • 使用 channels.discord.accounts.<id>.ui.components.accentColor 按账户设置。
  • 当 components v2 存在时,会忽略 embeds

示例:

{
  channels: {
    discord: {
      ui: {
        components: {
          accentColor: "#5865F2",
        },
      },
    },
  },
}

语音

Discord 有两个不同的语音表面:实时语音频道(连续对话)和语音消息附件(波形预览格式)。Gateway 网关支持两者。

语音频道

设置检查清单:

  1. 在 Discord Developer Portal 中启用 Message Content Intent。
  2. 使用角色/用户允许列表时,启用 Server Members Intent。
  3. 使用 botapplications.commands scopes 邀请机器人。
  4. 在目标语音频道中授予 Connect、Speak、Send Messages 和 Read Message History 权限。
  5. 启用原生命令(commands.nativechannels.discord.commands.native)。
  6. 配置 channels.discord.voice

使用 /vc join|leave|status 控制会话。该命令使用账户默认智能体,并遵循与其他 Discord 命令相同的允许列表和群组策略规则。

/vc join channel:<voice-channel-id>
/vc status
/vc leave

要在加入前检查机器人的有效权限,请运行:

openclaw channels capabilities --channel discord --target channel:<voice-channel-id>

自动加入示例:

{
  channels: {
    discord: {
      voice: {
        enabled: true,
        model: "openai-codex/gpt-5.5",
        autoJoin: [
          {
            guildId: "123456789012345678",
            channelId: "234567890123456789",
          },
        ],
        allowedChannels: [
          {
            guildId: "123456789012345678",
            channelId: "234567890123456789",
          },
        ],
        daveEncryption: true,
        decryptionFailureTolerance: 24,
        connectTimeoutMs: 30000,
        reconnectGraceMs: 15000,
        realtime: {
          provider: "openai",
          model: "gpt-realtime-2",
          voice: "cedar",
        },
      },
    },
  },
}

注意事项:

  • voice.tts 仅会为 stt-tts 语音播放覆盖 messages.tts。实时模式使用 voice.realtime.voice
  • voice.mode 控制对话路径。默认值是 agent-proxy:实时语音前端负责轮次时序、中断和播放,通过 openclaw_agent_consult 将实质性工作委托给路由到的 OpenClaw 智能体,并像处理来自该说话者的已输入 Discord 提示一样处理结果。stt-tts 保留较旧的批处理 STT 加 TTS 流程。bidi 让实时模型直接对话,同时暴露 openclaw_agent_consult 作为 OpenClaw 大脑。
  • voice.agentSession 控制哪个 OpenClaw 对话接收语音轮次。保持未设置时使用语音频道自己的会话,或者设置 { mode: "target", target: "channel:<text-channel-id>" },让语音频道作为现有 Discord 文本频道会话(例如 #maintainers)的麦克风/扬声器扩展。
  • voice.model 会覆盖 Discord 语音响应和实时咨询所用的 OpenClaw 智能体大脑。保持未设置时继承路由到的智能体模型。它与 voice.realtime.model 是分开的。
  • agent-proxy 通过 discord-voice 路由语音,这会保留说话者和目标会话的正常所有者/工具授权,但会隐藏智能体 tts 工具,因为 Discord 语音拥有播放权。默认情况下,agent-proxy 会为所有者说话者提供等同所有者的完整工具访问权限(voice.realtime.toolPolicy: "owner"),并强烈倾向于在给出实质性回答前咨询 OpenClaw 智能体(voice.realtime.consultPolicy: "always")。在默认的 always 模式中,实时层不会在咨询答案前自动说填充内容;它会捕获并转写语音,然后说出路由后的 OpenClaw 答案。如果 Discord 仍在播放第一个答案时有多个强制咨询答案完成,后续的精确语音答案会排队,直到播放空闲,而不是在句子中途替换语音。
  • stt-tts 模式中,STT 使用 tools.media.audiovoice.model 不影响转写。
  • 在实时模式中,voice.realtime.providervoice.realtime.modelvoice.realtime.voice 配置实时音频会话。对于 OpenAI Realtime 2 加 Codex 大脑,使用 voice.realtime.model: "gpt-realtime-2"voice.model: "openai-codex/gpt-5.5"
  • OpenAI 实时提供商接受当前的 Realtime 2 事件名称,以及用于输出音频和转写事件的旧版 Codex 兼容别名,因此兼容的提供商快照可以发生偏移而不会丢失助手音频。
  • voice.realtime.bargeIn 控制 Discord 说话者开始事件是否中断活跃的实时播放。如果未设置,它会跟随实时提供商的输入音频中断设置。
  • voice.realtime.minBargeInAudioEndMs 控制 OpenAI 实时打断截断音频前的最短助手播放时长。默认值:250。在低回声房间中设置为 0 可立即中断,或者在回声较重的扬声器设置中调高它。
  • 对于 Discord 播放中的 OpenAI 语音,设置 voice.tts.provider: "openai",并在 voice.tts.openai.voicevoice.tts.providers.openai.voice 下选择 Text-to-speech 语音。在当前 OpenAI TTS 模型上,cedar 是一个不错的偏男性声音选择。
  • 每个频道的 Discord systemPrompt 覆盖会应用到该语音频道的语音转写轮次。
  • 语音转写轮次会根据 Discord allowFrom(或 dm.allowFrom)推导所有者状态;非所有者说话者不能访问仅限所有者的工具(例如 gatewaycron)。
  • Discord 语音对于纯文本配置是选择启用的;设置 channels.discord.voice.enabled=true(或保留现有 channels.discord.voice 块)以启用 /vc 命令、语音运行时以及 GuildVoiceStates Gateway 网关意图。
  • channels.discord.intents.voiceStates 可以显式覆盖语音状态意图订阅。保持未设置时,该意图会跟随有效的语音启用状态。
  • 如果 voice.autoJoin 对同一个服务器有多个条目,OpenClaw 会加入该服务器最后配置的频道。
  • voice.allowedChannels 是可选的驻留允许列表。保持未设置时允许 /vc join 加入任何已授权的 Discord 语音频道。设置后,/vc join、启动时自动加入和机器人语音状态移动都将限制到列出的 { guildId, channelId } 条目。将它设置为空数组会拒绝所有 Discord 语音加入。如果 Discord 将机器人移到允许列表之外,OpenClaw 会离开该频道,并在有可用目标时重新加入配置的自动加入目标。
  • voice.daveEncryptionvoice.decryptionFailureTolerance 会透传给 @discordjs/voice 加入选项。
  • 如果未设置,@discordjs/voice 默认值为 daveEncryption=truedecryptionFailureTolerance=24
  • OpenClaw 默认使用纯 JS 的 opusscript 解码器接收 Discord 语音。可选的原生 @discordjs/opus 包会被仓库 pnpm 安装策略忽略,因此普通安装、Docker 通道和无关测试不会编译原生插件。专用语音性能主机可以在安装原生插件后通过 OPENCLAW_DISCORD_OPUS_DECODER=native 选择启用。
  • voice.connectTimeoutMs 控制 /vc join 和自动加入尝试的初始 @discordjs/voice Ready 等待。默认值:30000
  • voice.reconnectGraceMs 控制 OpenClaw 在销毁断开的语音会话前等待其开始重连的时长。默认值:15000
  • stt-tts 模式中,语音播放不会仅仅因为另一个用户开始说话而停止。为避免反馈回路,OpenClaw 会在 TTS 播放期间忽略新的语音捕获;请在播放结束后再说下一轮。实时模式会将说话者开始转发为实时提供商的打断信号。
  • 在实时模式中,扬声器回声进入打开的麦克风可能看起来像打断并中断播放。对于回声较重的 Discord 房间,设置 voice.realtime.providers.openai.interruptResponseOnInputAudio: false,防止 OpenAI 在输入音频上自动中断。如果你仍希望 Discord 说话者开始事件中断活跃播放,请添加 voice.realtime.bargeIn: true。OpenAI 实时桥会将短于 voice.realtime.minBargeInAudioEndMs 的播放截断视为可能的回声/噪声并忽略,将其记录为已跳过,而不是清除 Discord 播放。
  • voice.captureSilenceGraceMs 控制 OpenClaw 在 Discord 报告说话者停止后等待多久才将该音频片段最终确定用于 STT。默认值:2500;如果 Discord 将正常停顿切分成零碎的部分转写,请调高此值。
  • 当 ElevenLabs 是选定的 TTS 提供商时,Discord 语音播放会使用流式 TTS,并从提供商响应流开始。没有流式支持的提供商会回退到合成临时文件路径。
  • OpenClaw 还会监视接收解密失败,并在短时间窗口内重复失败后通过离开/重新加入语音频道自动恢复。
  • 如果更新后接收日志反复显示 DecryptionFailed(UnencryptedWhenPassthroughDisabled),请收集依赖报告和日志。内置的 @discordjs/voice 版本线包含来自 discord.js PR #11449 的上游填充修复,该修复关闭了 discord.js issue #11419。
  • 当 OpenClaw 最终确定捕获到的说话者片段时,The operation was aborted 接收事件是预期行为;它们是详细诊断,不是警告。
  • 详细 Discord 语音日志会为每个接受的说话者片段包含一个有界的单行 STT 转写预览,因此调试时可以同时看到用户侧和智能体回复侧,而不会转储无界转写文本。
  • agent-proxy 模式中,强制咨询回退会跳过可能不完整的转写片段,例如以 ... 结尾的文本或像 and 这样的尾随连接词,以及明显不可操作的结束语,例如“be right back”或“bye”。当这避免了陈旧的排队答案时,日志会显示 forced agent consult skipped reason=...

源码检出的原生 opus 设置:

pnpm install
mise exec node@22 -- pnpm discord:opus:install

当你想要上游 macOS arm64 预构建原生插件时,请为 Gateway 网关使用 Node 22。如果你使用另一个 Node 运行时,选择启用的安装器可能需要本地 node-gyp 源码构建工具链。

安装原生插件后,使用以下命令启动 Gateway 网关:

OPENCLAW_DISCORD_OPUS_DECODER=native pnpm gateway:watch

详细语音日志应显示 discord voice: opus decoder: @discordjs/opus。如果没有选择启用环境变量,或者原生插件缺失或无法在主机上加载,OpenClaw 会记录 discord voice: opus decoder: opusscript,并通过纯 JS 回退继续接收语音。

STT 加 TTS 管道:

  • Discord PCM 捕获会转换为 WAV 临时文件。
  • tools.media.audio 处理 STT,例如 openai/gpt-4o-mini-transcribe
  • 转写会通过 Discord 入口和路由发送,同时响应 LLM 以语音输出策略运行,该策略会隐藏智能体 tts 工具并要求返回文本,因为 Discord 语音拥有最终 TTS 播放权。
  • 设置 voice.model 时,它只会覆盖此语音频道轮次的响应 LLM。
  • voice.tts 会合并并覆盖 messages.tts;支持流式传输的提供商会直接馈送播放器,否则会播放生成的音频文件到已加入的频道。

默认 agent-proxy 语音频道会话示例:

{
  channels: {
    discord: {
      voice: {
        enabled: true,
        model: "openai-codex/gpt-5.5",
        realtime: {
          provider: "openai",
          model: "gpt-realtime-2",
          voice: "cedar",
        },
      },
    },
  },
}

没有 voice.agentSession 块时,每个语音频道都会获得自己的路由 OpenClaw 会话。例如,/vc join channel:234567890123456789 会与该 Discord 语音频道的会话对话。实时模型只是语音前端;实质性请求会交给配置的 OpenClaw 智能体。如果实时模型在未调用咨询工具的情况下生成最终转写,OpenClaw 会强制将咨询作为回退,因此默认行为仍像是在和智能体对话。

旧版 STT 加 TTS 示例:

{
  channels: {
    discord: {
      voice: {
        enabled: true,
        mode: "stt-tts",
        model: "openai/gpt-5.4-mini",
        tts: {
          provider: "openai",
          openai: {
            model: "gpt-4o-mini-tts",
            voice: "cedar",
          },
        },
      },
    },
  },
}

实时 bidi 示例:

{
  channels: {
    discord: {
      voice: {
        enabled: true,
        mode: "bidi",
        model: "openai-codex/gpt-5.5",
        realtime: {
          provider: "openai",
          model: "gpt-realtime-2",
          voice: "cedar",
          toolPolicy: "safe-read-only",
          consultPolicy: "always",
        },
      },
    },
  },
}

语音作为现有 Discord 频道会话的扩展:

{
  channels: {
    discord: {
      voice: {
        enabled: true,
        mode: "agent-proxy",
        model: "openai-codex/gpt-5.5",
        agentSession: {
          mode: "target",
          target: "channel:123456789012345678",
        },
        realtime: {
          provider: "openai",
          model: "gpt-realtime-2",
          voice: "cedar",
        },
      },
    },
  },
}

agent-proxy 模式中,机器人会加入配置的语音频道,但 OpenClaw 智能体轮次使用目标频道的正常路由会话和智能体。实时语音会话会将返回结果说回语音频道。监督智能体仍然可以根据其工具策略使用正常消息工具,包括在合适时发送单独的 Discord 消息。

有用的目标形式:

  • target: "channel:123456789012345678" 通过 Discord 文本频道会话路由。
  • target: "123456789012345678" 会被视为频道目标。
  • target: "dm:123456789012345678"target: "user:123456789012345678" 通过该直接消息会话路由。

回声较重的 OpenAI Realtime 示例:

{
  channels: {
    discord: {
      voice: {
        enabled: true,
        mode: "bidi",
        model: "openai-codex/gpt-5.5",
        realtime: {
          provider: "openai",
          model: "gpt-realtime-2",
          voice: "cedar",
          bargeIn: true,
          minBargeInAudioEndMs: 500,
          consultPolicy: "always",
          providers: {
            openai: {
              interruptResponseOnInputAudio: false,
            },
          },
        },
      },
    },
  },
}

当模型通过打开的麦克风听到自己的 Discord 播放声音,但你仍希望通过说话打断它时,请使用此配置。OpenClaw 会阻止 OpenAI 根据原始输入音频自动打断,而 bargeIn: true 允许 Discord 说话者开始事件和已活跃的说话者音频,在下一个捕获的轮次到达 OpenAI 之前取消活跃的实时响应。audioEndMs 低于 minBargeInAudioEndMs 的很早期抢话信号会被视为可能的回声/噪声并忽略,因此模型不会在第一个播放帧处被截断。

预期语音日志:

  • 加入时:discord voice: joining ... voiceSession=... supervisorSession=... agentSessionMode=... voiceModel=... realtimeModel=...
  • 实时启动时:discord voice: realtime bridge starting ... autoRespond=false interruptResponse=false bargeIn=false minBargeInAudioEndMs=...
  • 说话者音频时:discord voice: realtime speaker turn opened ...discord voice: realtime input audio started ... outputAudioMs=... outputActive=...,以及 discord voice: realtime speaker turn closed ... chunks=... discordBytes=... realtimeBytes=... interruptedPlayback=...
  • 跳过过期语音时:discord voice: realtime forced agent consult skipped reason=incomplete-transcript ...reason=non-actionable-closing ...
  • 实时响应完成时:discord voice: realtime audio playback finishing reason=response.done ... audioMs=... chunks=...
  • 播放停止/重置时:discord voice: realtime audio playback stopped reason=... audioMs=... elapsedMs=... chunks=...
  • 实时咨询时:discord voice: realtime consult requested ... voiceSession=... supervisorSession=... question=...
  • Agent 回答时:discord voice: agent turn answer ...
  • 精确语音入队时:discord voice: realtime exact speech queued ... queued=... outputAudioMs=... outputActive=...,随后是 discord voice: realtime exact speech dequeued reason=player-idle ...
  • 检测到抢话时:discord voice: realtime barge-in detected source=speaker-start ...discord voice: realtime barge-in detected source=active-speaker-audio ...,随后是 discord voice: realtime barge-in requested reason=... outputAudioMs=... outputActive=...
  • 实时中断时:discord voice: realtime model interrupt requested client:response.cancel reason=barge-in,随后是 discord voice: realtime model audio truncated client:conversation.item.truncate reason=barge-in audioEndMs=...discord voice: realtime model interrupt confirmed server:response.done status=cancelled ...
  • 忽略回声/噪声时:discord voice: realtime model interrupt ignored client:conversation.item.truncate.skipped reason=barge-in audioEndMs=0 minAudioEndMs=250
  • 禁用抢话时:discord voice: realtime capture ignored during playback (barge-in disabled) ...
  • 空闲播放时:discord voice: realtime barge-in ignored reason=... outputActive=false ... playbackChunks=0

要调试音频被截断的问题,请把实时语音日志按时间线阅读:

  1. realtime audio playback started 表示 Discord 已开始播放助手音频。网桥会从此时开始统计助手输出分块、Discord PCM 字节、提供商实时字节,以及合成音频时长。
  2. realtime speaker turn opened 标记某个 Discord 说话者变为活跃。如果播放已处于活跃状态且 bargeIn 已启用,后面可能会出现 barge-in detected source=speaker-start
  3. realtime input audio started 标记该说话者轮次收到的第一个实际音频帧。这里的 outputActive=true 或非零 outputAudioMs 表示麦克风在助手播放仍处于活跃状态时发送输入。
  4. barge-in detected source=active-speaker-audio 表示 OpenClaw 在助手播放处于活跃状态时看到了实时说话者音频。这有助于区分真实打断和没有有效音频的 Discord 说话者开始事件。
  5. barge-in requested reason=... 表示 OpenClaw 已要求实时提供商取消或截断活跃响应。它包含 outputAudioMsoutputActiveplaybackChunks,因此你可以看到中断前实际播放了多少助手音频。
  6. realtime audio playback stopped reason=... 是本地 Discord 播放重置点。原因会说明是谁停止了播放:barge-inplayer-idleprovider-clear-audioforced-agent-consultstream-closesession-close
  7. realtime speaker turn closed 汇总捕获的输入轮次。chunks=0hasAudio=false 表示说话者轮次已打开,但没有可用音频到达实时网桥。interruptedPlayback=true 表示该输入轮次与助手输出重叠,并触发了抢话逻辑。

有用字段:

  • outputAudioMs:日志行之前由实时提供商生成的助手音频时长。
  • audioMs:OpenClaw 在播放停止前统计的助手音频时长。
  • elapsedMs:打开和关闭播放流或说话者轮次之间的挂钟时间。
  • discordBytes:发送到 Discord 语音或从 Discord 语音接收的 48 kHz 立体声 PCM 字节。
  • realtimeBytes:发送到实时提供商或从实时提供商接收的提供商格式 PCM 字节。
  • playbackChunks:为活跃响应转发到 Discord 的助手音频分块。
  • sinceLastAudioMs:最后一个捕获的说话者音频帧与说话者轮次关闭之间的间隔。

常见模式:

  • 如果立即截断,并且带有 source=active-speaker-audio、较小的 outputAudioMs,且同一用户在附近,通常说明扬声器回声进入了麦克风。提高 voice.realtime.minBargeInAudioEndMs,降低扬声器音量,使用耳机,或设置 voice.realtime.providers.openai.interruptResponseOnInputAudio: false
  • source=speaker-start 后跟 speaker turn closed ... hasAudio=false 表示 Discord 报告了说话者开始,但没有音频到达 OpenClaw。这可能是短暂的 Discord 语音事件、噪声门行为,或客户端短暂按下麦克风。
  • 如果 audio playback stopped reason=stream-close 附近没有抢话或 provider-clear-audio,表示本地 Discord 播放流意外结束。检查前面的提供商和 Discord 播放器日志。
  • capture ignored during playback (barge-in disabled) 表示 OpenClaw 在助手音频处于活跃状态时有意丢弃了输入。如果你希望语音打断播放,请启用 voice.realtime.bargeIn
  • barge-in ignored ... outputActive=false 表示 Discord 或提供商 VAD 报告了语音,但 OpenClaw 没有活跃播放可供打断。这不应截断音频。

凭证按组件解析:voice.model 使用 LLM 路由凭证,tools.media.audio 使用 STT 凭证,messages.tts/voice.tts 使用 TTS 凭证,voice.realtime.providers 或提供商的常规认证配置使用实时提供商凭证。

语音消息

Discord 语音消息会显示波形预览,并要求使用 OGG/Opus 音频。OpenClaw 会自动生成波形,但需要 Gateway 网关主机上有 ffmpegffprobe 来检查并转换。

  • 提供本地文件路径(URL 会被拒绝)。
  • 省略文本内容(Discord 会拒绝同一 payload 中同时包含文本和语音消息)。
  • 接受任何音频格式;OpenClaw 会按需转换为 OGG/Opus。
message(action="send", channel="discord", target="channel:123", path="/path/to/audio.mp3", asVoice=true)

故障排除


- 启用 Message Content Intent
- 当你依赖用户/成员解析时,启用 Server Members Intent
- 更改 intents 后重启 Gateway 网关

- 验证 `groupPolicy`
- 验证 `channels.discord.guilds` 下的服务器 allowlist
- 如果服务器 `channels` map 存在,则只允许列出的频道
- 验证 `requireMention` 行为和提及模式

有用检查:
openclaw doctor
openclaw channels status --probe
openclaw logs --follow


常见原因:

- `groupPolicy="allowlist"`,但没有匹配的服务器/频道 allowlist
- `requireMention` 配置在错误的位置(必须位于 `channels.discord.guilds` 或频道条目下)
- 发送者被服务器/频道 `users` allowlist 阻止

典型日志:

- `Slow listener detected ...`
- `stuck session: sessionKey=agent:...:discord:... state=processing ...`

Discord Gateway 网关队列旋钮:

- 单账号:`channels.discord.eventQueue.listenerTimeout`
- 多账号:`channels.discord.accounts.<accountId>.eventQueue.listenerTimeout`
- 这只控制 Discord Gateway 网关监听器工作,不控制 agent 轮次生命周期

Discord 不会对排队的 agent 轮次应用频道拥有的超时。消息监听器会立即交接,排队的 Discord 运行会保留每个会话的顺序,直到会话/工具/运行时生命周期完成或中止工作。
{
  channels: {
    discord: {
      accounts: {
        default: {
          eventQueue: {
            listenerTimeout: 120000,
          },
        },
      },
    },
  },
}


OpenClaw 在连接前会获取 Discord /gateway/bot 元数据。短暂失败会回退到 Discord 的默认 Gateway 网关 URL,并在日志中进行限速。

元数据超时旋钮:

- 单账号:`channels.discord.gatewayInfoTimeoutMs`
- 多账号:`channels.discord.accounts.<accountId>.gatewayInfoTimeoutMs`
- 配置未设置时的环境变量回退:`OPENCLAW_DISCORD_GATEWAY_INFO_TIMEOUT_MS`
- 默认值:`30000`(30 秒),最大值:`120000`


OpenClaw 在启动期间和运行时重连之后等待 Discord 的 Gateway 网关 READY 事件。带有启动错峰的多账号设置可能需要比默认值更长的启动 READY 窗口。

READY 超时旋钮:

- 启动单账号:`channels.discord.gatewayReadyTimeoutMs`
- 启动多账号:`channels.discord.accounts.<accountId>.gatewayReadyTimeoutMs`
- 配置未设置时的启动环境变量回退:`OPENCLAW_DISCORD_READY_TIMEOUT_MS`
- 启动默认值:`15000`(15 秒),最大值:`120000`
- 运行时单账号:`channels.discord.gatewayRuntimeReadyTimeoutMs`
- 运行时多账号:`channels.discord.accounts.<accountId>.gatewayRuntimeReadyTimeoutMs`
- 配置未设置时的运行时环境变量回退:`OPENCLAW_DISCORD_RUNTIME_READY_TIMEOUT_MS`
- 运行时默认值:`30000`(30 秒),最大值:`120000`


channels status --probe 权限检查仅适用于数字频道 ID。

如果你使用 slug 键,运行时匹配仍可工作,但 probe 无法完整验证权限。

- 私信已禁用:`channels.discord.dm.enabled=false`
- 私信策略已禁用:`channels.discord.dmPolicy="disabled"`(旧版:`channels.discord.dm.policy`
-  `pairing` 模式中等待配对批准


默认情况下,机器人编写的消息会被忽略。

如果你设置了 `channels.discord.allowBots=true`,请使用严格的提及和允许列表规则,避免循环行为。
优先使用 `channels.discord.allowBots="mentions"`,以便只接收提及该 bot 的 bot 消息。
{
  channels: {
    discord: {
      accounts: {
        mantis: {
          // Mantis listens to other bots only when they mention her.
          allowBots: "mentions",
        },
        molty: {
          // Molty listens to all bot-authored Discord messages.
          allowBots: true,
          mentionAliases: {
            // Lets Molty write "@Mantis" and send a real Discord mention.
            Mantis: "MANTIS_DISCORD_USER_ID",
          },
        },
      },
    },
  },
}

- 保持 OpenClaw 为当前版本(`openclaw update`),确保 Discord 语音接收恢复逻辑可用
- 确认 `channels.discord.voice.daveEncryption=true`(默认
-  `channels.discord.voice.decryptionFailureTolerance=24`(上游默认值开始仅在需要时调整
- 查看日志中的
  - `discord voice: DAVE decrypt failures detected`
  - `discord voice: repeated decrypt failures; attempting rejoin`
- 如果自动重新加入后故障仍然持续请收集日志并与 [discord.js #11419](https://github.com/discordjs/discord.js/issues/11419)  [discord.js #11449](https://github.com/discordjs/discord.js/pull/11449) 中的上游 DAVE 接收历史进行对比


配置参考

主要参考:Configuration reference - Discord

  • 启动/认证:enabledtokenaccounts.*allowBots
  • 策略:groupPolicydm.*guilds.*guilds.*.channels.*
  • 命令:commands.nativecommands.useAccessGroupsconfigWritesslashCommand.*
  • 事件队列:eventQueue.listenerTimeout(监听器预算)、eventQueue.maxQueueSizeeventQueue.maxConcurrency
  • Gateway 网关:gatewayInfoTimeoutMsgatewayReadyTimeoutMsgatewayRuntimeReadyTimeoutMs
  • 回复/历史:replyToModehistoryLimitdmHistoryLimitdms.*.historyLimit
  • 递送:textChunkLimitchunkModemaxLinesPerMessage
  • 流式传输:streaming(旧版别名:streamMode)、streaming.preview.toolProgressdraftChunkblockStreamingblockStreamingCoalesce
  • 媒体/重试:mediaMaxMb(限制出站 Discord 上传,默认 100MB)、retry
  • 操作:actions.*
  • 在线状态:activitystatusactivityTypeactivityUrl
  • UI:ui.components.accentColor
  • 功能:threadBindings、顶层 bindings[]type: "acp")、pluralkitexecApprovalsintentsagentComponentsheartbeatresponsePrefix

安全和运维

  • 将 bot token 视为密钥(在受监督环境中优先使用 DISCORD_BOT_TOKEN)。
  • 授予最小权限的 Discord 权限。
  • 如果命令部署/状态已过期,请重启 Gateway 网关,并使用 openclaw channels status --probe 重新检查。

相关



将 Discord 用户与 Gateway 网关配对。


群聊和允许列表行为。


将入站消息路由到智能体。


威胁模型和加固。


将服务器和频道映射到智能体。


原生命令行为。


📄 Feishu

原文:https://docs.openclaw.ai/zh-CN/channels/feishu

Feishu/Lark 是一个一体化协作平台,团队可以在其中聊天、共享文档、管理日历并共同完成工作。

Status: 已可用于生产,支持机器人私信 + 群聊。WebSocket 是默认模式;webhook 模式是可选的。


快速开始


需要 OpenClaw 2026.4.25 或更高版本。运行 openclaw --version 检查。使用 openclaw update 升级。



bash
openclaw channels login --channel feishu

选择手动设置以粘贴来自 Feishu Open Platform 的 App ID 和 App Secret,或选择二维码设置以自动创建机器人。如果国内版 Feishu 移动应用对二维码没有反应,请重新运行设置并选择手动设置。


bash
openclaw gateway restart



访问控制

直接消息

配置 dmPolicy 以控制谁可以向机器人发送私信:

  • "pairing" - 未知用户会收到配对码;通过 CLI 批准
  • "allowlist" - 只有 allowFrom 中列出的用户可以聊天(默认:仅机器人所有者)
  • "open" - 仅当 allowFrom 包含 "*" 时允许公开私信;如果是限制性条目,则只有匹配的用户可以聊天
  • "disabled" - 禁用所有私信

批准配对请求:

openclaw pairing list feishu
openclaw pairing approve feishu <CODE>

群聊

群组策略channels.feishu.groupPolicy):

行为
"open" 回复群组中的所有消息
"allowlist" 仅回复 groupAllowFrom 中的群组,或在 groups.<chat_id> 下显式配置的群组
"disabled" 禁用所有群组消息;显式 groups.<chat_id> 条目不会覆盖此设置

默认值:allowlist

提及要求channels.feishu.requireMention):

  • true - 需要 @提及(默认)
  • false - 无需 @提及即可回复
  • 按群组覆盖:channels.feishu.groups.<chat_id>.requireMention
  • 仅广播的 @all@_all 不会被视为机器人提及。同时提及 @all 和机器人的消息仍算作机器人提及。

群组配置示例

允许所有群组,无需 @提及

{
  channels: {
    feishu: {
      groupPolicy: "open",
    },
  },
}

允许所有群组,仍需要 @提及

{
  channels: {
    feishu: {
      groupPolicy: "open",
      requireMention: true,
    },
  },
}

仅允许特定群组

{
  channels: {
    feishu: {
      groupPolicy: "allowlist",
      // Group IDs look like: oc_xxx
      groupAllowFrom: ["oc_xxx", "oc_yyy"],
    },
  },
}

allowlist 模式下,你也可以通过添加显式 groups.<chat_id> 条目来准入某个群组。显式条目不会覆盖 groupPolicy: "disabled"groups.* 下的通配符默认值会配置匹配的群组,但它们本身不会准入群组。

{
  channels: {
    feishu: {
      groupPolicy: "allowlist",
      groups: {
        oc_xxx: {
          requireMention: false,
        },
      },
    },
  },
}

限制群组内的发送者

{
  channels: {
    feishu: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["oc_xxx"],
      groups: {
        oc_xxx: {
          // User open_ids look like: ou_xxx
          allowFrom: ["ou_user1", "ou_user2"],
        },
      },
    },
  },
}

获取群组/用户 ID

群组 ID(chat_id,格式:oc_xxx

在 Feishu/Lark 中打开群组,点击右上角的菜单图标,然后前往设置。群组 ID(chat_id)会列在设置页面上。

获取群组 ID

用户 ID(open_id,格式:ou_xxx

启动 Gateway 网关,向机器人发送私信,然后检查日志:

openclaw logs --follow

在日志输出中查找 open_id。你也可以检查待处理的配对请求:

openclaw pairing list feishu

常用命令

命令 描述
/status 显示机器人状态
/reset 重置当前会话
/model 显示或切换 AI 模型


Feishu/Lark 不支持原生斜杠菜单,因此请将这些命令作为纯文本消息发送。


故障排除

机器人在群聊中不回复

  1. 确保机器人已添加到群组
  2. 确保你 @提及机器人(默认需要)
  3. 确认 groupPolicy 不是 "disabled"
  4. 检查日志:openclaw logs --follow

机器人收不到消息

  1. 确保机器人已在 Feishu Open Platform / Lark Developer 中发布并获批
  2. 确保事件订阅包含 im.message.receive_v1
  3. 确保已选择长连接(WebSocket)
  4. 确保已授予所有必需的权限范围
  5. 确保 Gateway 网关正在运行:openclaw gateway status
  6. 检查日志:openclaw logs --follow

Feishu 移动应用中的二维码设置没有反应

  1. 重新运行设置:openclaw channels login --channel feishu
  2. 选择手动设置
  3. 在 Feishu Open Platform 中创建一个自建应用,并复制其 App ID 和 App Secret
  4. 将这些凭据粘贴到设置向导中

App Secret 已泄露

  1. 在 Feishu Open Platform / Lark Developer 中重置 App Secret
  2. 更新你的配置中的值
  3. 重启 Gateway 网关:openclaw gateway restart

高级配置

多账户

{
  channels: {
    feishu: {
      defaultAccount: "main",
      accounts: {
        main: {
          appId: "cli_xxx",
          appSecret: "xxx",
          name: "Primary bot",
          tts: {
            providers: {
              openai: { voice: "shimmer" },
            },
          },
        },
        backup: {
          appId: "cli_yyy",
          appSecret: "yyy",
          name: "Backup bot",
          enabled: false,
        },
      },
    },
  },
}

defaultAccount 控制在出站 API 未指定 accountId 时使用哪个账户。
accounts.<id>.tts 使用与 messages.tts 相同的结构,并深度合并到
全局 TTS 配置之上,因此多机器人 Feishu 设置可以在全局保留共享的提供商
凭据,同时按账户仅覆盖语音、模型、人设或自动模式。

消息限制

  • textChunkLimit - 出站文本分块大小(默认:2000 个字符)
  • mediaMaxMb - 媒体上传/下载限制(默认:30 MB)

流式传输

Feishu/Lark 支持通过交互式卡片进行流式回复。启用后,机器人会在生成文本时实时更新卡片。

{
  channels: {
    feishu: {
      streaming: true, // enable streaming card output (default: true)
      blockStreaming: true, // opt into completed-block streaming
    },
  },
}

设置 streaming: false 可在一条消息中发送完整回复。blockStreaming 默认关闭;仅当你希望在最终回复前刷新已完成的助手分块时启用它。

配额优化

通过两个可选标志减少 Feishu/Lark API 调用次数:

  • typingIndicator(默认 true):设置为 false 可跳过输入中反应调用
  • resolveSenderNames(默认 true):设置为 false 可跳过发送者资料查询
{
  channels: {
    feishu: {
      typingIndicator: false,
      resolveSenderNames: false,
    },
  },
}

ACP 会话

Feishu/Lark 支持用于私信和群组线程消息的 ACP。Feishu/Lark ACP 由文本命令驱动,没有原生斜杠菜单,因此请直接在对话中使用 /acp ... 消息。

持久 ACP 绑定

{
  agents: {
    list: [
      {
        id: "codex",
        runtime: {
          type: "acp",
          acp: {
            agent: "codex",
            backend: "acpx",
            mode: "persistent",
            cwd: "/workspace/openclaw",
          },
        },
      },
    ],
  },
  bindings: [
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "feishu",
        accountId: "default",
        peer: { kind: "direct", id: "ou_1234567890" },
      },
    },
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "feishu",
        accountId: "default",
        peer: { kind: "group", id: "oc_group_chat:topic:om_topic_root" },
      },
      acp: { label: "codex-feishu-topic" },
    },
  ],
}

从聊天中生成 ACP

在 Feishu/Lark 私信或线程中:

/acp spawn codex --thread here

--thread here 适用于私信和 Feishu/Lark 线程消息。绑定对话中的后续消息会直接路由到该 ACP 会话。

多 Agent 路由

使用 bindings 将 Feishu/Lark 私信或群组路由到不同的智能体。

{
  agents: {
    list: [
      { id: "main" },
      { id: "agent-a", workspace: "/home/user/agent-a" },
      { id: "agent-b", workspace: "/home/user/agent-b" },
    ],
  },
  bindings: [
    {
      agentId: "agent-a",
      match: {
        channel: "feishu",
        peer: { kind: "direct", id: "ou_xxx" },
      },
    },
    {
      agentId: "agent-b",
      match: {
        channel: "feishu",
        peer: { kind: "group", id: "oc_zzz" },
      },
    },
  ],
}

路由字段:

  • match.channel"feishu"
  • match.peer.kind"direct"(私信)或 "group"(群聊)
  • match.peer.id:用户 Open ID(ou_xxx)或群组 ID(oc_xxx

请参阅获取群组/用户 ID了解查询提示。


配置参考

完整配置:Gateway 网关配置

设置 描述 默认值
channels.feishu.enabled 启用/禁用该渠道 true
channels.feishu.domain API 域(feishulark feishu
channels.feishu.connectionMode 事件传输(websocketwebhook websocket
channels.feishu.defaultAccount 出站路由的默认账号 default
channels.feishu.verificationToken webhook 模式必需 -
channels.feishu.encryptKey webhook 模式必需 -
channels.feishu.webhookPath Webhook 路由路径 /feishu/events
channels.feishu.webhookHost Webhook 绑定主机 127.0.0.1
channels.feishu.webhookPort Webhook 绑定端口 3000
channels.feishu.accounts.<id>.appId App ID -
channels.feishu.accounts.<id>.appSecret App Secret -
channels.feishu.accounts.<id>.domain 单账号域覆盖 feishu
channels.feishu.accounts.<id>.tts 单账号 TTS 覆盖 messages.tts
channels.feishu.dmPolicy 私信策略 allowlist
channels.feishu.allowFrom 私信允许列表(open_id 列表) [BotOwnerId]
channels.feishu.groupPolicy 群组策略 allowlist
channels.feishu.groupAllowFrom 群组允许列表 -
channels.feishu.requireMention 群组中需要 @提及 true
channels.feishu.groups.<chat_id>.requireMention 单群组 @提及覆盖;显式 ID 也会在允许列表模式下准入该群组 inherited
channels.feishu.groups.<chat_id>.enabled 启用/禁用特定群组 true
channels.feishu.textChunkLimit 消息分块大小 2000
channels.feishu.mediaMaxMb 媒体大小限制 30
channels.feishu.streaming 流式卡片输出 true
channels.feishu.blockStreaming 已完成分块回复流式传输 false
channels.feishu.typingIndicator 发送正在输入反应 true
channels.feishu.resolveSenderNames 解析发送者显示名称 true

支持的消息类型

接收

  • ✅ 文本
  • ✅ 富文本(post)
  • ✅ 图片
  • ✅ 文件
  • ✅ 音频
  • ✅ 视频/媒体
  • ✅ 贴纸

入站 Feishu/Lark 音频消息会规范化为媒体占位符,而不是原始 file_key JSON。当配置了 tools.media.audio 时,OpenClaw 会下载语音笔记资源,并在智能体轮次之前运行共享音频转写,因此智能体会收到语音转写文本。如果 Feishu 直接在音频载荷中包含转写文本,则会使用该文本,而不会再次调用 ASR。没有音频转写提供商时,智能体仍会收到 <media:audio> 占位符和已保存的附件,而不是原始 Feishu 资源载荷。

发送

  • ✅ 文本
  • ✅ 图片
  • ✅ 文件
  • ✅ 音频
  • ✅ 视频/媒体
  • ✅ 交互式卡片(包括流式更新)
  • ⚠️ 富文本(post 风格格式;不支持完整的 Feishu/Lark 创作能力)

原生 Feishu/Lark 音频气泡使用 Feishu audio 消息类型,并需要 Ogg/Opus 上传媒体(file_type: "opus")。现有 .opus.ogg 媒体会直接作为原生音频发送。只有当回复请求语音投递(audioAsVoice / 消息工具 asVoice,包括 TTS 语音笔记回复)时,MP3/WAV/M4A 和其他可能的音频格式才会使用 ffmpeg 转码为 48kHz Ogg/Opus。普通 MP3 附件仍作为常规文件。如果缺少 ffmpeg 或转换失败,OpenClaw 会回退为文件附件并记录原因。

线程和回复

  • ✅ 内联回复
  • ✅ 线程回复
  • ✅ 回复线程消息时,媒体回复仍保持线程感知

对于 groupSessionScope: "group_topic""group_topic_sender",原生 Feishu/Lark 话题群组使用事件 thread_idomt_*)作为规范话题会话键。如果原生话题起始事件省略 thread_id,OpenClaw 会在路由该轮次之前从 Feishu 补全它。OpenClaw 转换为线程的普通群组回复会继续使用回复根消息 ID(om_*),因此第一轮和后续轮次会保留在同一会话中。


相关


📄 Google Chat

原文:https://docs.openclaw.ai/zh-CN/channels/googlechat

Status:可下载插件,通过 Google Chat API webhooks 支持私信 + 空间(仅 HTTP)。

安装

在配置渠道之前安装 Google Chat:

openclaw plugins install @openclaw/googlechat

本地检出(从 git 仓库运行时):

openclaw plugins install ./path/to/local/googlechat-plugin

快速设置(初学者)

  1. 创建 Google Cloud 项目并启用 Google Chat API
    - 前往:Google Chat API Credentials
    - 如果尚未启用该 API,请启用它。
  2. 创建 Service Account
    - 点击 Create Credentials > Service Account
    - 按你的需要命名(例如 openclaw-chat)。
    - 权限留空(点击 Continue)。
    - 有访问权限的主体留空(点击 Done)。
  3. 创建并下载 JSON Key
    - 在服务账号列表中,点击你刚创建的账号。
    - 前往 Keys 标签页。
    - 点击 Add Key > Create new key
    - 选择 JSON 并点击 Create
  4. 将下载的 JSON 文件存储在你的 Gateway 网关主机上(例如 ~/.openclaw/googlechat-service-account.json)。
  5. Google Cloud Console Chat Configuration 中创建 Google Chat 应用:
    - 填写 Application info

    • App name:(例如 OpenClaw
    • Avatar URL:(例如 https://openclaw.ai/logo.png
    • Description:(例如 Personal AI Assistant
    • 启用 Interactive features
    • Functionality 下,勾选 Join spaces and group conversations
    • Connection settings 下,选择 HTTP endpoint URL
    • Triggers 下,选择 Use a common HTTP endpoint URL for all triggers,并将其设置为你的 Gateway 网关公共 URL,后接 /googlechat
    • 提示:运行 openclaw status 查找你的 Gateway 网关公共 URL。
    • Visibility 下,勾选 Make this Chat app available to specific people and groups in <Your Domain>
    • 在文本框中输入你的电子邮件地址(例如 user@example.com)。
    • 点击底部的 Save
  6. 启用应用状态
    - 保存后,刷新页面
    - 查找 App status 部分(保存后通常在页面顶部或底部附近)。
    - 将状态更改为 Live - available to users
    - 再次点击 Save
  7. 使用服务账号路径 + webhook audience 配置 OpenClaw:
    - 环境变量:GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json
    - 或配置:channels.googlechat.serviceAccountFile: "/path/to/service-account.json"
  8. 设置 webhook audience 类型 + 值(与你的 Chat 应用配置匹配)。
  9. 启动 Gateway 网关。Google Chat 会向你的 webhook 路径发送 POST 请求。

添加到 Google Chat

Gateway 网关运行且你的电子邮件已添加到可见性列表后:

  1. 前往 Google Chat
  2. 点击 Direct Messages 旁边的 +(加号)图标。
  3. 在搜索栏(你通常添加人员的位置)中,输入你在 Google Cloud Console 中配置的 App name
    - 注意:这个 bot 不会出现在“Marketplace”浏览列表中,因为它是私有应用。你必须按名称搜索它。
  4. 从结果中选择你的 bot。
  5. 点击 AddChat 开始一对一对话。
  6. 发送“Hello”以触发助手!

公共 URL(仅 Webhook)

Google Chat webhooks 需要公共 HTTPS endpoint。出于安全考虑,只将 /googlechat 路径暴露到互联网。将 OpenClaw dashboard 和其他敏感 endpoint 保留在你的私有网络中。

选项 A:Tailscale Funnel(推荐)

使用 Tailscale Serve 处理私有 dashboard,并使用 Funnel 处理公共 webhook 路径。这样会保持 / 私有,同时只暴露 /googlechat

  1. 检查你的 Gateway 网关绑定到了什么地址:

bash
ss -tlnp | grep 18789

记下 IP 地址(例如 127.0.0.10.0.0.0,或你的 Tailscale IP,如 100.x.x.x)。

  1. 仅向 tailnet 暴露 dashboard(端口 8443):

```bash
# If bound to localhost (127.0.0.1 or 0.0.0.0):
tailscale serve --bg --https 8443 http://127.0.0.1:18789

# If bound to Tailscale IP only (e.g., 100.106.161.80):
tailscale serve --bg --https 8443 http://100.106.161.80:18789
```

  1. 仅公开暴露 webhook 路径:

```bash
# If bound to localhost (127.0.0.1 or 0.0.0.0):
tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat

# If bound to Tailscale IP only (e.g., 100.106.161.80):
tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat
```

  1. 授权该节点使用 Funnel 访问:
    如果系统提示,请访问输出中显示的授权 URL,在你的 tailnet 策略中为此节点启用 Funnel。

  2. 验证配置:

bash
tailscale serve status
tailscale funnel status

你的公共 webhook URL 将是:
https://<node-name>.<tailnet>.ts.net/googlechat

你的私有 dashboard 保持仅 tailnet 可访问:
https://<node-name>.<tailnet>.ts.net:8443/

在 Google Chat 应用配置中使用公共 URL(不带 :8443)。

注意:此配置会在重启后保留。之后若要移除它,请运行 tailscale funnel resettailscale serve reset

选项 B:反向代理(Caddy)

如果你使用 Caddy 这样的反向代理,只代理特定路径:

your-domain.com {
    reverse_proxy /googlechat* localhost:18789
}

使用此配置时,对 your-domain.com/ 的任何请求都会被忽略或返回 404,而 your-domain.com/googlechat 会被安全地路由到 OpenClaw。

选项 C:Cloudflare Tunnel

配置你的 tunnel ingress 规则,使其只路由 webhook 路径:

  • Path/googlechat -> http://localhost:18789/googlechat
  • Default Rule:HTTP 404(Not Found)

工作原理

  1. Google Chat 向 Gateway 网关发送 webhook POST。每个请求都包含 Authorization: Bearer <token> header。
    - 当 header 存在时,OpenClaw 会在读取/解析完整 webhook body 之前验证 bearer auth。
    - 支持在 body 中携带 authorizationEventObject.systemIdToken 的 Google Workspace Add-on 请求,并使用更严格的预认证 body 预算。
  2. OpenClaw 根据配置的 audienceType + audience 验证 token:
    - audienceType: "app-url" → audience 是你的 HTTPS webhook URL。
    - audienceType: "project-number" → audience 是 Cloud 项目编号。
  3. 消息按空间路由:
    - 私信使用会话键 agent:<agentId>:googlechat:direct:<spaceId>
    - 空间使用会话键 agent:<agentId>:googlechat:group:<spaceId>
  4. 默认情况下,私信访问需要配对。未知发送者会收到配对码;使用以下命令批准:
    - openclaw pairing approve googlechat <code>
  5. 群组空间默认需要 @ 提及。如果提及检测需要应用的用户名,请使用 botUser

目标

使用这些标识符进行投递和 allowlists:

  • 私信:users/<userId>(推荐)。
  • 原始电子邮件 name@example.com 是可变的,并且仅在 channels.googlechat.dangerouslyAllowNameMatching: true 时用于直接 allowlist 匹配。
  • 已弃用:users/<email> 被视为用户 ID,而不是电子邮件 allowlist。
  • 空间:spaces/<spaceId>

配置要点

{
  channels: {
    googlechat: {
      enabled: true,
      serviceAccountFile: "/path/to/service-account.json",
      // or serviceAccountRef: { source: "file", provider: "filemain", id: "/channels/googlechat/serviceAccount" }
      audienceType: "app-url",
      audience: "https://gateway.example.com/googlechat",
      webhookPath: "/googlechat",
      botUser: "users/1234567890", // optional; helps mention detection
      dm: {
        policy: "pairing",
        allowFrom: ["users/1234567890"],
      },
      groupPolicy: "allowlist",
      groups: {
        "spaces/AAAA": {
          enabled: true,
          requireMention: true,
          users: ["users/1234567890"],
          systemPrompt: "Short answers only.",
        },
      },
      actions: { reactions: true },
      typingIndicator: "message",
      mediaMaxMb: 20,
    },
  },
}

说明:

  • 服务账号凭据也可以通过 serviceAccount(JSON 字符串)内联传递。
  • 也支持 serviceAccountRef(环境变量/文件 SecretRef),包括 channels.googlechat.accounts.<id>.serviceAccountRef 下的按账号 refs。
  • 如果未设置 webhookPath,默认 webhook 路径为 /googlechat
  • dangerouslyAllowNameMatching 会重新启用可变电子邮件主体匹配以用于 allowlists(应急兼容模式)。
  • 启用 actions.reactions 后,可以通过 reactions 工具和 channels action 使用 reactions。
  • 消息操作暴露 send 用于文本,暴露 upload-file 用于显式附件发送。upload-file 接受 media / filePath / path,以及可选的 messagefilename 和 thread 目标。
  • typingIndicator 支持 nonemessage(默认)和 reaction(reaction 需要用户 OAuth)。
  • 附件通过 Chat API 下载,并存储在媒体 pipeline 中(大小受 mediaMaxMb 限制)。

Secrets 引用详情:Secrets Management

故障排除

405 Method Not Allowed

如果 Google Cloud Logs Explorer 显示如下错误:

status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed

这表示 webhook handler 未注册。常见原因:

  1. 渠道未配置:你的配置中缺少 channels.googlechat 部分。使用以下命令验证:

bash
openclaw config get channels.googlechat

如果返回“Config path not found”,请添加配置(参见配置要点)。

  1. 插件未启用:检查插件状态:

bash
openclaw plugins list | grep googlechat

如果显示“disabled”,请将 plugins.entries.googlechat.enabled: true 添加到你的配置。

  1. Gateway 网关未重启:添加配置后,重启 Gateway 网关:

bash
openclaw gateway restart

验证渠道正在运行:

openclaw channels status
# Should show: Google Chat default: enabled, configured, ...

其他问题

  • 检查 openclaw channels status --probe,查看认证错误或缺失的 audience 配置。
  • 如果没有收到消息,请确认 Chat 应用的 webhook URL + event subscriptions。
  • 如果提及门控阻止回复,请将 botUser 设置为应用的用户资源名称,并验证 requireMention
  • 发送测试消息时使用 openclaw logs --follow,查看请求是否到达 Gateway 网关。

相关文档:

相关


📄 iMessage

原文:https://docs.openclaw.ai/zh-CN/channels/imessage


对于 OpenClaw iMessage 部署,请在已登录的 macOS Messages 主机上使用 imsg。如果你的 Gateway 网关运行在 Linux 或 Windows 上,请将 channels.imessage.cliPath 指向一个 SSH 包装脚本,由它在 Mac 上运行 imsg

Gateway 网关停机后的补齐是选择启用的。 启用后(channels.imessage.catchup.enabled: true),Gateway 网关会在下次启动时重放其离线期间(崩溃、重启、Mac 睡眠)落入 chat.db 的入站消息。默认禁用 — 见在 Gateway 网关停机后补齐。关闭 openclaw#78649


BlueBubbles 支持已移除。请将 channels.bluebubbles 配置迁移到 channels.imessage;OpenClaw 仅通过 imsg 支持 iMessage。先阅读简短公告 BlueBubbles removal and the imsg iMessage path,或查看完整迁移表 Coming from BlueBubbles

Status:原生外部 CLI 集成。Gateway 网关会启动 imsg rpc,并通过 stdio 上的 JSON-RPC 通信(没有单独的守护进程/端口)。高级操作需要 imsg launch 和成功的私有 API 探测。



回复、tapback、效果、附件和群组管理。


iMessage 私信默认使用配对模式。


当 Gateway 网关未运行在 Messages Mac 上时,使用 SSH 包装脚本。


完整的 iMessage 字段参考。

快速设置




brew install steipete/tap/imsg
imsg rpc --help
imsg launch
openclaw channels status --probe
  </Step>

  <Step title="配置 OpenClaw">
{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "/usr/local/bin/imsg",
      dbPath: "/Users/user/Library/Messages/chat.db",
    },
  },
}
  </Step>

  <Step title="启动 Gateway 网关">
openclaw gateway
  </Step>

  <Step title="批准首次私信配对(默认 dmPolicy)">
openclaw pairing list imessage
openclaw pairing approve imessage <CODE>
    配对请求会在 1 小时后过期。
  </Step>
</Steps>


OpenClaw 只需要一个兼容 stdio 的 cliPath,因此你可以将 cliPath 指向一个包装脚本,由它通过 SSH 连接到远程 Mac 并运行 imsg

#!/usr/bin/env bash
exec ssh -T gateway-host imsg "$@"
启用附件时的推荐配置:
{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "~/.openclaw/scripts/imsg-ssh",
      remoteHost: "user@gateway-host", // used for SCP attachment fetches
      includeAttachments: true,
      // Optional: override allowed attachment roots.
      // Defaults include /Users/*/Library/Messages/Attachments
      attachmentRoots: ["/Users/*/Library/Messages/Attachments"],
      remoteAttachmentRoots: ["/Users/*/Library/Messages/Attachments"],
    },
  },
}
如果未设置 `remoteHost`,OpenClaw 会尝试通过解析 SSH 包装脚本来自动检测它。
`remoteHost` 必须是 `host`  `user@host`(不能包含空格或 SSH 选项)。
OpenClaw  SCP 使用严格的主机密钥检查,因此中继主机密钥必须已存在于 `~/.ssh/known_hosts` 中。
附件路径会根据允许的根路径(`attachmentRoots` / `remoteAttachmentRoots`)进行验证。


要求和权限(macOS)

  • 运行 imsg 的 Mac 必须已登录 Messages。
  • 运行 OpenClaw/imsg 的进程上下文需要完整磁盘访问权限(Messages 数据库访问)。
  • 通过 Messages.app 发送消息需要自动化权限。
  • 对于高级操作(回应 / 编辑 / 撤回发送 / 线程回复 / 效果 / 群组操作),必须禁用系统完整性保护 — 见下方启用 imsg 私有 API。基础文本和媒体发送/接收不需要它也能工作。


权限按进程上下文授予。如果 Gateway 网关以无头方式运行(LaunchAgent/SSH),请在同一上下文中运行一次交互式命令以触发提示:

imsg chats --limit 1
# or
imsg send <handle> "test"

启用 imsg 私有 API

imsg 提供两种运行模式:

  • 基础模式(默认,无需更改 SIP):通过 send 发送出站文本和媒体、入站 watch/history、聊天列表。这就是全新 brew install steipete/tap/imsg 加上上方标准 macOS 权限后即可获得的能力。
  • 私有 API 模式imsg 将辅助 dylib 注入 Messages.app,以调用内部 IMCore 函数。这会解锁 reacteditunsendreply(线程式)、sendWithEffectrenameGroupsetGroupIconaddParticipantremoveParticipantleaveGroup,以及输入状态指示和已读回执。

要使用此渠道页面记录的高级操作能力,你需要私有 API 模式。imsg README 明确说明了该要求:

readtypinglaunch、由桥接支持的富发送、消息变更和聊天管理等高级功能是选择启用的。它们要求禁用 SIP,并将辅助 dylib 注入 Messages.app。当 SIP 启用时,imsg launch 会拒绝注入。

辅助注入技术使用 imsg 自己的 dylib 来访问 Messages 私有 API。OpenClaw iMessage 路径中没有第三方服务器或 BlueBubbles 运行时。


禁用 SIP 是真实的安全权衡。 SIP 是 macOS 防止运行被修改系统代码的核心保护之一;系统范围关闭它会带来额外攻击面和副作用。特别是,在 Apple Silicon Mac 上禁用 SIP 也会禁用在 Mac 上安装和运行 iOS 应用的能力

请将其视为有意的运维选择,而不是默认选项。如果你的威胁模型无法容忍关闭 SIP,内置 iMessage 将仅限于基础模式 — 只能发送/接收文本和媒体,没有回应 / 编辑 / 撤回发送 / 效果 / 群组操作。

设置

  1. 在运行 Messages.app 的 Mac 上安装(或升级)imsg

bash
brew install steipete/tap/imsg
imsg --version
imsg status --json

imsg status --json 输出会报告 bridge_versionrpc_methods 和每个方法的 selectors,因此你可以在开始前查看当前构建支持什么。

  1. 禁用系统完整性保护。 这与 macOS 版本相关,因为底层 Apple 要求取决于操作系统和硬件:
    - macOS 10.13–10.15(Sierra–Catalina): 通过 Terminal 禁用 Library Validation,重启到恢复模式,运行 csrutil disable,再重启。
    - macOS 11+(Big Sur 及更高版本),Intel: 恢复模式(或互联网恢复),csrutil disable,重启。
    - macOS 11+,Apple Silicon: 通过电源按钮启动序列进入恢复;在较新的 macOS 版本中,点击 Continue 时按住 Left Shift 键,然后运行 csrutil disable。虚拟机设置遵循单独流程 — 先创建 VM 快照。
    - macOS 26 / Tahoe: library-validation 策略和 imagent 私有授权检查进一步收紧;imsg 可能需要更新构建才能跟上。如果在 macOS 主版本升级后,imsg launch 注入或特定 selectors 开始返回 false,请先检查 imsg 的发布说明,再假设 SIP 步骤已成功。

在运行 imsg launch 前,请按照 Apple 针对你的 Mac 的恢复模式流程禁用 SIP。

  1. 注入辅助程序。 在 SIP 已禁用且 Messages.app 已登录的情况下:

bash
imsg launch

当 SIP 仍启用时,imsg launch 会拒绝注入,因此这也可用来确认第 2 步已生效。

  1. 从 OpenClaw 验证桥接:

bash
openclaw channels status --probe

iMessage 条目应报告 works,且 imsg status --json | jq '.selectors' 应显示 retractMessagePart: true,以及你的 macOS 构建暴露的任意编辑 / 输入 / 已读 selectors。actions.ts 中的 OpenClaw 插件按方法门控只会公布底层 selector 为 true 的操作,因此你在智能体工具列表中看到的操作面反映了此主机上的桥接实际能做什么。

如果 openclaw channels status --probe 将渠道报告为 works,但特定操作在分发时抛出 “iMessage <action> requires the imsg private API bridge”,请再次运行 imsg launch — 辅助程序可能会脱落(Messages.app 重启、操作系统更新等),而缓存的 available: true 状态会持续公布操作,直到下一次探测刷新。

当你无法禁用 SIP 时

如果禁用 SIP 对你的威胁模型不可接受:

  • imsg 会回退到基础模式 — 仅支持文本 + 媒体 + 接收。
  • OpenClaw 插件仍会公布文本/媒体发送和入站监控;它只是会从操作面隐藏 reacteditunsendreplysendWithEffect 和群组操作(根据按方法能力门控)。
  • 你可以为 iMessage 工作负载运行一台单独的非 Apple-Silicon Mac(或专用机器人 Mac)并关闭 SIP,同时在你的主要设备上保持 SIP 启用。见下方专用机器人 macOS 用户(单独的 iMessage 身份)

访问控制和路由



channels.imessage.dmPolicy 控制直接消息:

- `pairing`(默认)
- `allowlist`
- `open`(要求 `allowFrom` 包含 `"*"`
- `disabled`

允许列表字段:`channels.imessage.allowFrom`

允许列表条目必须标识发送者:handle 或静态发送者访问组(`accessGroup:<name>`)。对 `chat_id:*``chat_guid:*`  `chat_identifier:*` 等聊天目标使用 `channels.imessage.groupAllowFrom`;对数字 `chat_id` 注册表键使用 `channels.imessage.groups`


channels.imessage.groupPolicy 控制群组处理:

- `allowlist`(配置时默认)
- `open`
- `disabled`

群组发送者允许列表:`channels.imessage.groupAllowFrom`

`groupAllowFrom` 条目也可以引用静态发送者访问组(`accessGroup:<name>`)。

运行时回退:如果未设置 `groupAllowFrom`,iMessage 群组发送者检查会使用 `allowFrom`;当私信和群组准入应不同时,请设置 `groupAllowFrom`
运行时说明:如果完全缺少 `channels.imessage`,运行时会回退到 `groupPolicy="allowlist"` 并记录警告(即使已设置 `channels.defaults.groupPolicy`)。

<Warning>
群组路由有**两个**连续运行的允许列表门控,并且两者都必须通过:

1. **发送者 / 聊天目标允许列表**`channels.imessage.groupAllowFrom`)— handle、`chat_guid``chat_identifier`  `chat_id`
2. **群组注册表**`channels.imessage.groups`)— 使用 `groupPolicy: "allowlist"` 时,此门控需要 `groups: { "*": { ... } }` 通配符条目(设置 `allowAll = true`),或 `groups` 下显式的每个 `chat_id` 条目。

如果门控 2 中没有任何内容,每条群组消息都会被丢弃。插件会在默认日志级别发出两个 `warn` 级别信号:

- 启动时每个账号一次:`imessage: groupPolicy="allowlist" but channels.imessage.groups is empty for account "<id>"`
- 运行时每个 `chat_id` 一次:`imessage: dropping group message from chat_id=<id> ...`

私信会继续工作,因为它们走的是不同代码路径。

 `groupPolicy: "allowlist"` 下保持群组继续流转的最低配置:

```json5
{
  channels: {
    imessage: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["+15555550123"],
      groups: { "*": { "requireMention": true } },
    },
  },
}
```

如果这些 `warn` 行出现在 Gateway 网关日志中,说明第 2 道门控正在丢弃消息 —— 添加 `groups` 块。
</Warning>

群组的提及门控:

- iMessage 没有原生提及元数据
- 提及检测使用正则表达式模式(`agents.list[].groupChat.mentionPatterns`,回退为 `messages.groupChat.mentionPatterns`
- 未配置模式时,无法强制执行提及门控

来自已授权发送者的控制命令可以绕过群组中的提及门控。

按群组设置的 `systemPrompt`

`channels.imessage.groups.*` 下的每个条目都接受一个可选的 `systemPrompt` 字符串。该值会在处理该群组消息的每个轮次中注入到智能体的系统提示词中。解析方式与 `channels.whatsapp.groups` 使用的按群组提示词解析一致:

1. **群组专用系统提示词**`groups["<chat_id>"].systemPrompt`):当映射中存在特定群组条目,**并且**定义了其 `systemPrompt` 键时使用。如果 `systemPrompt` 是空字符串(`""`),则会抑制通配符,并且不会对该群组应用系统提示词。
2. **群组通配符系统提示词**`groups["*"].systemPrompt`):当映射中完全不存在特定群组条目,或存在但未定义 `systemPrompt` 键时使用。

```json5
{
  channels: {
    imessage: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["+15555550123"],
      groups: {
        "*": { systemPrompt: "Use British spelling." },
        "8421": {
          requireMention: true,
          systemPrompt: "This is the on-call rotation chat. Keep replies under 3 sentences.",
        },
        "9907": {
          // explicit suppression: the wildcard "Use British spelling." does not apply here
          systemPrompt: "",
        },
      },
    },
  },
}
```

按群组设置的提示词只应用于群组消息 —— 此频道中的私信不受影响。


- 私信使用直接路由;群组使用群组路由。
- 使用默认的 session.dmScope=main 时,iMessage 私信会折叠到智能体主会话中。
- 群组会话相互隔离(agent:<agentId>:imessage:group:<chat_id>)。
- 回复会使用来源频道/目标元数据路由回 iMessage。

类群组线程行为:

一些多参与者 iMessage 线程可能会以 `is_group=false` 到达。
如果该 `chat_id` 已在 `channels.imessage.groups` 下显式配置,OpenClaw 会将其视为群组流量(群组门控 + 群组会话隔离)。


ACP 会话绑定

旧版 iMessage 聊天也可以绑定到 ACP 会话。

快速操作流程:

  • 在私信或允许的群聊中运行 /acp spawn codex --bind here
  • 同一 iMessage 对话中的后续消息会路由到已生成的 ACP 会话。
  • /new/reset 会在原位重置同一个已绑定 ACP 会话。
  • /acp close 会关闭 ACP 会话并移除绑定。

支持通过顶层 bindings[] 条目配置持久绑定,其中包含 type: "acp"match.channel: "imessage"

match.peer.id 可以使用:

  • 规范化的私信句柄,例如 +15555550123user@example.com
  • chat_id:<id>(推荐用于稳定的群组绑定)
  • chat_guid:<guid>
  • chat_identifier:<identifier>

示例:

{
  agents: {
    list: [
      {
        id: "codex",
        runtime: {
          type: "acp",
          acp: { agent: "codex", backend: "acpx", mode: "persistent" },
        },
      },
    ],
  },
  bindings: [
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "imessage",
        accountId: "default",
        peer: { kind: "group", id: "chat_id:123" },
      },
      acp: { label: "codex-group" },
    },
  ],
}

参见 ACP 智能体,了解共享的 ACP 绑定行为。

部署模式



使用专用 Apple ID 和 macOS 用户,使机器人流量与你的个人 Messages 配置文件隔离。

典型流程:

1. 创建/登录专用 macOS 用户。
2. 在该用户中使用机器人 Apple ID 登录 Messages。
3. 在该用户中安装 `imsg`
4. 创建 SSH 包装器,让 OpenClaw 能够在该用户上下文中运行 `imsg`
5.  `channels.imessage.accounts.<id>.cliPath`  `.dbPath` 指向该用户配置文件。

首次运行可能需要在该机器人用户会话中授予 GUI 权限(自动化 + 完全磁盘访问权限)。


常见拓扑:

- Gateway 网关在 Linux/VM 上运行
- iMessage + `imsg` 在你的 tailnet 中的一台 Mac 上运行
- `cliPath` 包装器使用 SSH 运行 `imsg`
- `remoteHost` 启用 SCP 附件获取

示例

```json5
{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "~/.openclaw/scripts/imsg-ssh",
      remoteHost: "bot@mac-mini.tailnet-1234.ts.net",
      includeAttachments: true,
      dbPath: "/Users/bot/Library/Messages/chat.db",
    },
  },
}
```

```bash
#!/usr/bin/env bash
exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"
```

使用 SSH 密钥 SSH  SCP 都无需交互
请先确保主机密钥受信任例如 `ssh bot@mac-mini.tailnet-1234.ts.net`),以便填充 `known_hosts`。


iMessage 支持在 channels.imessage.accounts 下进行按账号配置。

每个账号都可以覆盖 `cliPath``dbPath``allowFrom``groupPolicy``mediaMaxMb`、历史记录设置以及附件根路径允许列表等字段。


媒体、分块和投递目标



- 入站附件摄取默认关闭 —— 设置 channels.imessage.includeAttachments: true,即可将照片、语音备忘录、视频和其他附件转发给智能体。禁用时,仅含附件的 iMessage 会在到达智能体之前被丢弃,并且可能根本不会产生 Inbound message 日志行。
- 设置 remoteHost 时,可以通过 SCP 获取远程附件路径
- 附件路径必须匹配允许的根路径:
- channels.imessage.attachmentRoots(本地)
- channels.imessage.remoteAttachmentRoots(远程 SCP 模式)
- 默认根路径模式:/Users/*/Library/Messages/Attachments
- SCP 使用严格主机密钥检查(StrictHostKeyChecking=yes
- 出站媒体大小使用 channels.imessage.mediaMaxMb(默认 16 MB)


- 文本分块限制:channels.imessage.textChunkLimit(默认 4000)
- 分块模式:channels.imessage.chunkMode
- length(默认)
- newline(优先按段落拆分)


首选显式目标:

- `chat_id:123`(推荐用于稳定路由
- `chat_guid:...`
- `chat_identifier:...`

也支持句柄目标

- `imessage:+1555...`
- `sms:+1555...`
- `user@example.com`

```bash
imsg chats --limit 20
```


私有 API 操作

imsg launch 正在运行,并且 openclaw channels status --probe 报告 privateApi.available: true 时,消息工具除了普通文本发送外,还可以使用 iMessage 原生操作。

{
  channels: {
    imessage: {
      actions: {
        reactions: true,
        edit: true,
        unsend: true,
        reply: true,
        sendWithEffect: true,
        sendAttachment: true,
        renameGroup: true,
        setGroupIcon: true,
        addParticipant: true,
        removeParticipant: true,
        leaveGroup: true,
      },
    },
  },
}



- react:添加/移除 iMessage tapback(messageIdemojiremove)。支持的 tapback 映射到爱心、喜欢、不喜欢、大笑、强调和疑问。
- reply:向现有消息发送线程回复(messageIdtextmessage,以及 chatGuidchatIdchatIdentifierto)。
- sendWithEffect:使用 iMessage 效果发送文本(textmessageeffecteffectId)。
- edit:在支持的 macOS/私有 API 版本上编辑已发送消息(messageIdtextnewText)。
- unsend:在支持的 macOS/私有 API 版本上撤回已发送消息(messageId)。
- upload-file:发送媒体/文件(buffer 为 base64,或已注水的 media/path/filePathfilename,可选 asVoice)。旧版别名:sendAttachment
- renameGroupsetGroupIconaddParticipantremoveParticipantleaveGroup:当当前目标是群组对话时,管理群聊。


入站 iMessage 上下文会在可用时同时包含短 MessageSid 值和完整消息 GUID。短 ID 的作用域限定在最近的内存回复缓存中,并会在使用前根据当前聊天进行检查。如果短 ID 已过期或属于另一个聊天,请使用完整的 MessageSidFull 重试。


只有当缓存的探测状态显示桥接不可用时,OpenClaw 才会隐藏私有 API 操作。如果状态未知,操作会保持可见,并在调度时惰性探测,因此首次操作可以在 imsg launch 之后成功,而无需单独手动刷新状态。


当私有 API 桥接启动时,已接受的入站聊天会在调度前标记为已读,并在智能体生成期间向发送者显示正在输入气泡。使用以下配置禁用已读标记:

```json5
{
  channels: {
    imessage: {
      sendReadReceipts: false,
    },
  },
}
```

早于按方法能力列表的旧版 `imsg` 构建会静默关闭正在输入/已读门控;OpenClaw 会在每次重启时记录一次性警告,以便归因缺失的回执。


OpenClaw 订阅 iMessage tapback,并将已接受的反应作为系统事件路由,而不是作为普通消息文本,因此用户 tapback 不会触发普通回复循环。

通知模式由 `channels.imessage.reactionNotifications` 控制:

- `"own"`(默认):仅当用户对机器人撰写的消息作出反应时通知。
- `"all"`:通知来自已授权发送者的所有入站 tapback。
- `"off"`:忽略入站 tapback。

按账号覆盖使用 `channels.imessage.accounts.<id>.reactionNotifications`。


配置写入

iMessage 默认允许由频道发起的配置写入(用于 commands.config: true 时的 /config set|unset)。

禁用:

{
  channels: {
    imessage: {
      configWrites: false,
    },
  },
}

合并拆分发送的私信(同一次输入中的命令 + URL)

当用户同时输入命令和 URL 时 —— 例如 Dump https://example.com/article —— Apple 的 Messages 应用会将发送拆分成两条独立的 chat.db

  1. 一条文本消息("Dump")。
  2. 一个 URL 预览气泡("https://..."),并带有 OG 预览图片作为附件。

两行在大多数设置中会相隔约 0.8-2.0 秒到达 OpenClaw。如果没有合并,智能体会在第 1 轮只收到命令并回复(通常是“把 URL 发给我”),然后到第 2 轮才看到 URL,此时命令上下文已经丢失。这是 Apple 的发送管线行为,并不是 OpenClaw 或 imsg 引入的。

channels.imessage.coalesceSameSenderDms 会让一个私信选择把连续的同发送者行合并成一个智能体轮次。群组聊天会继续按消息分发,以保留多用户轮次结构。



在以下情况启用:

- 你发布的技能期望在一条消息中收到 `command + payload`(dumppastesavequeue 等)。
- 你的用户会把 URL、图片或长内容与命令一起粘贴。
- 你可以接受增加的私信轮次延迟(见下文)。

在以下情况保持禁用:

- 你需要单词私信触发器具备最低命令延迟。
- 你的所有流程都是没有后续载荷的一次性命令。



json5
{
channels: {
imessage: {
coalesceSameSenderDms: true, // opt in (default: false)
},
},
}

开启该标志且没有显式设置 `messages.inbound.byChannel.imessage` 防抖窗口会扩大到 **2500 ms**旧默认值为 0 ms即不防抖)。需要更宽的窗口是因为 Apple 的拆分发送节奏为 0.8-2.0 无法适配更紧的默认值

如需自行调整窗口

```json5
{
  messages: {
    inbound: {
      byChannel: {
        // 2500 ms works for most setups; raise to 4000 ms if your Mac is
        // slow or under memory pressure (observed gap can stretch past 2 s
        // then).
        imessage: 2500,
      },
    },
  },
}
```



- 私信消息会增加延迟。 开启该标志后,每条私信(包括独立控制命令和单条文本后续消息)都会在分发前最多等待一个防抖窗口,以防有载荷行即将到来。群组聊天消息仍会即时分发。
- 合并输出有上限。 合并文本最多 4000 个字符,并带有显式的 …[truncated] 标记;附件最多 20 个;来源条目最多 10 个(超出后保留第一条和最新条)。每个来源 GUID 都会在 coalescedMessageGuids 中跟踪,供下游遥测使用。
- 仅限私信。 群组聊天会回退为按消息分发,因此多人输入时 bot 仍能保持响应。
- 按渠道选择启用。 其他渠道(Telegram、WhatsApp、Slack、……)不受影响。设置了 channels.bluebubbles.coalesceSameSenderDms 的旧版 BlueBubbles 配置应将该值迁移到 channels.imessage.coalesceSameSenderDms


场景以及智能体看到的内容

用户编写 chat.db 产生 关闭标志(默认) 开启标志 + 2500 ms 窗口
Dump https://example.com(一次发送) 2 行,相隔约 1 秒 两个智能体轮次:“Dump” 单独一轮,然后是 URL 一个轮次:合并文本 Dump https://example.com
Save this 📎image.jpg caption(附件 + 文本) 2 行 两个轮次(附件在合并时丢弃) 一个轮次:保留文本 + 图片
/status(独立命令) 1 行 即时分发 最多等待一个窗口,然后分发
单独粘贴 URL 1 行 即时分发 即时分发(桶中只有一个条目)
文本 + URL 作为两条有意分开的消息发送,相隔数分钟 窗口外 2 行 两个轮次 两个轮次(两者之间窗口已过期)
快速大量发送(窗口内 >10 条小私信) N 行 N 个轮次 一个轮次,输出有上限(应用第一条 + 最新条、文本/附件上限)
两个人在群组聊天中输入 来自 M 个发送者的 N 行 M+ 个轮次(每个发送者桶一个) M+ 个轮次,群组聊天不会被合并

Gateway 网关停机后的追赶

当 Gateway 网关离线(崩溃、重启、Mac 休眠、机器关机)时,imsg watch 会在 Gateway 网关恢复后从当前 chat.db 状态继续;默认情况下,间隔期间到达的任何内容都不会被看到。追赶会在下一次启动时重放这些消息,确保智能体不会静默错过入站流量。

追赶功能默认禁用。按渠道启用:

channels: {
  imessage: {
    catchup: {
      enabled: true,             // master switch (default: false)
      maxAgeMinutes: 120,        // skip rows older than now - 2h (default: 120, clamp 1..720)
      perRunLimit: 50,           // max rows replayed per startup (default: 50, clamp 1..500)
      firstRunLookbackMinutes: 30, // first run with no cursor: look back 30 min (default: 30)
      maxFailureRetries: 10,     // give up on a wedged guid after 10 dispatch failures (default: 10)
    },
  },
}

运行方式

每次 monitorIMessageProvider 启动执行一轮,顺序为 imsg launch 就绪 → watch.subscribeperformIMessageCatchup → 实时分发循环。追赶本身使用 chats.list + 每个聊天的 messages.history,并通过与 imsg watch 相同的 JSON-RPC 客户端调用。追赶期间到达的任何内容都会照常流经实时分发;现有入站去重缓存会吸收与重放行之间的任何重叠。

每条重放行都会走实时分发路径(evaluateIMessageInbound + dispatchInboundMessage),因此允许列表、群组策略、防抖器、回声缓存和已读回执在重放消息与实时消息上的行为完全一致。

游标和重试语义

追赶会在 <openclawStateDir>/imessage/catchup/<account>__<hash>.json 保存按账号划分的游标(OpenClaw 状态目录默认是 ~/.openclaw,可用 OPENCLAW_STATE_DIR 覆盖):

{
  "lastSeenMs": 1717900800000,
  "lastSeenRowid": 482910,
  "updatedAt": 1717900801234,
  "failureRetries": { "<guid>": 1 }
}
  • 每次成功分发后,游标都会前进;当某行的分发抛出异常时,游标会保持不变,下一次启动会从保持的游标重试同一行。
  • 当同一个 guid 连续抛出异常达到 maxFailureRetries 次后,追赶会记录一条 warn,并强制将游标推进到卡住的消息之后,确保后续启动可以继续前进。
  • 已经放弃的 guid 在后续运行中一看到就跳过(不会尝试分发),并计入运行摘要中的 skippedGivenUp

操作者可见信号

imessage catchup: replayed=N skippedFromMe= skippedGivenUp= failed= givenUp= fetchedCount=
imessage catchup: giving up on guid=<guid> after <N> failures; advancing cursor past it
imessage catchup: fetched <X> rows across chats, capped to perRunLimit=<Y>

WARN ... capped to perRunLimit 行表示单次启动没有清空全部积压。如果你的间隔经常超过默认的 50 行处理量,请提高 perRunLimit(最大 500)。

何时保持关闭

  • Gateway 网关持续运行,并有看门狗自动重启,间隔始终小于几秒,此时默认关闭即可。
  • 私信量很低,错过消息也不会改变智能体行为;firstRunLookbackMinutes 初始窗口可能会在首次启用时分发令人意外的旧上下文。

启用追赶后,首次没有游标的启动只会回看 firstRunLookbackMinutes(默认 30 分钟),而不是完整的 maxAgeMinutes 窗口;这样可避免重放启用前的长历史消息。

故障排除



验证二进制文件和 RPC 支持:

```bash
imsg rpc --help
imsg status --json
openclaw channels status --probe
```

如果探测报告 RPC 不受支持,请更新 `imsg`。如果私有 API 操作不可用,请在已登录的 macOS 用户会话中运行 `imsg launch`,然后再次探测。如果 Gateway 网关未在 macOS 上运行,请使用上面的通过 SSH 连接远程 Mac 设置,而不是默认的本地 `imsg` 路径。


默认的 cliPath: "imsg" 必须在已登录 Messages 的 Mac 上运行。在 Linux 或 Windows 上,将 channels.imessage.cliPath 设置为一个包装脚本,用 SSH 连接到那台 Mac 并运行 imsg "$@"

#!/usr/bin/env bash
exec ssh -T messages-mac imsg "$@"
然后运行:
openclaw channels status --probe --channel imessage


检查:

- `channels.imessage.dmPolicy`
- `channels.imessage.allowFrom`
- 配对审批(`openclaw pairing list imessage`)


检查:

- `channels.imessage.groupPolicy`
- `channels.imessage.groupAllowFrom`
- `channels.imessage.groups` 允许列表行为
- 提及模式配置(`agents.list[].groupChat.mentionPatterns`)


检查:

- `channels.imessage.remoteHost`
- `channels.imessage.remoteAttachmentRoots`
- 来自 Gateway 网关主机的 SSH/SCP 密钥认证
- 主机密钥存在于 Gateway 网关主机上的 `~/.ssh/known_hosts`
- 运行 Messages 的 Mac 上远程路径可读


在同一用户/会话上下文中的交互式 GUI 终端中重新运行,并批准提示:

```bash
imsg chats --limit 1
imsg send <handle> "test"
```

确认运行 OpenClaw/`imsg` 的进程上下文已获得 Full Disk Access + Automation 权限。


配置参考指针

相关


📄 行

原文:https://docs.openclaw.ai/zh-CN/channels/line

LINE 通过 LINE Messaging API 连接到 OpenClaw。该插件在 Gateway 网关上作为 webhook 接收器运行,并使用你的渠道访问令牌 + 渠道密钥进行身份验证。

Status:可下载插件。支持私信、群聊、媒体、位置、Flex 消息、模板消息和快捷回复。不支持回应和线程。

安装

在配置渠道前安装 LINE:

openclaw plugins install @openclaw/line

本地检出(从 git 仓库运行时):

openclaw plugins install ./path/to/local/line-plugin

设置

  1. 创建 LINE Developers 账号并打开 Console:
    https://developers.line.biz/console/
  2. 创建(或选择)一个 Provider,并添加一个 Messaging API 渠道。
  3. 从渠道设置中复制 Channel access tokenChannel secret
  4. 在 Messaging API 设置中启用 Use webhook
  5. 将 webhook URL 设置为你的 Gateway 网关端点(需要 HTTPS):
https://gateway-host/line/webhook

Gateway 网关会响应 LINE 的 webhook 验证(GET)和入站事件(POST)。
如果你需要自定义路径,请设置 channels.line.webhookPath
channels.line.accounts.<id>.webhookPath,并相应更新 URL。

安全注意事项:

  • LINE 签名验证依赖请求体(对原始请求体执行 HMAC),因此 OpenClaw 会在验证前应用严格的预认证请求体限制和超时。
  • OpenClaw 会从已验证的原始请求字节处理 webhook 事件。为确保签名完整性安全,会忽略经上游中间件转换的 req.body 值。

配置

最小配置:

{
  channels: {
    line: {
      enabled: true,
      channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN",
      channelSecret: "LINE_CHANNEL_SECRET",
      dmPolicy: "pairing",
    },
  },
}

公开私信配置:

{
  channels: {
    line: {
      enabled: true,
      channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN",
      channelSecret: "LINE_CHANNEL_SECRET",
      dmPolicy: "open",
      allowFrom: ["*"],
    },
  },
}

环境变量(仅默认账号):

  • LINE_CHANNEL_ACCESS_TOKEN
  • LINE_CHANNEL_SECRET

令牌/密钥文件:

{
  channels: {
    line: {
      tokenFile: "/path/to/line-token.txt",
      secretFile: "/path/to/line-secret.txt",
    },
  },
}

tokenFilesecretFile 必须指向常规文件。符号链接会被拒绝。

多个账号:

{
  channels: {
    line: {
      accounts: {
        marketing: {
          channelAccessToken: "...",
          channelSecret: "...",
          webhookPath: "/line/marketing",
        },
      },
    },
  },
}

访问控制

私信默认使用配对。未知发送者会收到配对码,并且其消息会被忽略,直到获批。

openclaw pairing list line
openclaw pairing approve line <CODE>

允许列表和策略:

  • channels.line.dmPolicypairing | allowlist | open | disabled
  • channels.line.allowFrom:允许发送私信的 LINE 用户 ID;dmPolicy: "open" 需要 ["*"]
  • channels.line.groupPolicyallowlist | open | disabled
  • channels.line.groupAllowFrom:允许用于群组的 LINE 用户 ID
  • 按群组覆盖:channels.line.groups.<groupId>.allowFrom
  • 静态发送者访问组可通过 accessGroup:<name>allowFromgroupAllowFrom 和按群组的 allowFrom 引用。
  • 运行时说明:如果完全缺少 channels.line,运行时会在群组检查中回退到 groupPolicy="allowlist"(即使已设置 channels.defaults.groupPolicy)。

LINE ID 区分大小写。有效 ID 形如:

  • 用户:U + 32 个十六进制字符
  • 群组:C + 32 个十六进制字符
  • 房间:R + 32 个十六进制字符

消息行为

  • 文本会按 5000 个字符分块。
  • Markdown 格式会被移除;代码块和表格会在可能时转换为 Flex 卡片。
  • 流式响应会被缓冲;在智能体工作时,LINE 会收到带加载动画的完整分块。
  • 媒体下载受 channels.line.mediaMaxMb 限制(默认 10)。
  • 入站媒体会在传递给智能体前保存到 ~/.openclaw/media/inbound/ 下,与其他内置渠道插件使用的共享媒体存储保持一致。

渠道数据(富消息)

使用 channelData.line 发送快捷回复、位置、Flex 卡片或模板消息。

{
  text: "Here you go",
  channelData: {
    line: {
      quickReplies: ["Status", "Help"],
      location: {
        title: "Office",
        address: "123 Main St",
        latitude: 35.681236,
        longitude: 139.767125,
      },
      flexMessage: {
        altText: "Status card",
        contents: {
          /* Flex payload */
        },
      },
      templateMessage: {
        type: "confirm",
        text: "Proceed?",
        confirmLabel: "Yes",
        confirmData: "yes",
        cancelLabel: "No",
        cancelData: "no",
      },
    },
  },
}

LINE 插件还随附一个用于 Flex 消息预设的 /card 命令:

/card info "Welcome" "Thanks for joining!"

ACP 支持

LINE 支持 ACP(Agent Communication Protocol)会话绑定:

  • /acp spawn <agent> --bind here 会将当前 LINE 聊天绑定到 ACP 会话,而不创建子线程。
  • 配置的 ACP 绑定和活跃的会话绑定 ACP 会话在 LINE 上的工作方式与其他会话渠道相同。

详见 ACP Agents

出站媒体

LINE 插件支持通过智能体消息工具发送图片、视频和音频文件。媒体会通过 LINE 专用投递路径发送,并带有适当的预览和跟踪处理:

  • 图片:作为 LINE 图片消息发送,并自动生成预览。
  • 视频:发送时会显式处理预览和内容类型。
  • 音频:作为 LINE 音频消息发送。

出站媒体 URL 必须是公开 HTTPS URL。OpenClaw 会在将 URL 交给 LINE 前验证目标主机名,并拒绝 local loopback、链路本地和私有网络目标。

当 LINE 专用路径不可用时,通用媒体发送会回退到现有的仅图片路由。

故障排除

  • Webhook 验证失败: 确保 webhook URL 使用 HTTPS,并且 channelSecret 与 LINE console 匹配。
  • 没有入站事件: 确认 webhook 路径与 channels.line.webhookPath 匹配,并且 Gateway 网关可从 LINE 访问。
  • 媒体下载错误: 如果媒体超过默认限制,请提高 channels.line.mediaMaxMb

相关


📄 Matrix

原文:https://docs.openclaw.ai/zh-CN/channels/matrix

Matrix 是 OpenClaw 的可下载渠道插件。
它使用官方 matrix-js-sdk,并支持私信、房间、线程、媒体、回应、投票、位置和 E2EE。

安装

在配置渠道前,先从 ClawHub 安装 Matrix:

openclaw plugins install @openclaw/matrix

裸插件规格会先尝试 ClawHub,然后回退到 npm。若要强制使用注册表来源,请使用 openclaw plugins install clawhub:@openclaw/matrixopenclaw plugins install npm:@openclaw/matrix

从本地检出安装:

openclaw plugins install ./path/to/local/matrix-plugin

plugins install 会注册并启用插件,因此不需要单独执行 openclaw plugins enable matrix 步骤。在配置下面的渠道前,该插件仍不会执行任何操作。请参阅插件了解通用插件行为和安装规则。

设置

  1. 在你的 homeserver 上创建一个 Matrix 账号。
  2. 使用 homeserver + accessToken,或 homeserver + userId + password 配置 channels.matrix
  3. 重启 Gateway 网关。
  4. 与机器人开启私信,或邀请它加入房间(请参阅自动加入 - 只有 autoJoin 允许时,新邀请才会落地)。

交互式设置

openclaw channels add
openclaw configure --section channels

向导会询问:homeserver URL、认证方式(访问令牌或密码)、用户 ID(仅限密码认证)、可选设备名称、是否启用 E2EE,以及是否配置房间访问和自动加入。

如果匹配的 MATRIX_* 环境变量已经存在,并且所选账号没有保存的认证信息,向导会提供一个环境变量快捷方式。若要在保存允许列表前解析房间名称,请运行 openclaw channels resolve --channel matrix "Project Room"。启用 E2EE 时,向导会写入配置,并运行与 openclaw matrix encryption setup 相同的引导流程。

最小配置

基于令牌:

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      accessToken: "syt_xxx",
      dm: { policy: "pairing" },
    },
  },
}

基于密码(首次登录后会缓存令牌):

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      userId: "@bot:example.org",
      password: "replace-me", // pragma: allowlist secret
      deviceName: "OpenClaw Gateway",
    },
  },
}

自动加入

channels.matrix.autoJoin 默认为 off。使用默认值时,在你手动加入前,机器人不会出现在新邀请带来的新房间或私信中。

OpenClaw 无法在邀请时判断受邀房间是私信还是群组,因此所有邀请(包括类似私信的邀请)都会先经过 autoJoindm.policy 只会在稍后应用,即机器人加入且房间完成分类之后。


设置 autoJoin: "allowlist"autoJoinAllowlist 来限制机器人接受哪些邀请,或设置 autoJoin: "always" 来接受所有邀请。

autoJoinAllowlist 只接受稳定目标:!roomId:server#alias:server*。纯房间名称会被拒绝;别名条目会根据 homeserver 解析,而不是根据受邀房间声称的状态解析。

{
  channels: {
    matrix: {
      autoJoin: "allowlist",
      autoJoinAllowlist: ["!ops:example.org", "#support:example.org"],
      groups: {
        "!ops:example.org": { requireMention: true },
      },
    },
  },
}

若要接受所有邀请,请使用 autoJoin: "always"

允许列表目标格式

私信和房间允许列表最好填入稳定 ID:

  • 私信(dm.allowFromgroupAllowFromgroups.<room>.users):使用 @user:server。默认会忽略显示名称,因为它们可变;只有在你明确需要兼容显示名称条目时,才设置 dangerouslyAllowNameMatching: true
  • 房间允许列表键(groups、旧版 rooms):使用 !room:server#alias:server。默认会忽略纯房间名称;只有在你明确需要兼容已加入房间名称查找时,才设置 dangerouslyAllowNameMatching: true
  • 邀请允许列表(autoJoinAllowlist):使用 !room:server#alias:server*。纯房间名称会被拒绝。

账号 ID 规范化

向导会将友好名称转换为规范化账号 ID。例如,Ops Bot 会变成 ops-bot。标点符号会在作用域环境变量名称中转义,因此两个账号不会冲突:-_X2D_,所以 ops-prod 会映射到 MATRIX_OPS_X2D_PROD_*

缓存凭证

Matrix 会将缓存凭证存储在 ~/.openclaw/credentials/matrix/ 下:

  • 默认账号:credentials.json
  • 命名账号:credentials-<account>.json

当这里存在缓存凭证时,即使配置文件中没有访问令牌,OpenClaw 也会将 Matrix 视为已配置。这涵盖设置、openclaw doctor 和渠道状态探测。

环境变量

当等效配置键未设置时使用。默认账号使用无前缀名称;命名账号会在后缀前插入账号 ID。

默认账号 命名账号(<ID> 是规范化账号 ID)
MATRIX_HOMESERVER MATRIX_<ID>_HOMESERVER
MATRIX_ACCESS_TOKEN MATRIX_<ID>_ACCESS_TOKEN
MATRIX_USER_ID MATRIX_<ID>_USER_ID
MATRIX_PASSWORD MATRIX_<ID>_PASSWORD
MATRIX_DEVICE_ID MATRIX_<ID>_DEVICE_ID
MATRIX_DEVICE_NAME MATRIX_<ID>_DEVICE_NAME
MATRIX_RECOVERY_KEY MATRIX_<ID>_RECOVERY_KEY

对于账号 ops,名称会变为 MATRIX_OPS_HOMESERVERMATRIX_OPS_ACCESS_TOKEN,依此类推。当你通过 --recovery-key-stdin 输入密钥时,支持恢复的 CLI 流程(verify backup restoreverify deviceverify bootstrap)会读取恢复密钥环境变量。

MATRIX_HOMESERVER 不能从工作区 .env 设置;请参阅工作区 .env 文件

配置示例

一个包含私信配对、房间允许列表和 E2EE 的实用基线:

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      accessToken: "syt_xxx",
      encryption: true,

      dm: {
        policy: "pairing",
        sessionScope: "per-room",
        threadReplies: "off",
      },

      groupPolicy: "allowlist",
      groupAllowFrom: ["@admin:example.org"],
      groups: {
        "!roomid:example.org": { requireMention: true },
      },

      autoJoin: "allowlist",
      autoJoinAllowlist: ["!roomid:example.org"],
      threadReplies: "inbound",
      replyToMode: "off",
      streaming: "partial",
    },
  },
}

流式预览

Matrix 回复流式传输需要显式选择启用。streaming 控制 OpenClaw 如何交付进行中的助手回复;blockStreaming 控制是否将每个完成的块保留为独立的 Matrix 消息。

{
  channels: {
    matrix: {
      streaming: "partial",
    },
  },
}

若要保留实时答案预览,但隐藏中间工具/进度行,请使用对象形式:

{
  channels: {
    matrix: {
      streaming: {
        mode: "partial",
        preview: {
          toolProgress: false,
        },
      },
    },
  },
}
streaming 行为
"off"(默认) 等待完整回复,然后发送一次。true"partial"false"off"
"partial" 在模型写入当前块时,就地编辑一条普通文本消息。原生 Matrix 客户端可能会在第一次预览时通知,而不是在最终编辑时通知。
"quiet" "partial" 相同,但消息是不触发通知的 notice。接收者只会在按用户的推送规则匹配最终编辑后收到一次通知(见下文)。

blockStreaming 独立于 streaming

streaming blockStreaming: true blockStreaming: false(默认)
"partial" / "quiet" 当前块的实时草稿,已完成块保留为消息 当前块的实时草稿,就地最终化
"off" 每个完成块一条触发通知的 Matrix 消息 完整回复一条触发通知的 Matrix 消息

注意:

  • 如果预览增长到超过 Matrix 的单事件大小限制,OpenClaw 会停止预览流式传输,并回退为仅最终交付。
  • 媒体回复始终正常发送附件。如果过期预览无法再安全复用,OpenClaw 会在发送最终媒体回复前将其撤回。
  • 当 Matrix 预览流式传输处于活动状态时,默认启用工具进度预览更新。设置 streaming.preview.toolProgress: false 可保留答案文本的预览编辑,但让工具进度走正常交付路径。
  • 预览编辑会增加额外的 Matrix API 调用。如果你想要最保守的速率限制配置,请保持 streaming: "off"

审批元数据

Matrix 原生审批提示是普通的 m.room.message 事件,其 com.openclaw.approval 下带有 OpenClaw 特定的自定义事件内容。Matrix 允许自定义事件内容键,因此原生客户端仍会渲染文本正文,而支持 OpenClaw 的客户端可以读取结构化审批 ID、类型、状态、可用决策,以及 exec/插件详情。

当审批提示过长,无法放入单个 Matrix 事件时,OpenClaw 会将可见文本分块,并仅将 com.openclaw.approval 附加到第一个块。允许/拒绝决策的回应会绑定到第一个事件,因此长提示与单事件提示保持相同的审批目标。

用于静默最终预览的自托管推送规则

streaming: "quiet" 只会在块或轮次最终化后通知接收者 - 必须有一条按用户的推送规则匹配最终预览标记。请参阅用于静默预览的 Matrix 推送规则获取完整配方(接收者令牌、推送器检查、规则安装、按 homeserver 的说明)。

机器人到机器人房间

默认情况下,会忽略来自其他已配置 OpenClaw Matrix 账号的 Matrix 消息。

当你有意启用智能体间 Matrix 流量时,请使用 allowBots

{
  channels: {
    matrix: {
      allowBots: "mentions", // true | "mentions"
      groups: {
        "!roomid:example.org": {
          requireMention: true,
        },
      },
    },
  },
}
  • allowBots: true 会接受允许房间和私信中来自其他已配置 Matrix 机器人账号的消息。
  • allowBots: "mentions" 只会在这些消息在房间中明确提及此机器人时接受它们。私信仍然允许。
  • groups.<room>.allowBots 会覆盖单个房间的账号级设置。
  • OpenClaw 仍会忽略来自相同 Matrix 用户 ID 的消息,以避免自我回复循环。
  • Matrix 这里不会暴露原生机器人标记;OpenClaw 将“由机器人发出”视为“由此 OpenClaw Gateway 网关上另一个已配置的 Matrix 账号发送”。

在共享房间中启用机器人到机器人流量时,请使用严格的房间允许列表和提及要求。

加密和验证

在加密(E2EE)房间中,出站图片事件使用 thumbnail_file,因此图片预览会与完整附件一起加密。未加密房间仍使用普通的 thumbnail_url。无需配置,插件会自动检测 E2EE 状态。

所有 openclaw matrix 命令都接受 --verbose(完整诊断)、--json(机器可读输出)和 --account <id>(多账户设置)。默认输出简洁,并带有静默的内部 SDK 日志。下面的示例展示规范形式;按需添加这些标志。

启用加密

openclaw matrix encryption setup

引导 secret storage 和 cross-signing,按需创建房间密钥备份,然后打印状态和后续步骤。实用标志:

  • --recovery-key <key> 在引导前应用恢复密钥(优先使用下面记录的 stdin 形式)
  • --force-reset-cross-signing 丢弃当前 cross-signing 身份并创建新身份(仅在有意这样做时使用)

对于新账户,请在创建时启用 E2EE:

openclaw matrix account add \
  --homeserver https://matrix.example.org \
  --access-token syt_xxx \
  --enable-e2ee

--encryption--enable-e2ee 的别名。

等效的手动配置:

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      accessToken: "syt_xxx",
      encryption: true,
      dm: { policy: "pairing" },
    },
  },
}

状态和信任信号

openclaw matrix verify status
openclaw matrix verify status --include-recovery-key --json

verify status 会报告三个独立的信任信号(--verbose 会显示全部):

  • Locally trusted:仅受此客户端信任
  • Cross-signing verified:SDK 报告已通过 cross-signing 验证
  • Signed by owner:由你自己的 self-signing 密钥签名(仅用于诊断)

只有当 Cross-signing verifiedyes 时,Verified by owner 才会变为 yes。仅有本地信任或所有者签名并不足够。

--allow-degraded-local-state 会在不先准备 Matrix 账户的情况下返回尽力而为的诊断;适用于离线或部分配置的探测。

使用恢复密钥验证此设备

恢复密钥是敏感信息,请通过 stdin 管道传入,而不是在命令行上传递。设置 MATRIX_RECOVERY_KEY(或为命名账户设置 MATRIX_<ID>_RECOVERY_KEY):

printf '%s\n' "$MATRIX_RECOVERY_KEY" | openclaw matrix verify device --recovery-key-stdin

该命令报告三种状态:

  • Recovery key accepted:Matrix 已接受该密钥,用于 secret storage 或设备信任。
  • Backup usable:可以使用受信任的恢复材料加载房间密钥备份。
  • Device verified by owner:此设备具有完整的 Matrix cross-signing 身份信任。

当完整身份信任不完整时,即使恢复密钥已解锁备份材料,命令也会以非零状态退出。在这种情况下,请从另一个 Matrix 客户端完成自验证:

openclaw matrix verify self

verify self 会等待 Cross-signing verified: yes,然后才成功退出。使用 --timeout-ms <ms> 调整等待时间。

字面密钥形式 openclaw matrix verify device "<recovery-key>" 也被接受,但密钥会进入你的 shell 历史记录。

引导或修复 cross-signing

openclaw matrix verify bootstrap

verify bootstrap 是加密账户的修复和设置命令。按顺序,它会:

  • 引导 secret storage,并在可能时复用现有恢复密钥
  • 引导 cross-signing 并上传缺失的公钥
  • 标记并 cross-sign 当前设备
  • 如果还不存在服务端房间密钥备份,则创建一个

如果 homeserver 要求 UIA 才能上传 cross-signing 密钥,OpenClaw 会先尝试无认证,然后尝试 m.login.dummy,再尝试 m.login.password(需要 channels.matrix.password)。

实用标志:

  • --recovery-key-stdin(配合 printf '%s\n' "$MATRIX_RECOVERY_KEY" | …)或 --recovery-key <key>
  • --force-reset-cross-signing 用于丢弃当前 cross-signing 身份(仅在有意这样做时使用)

房间密钥备份

openclaw matrix verify backup status
printf '%s\n' "$MATRIX_RECOVERY_KEY" | openclaw matrix verify backup restore --recovery-key-stdin

backup status 会显示是否存在服务端备份,以及此设备是否可以解密它。backup restore 会将已备份的房间密钥导入本地加密存储;如果恢复密钥已在磁盘上,可以省略 --recovery-key-stdin

要用新的基线替换损坏的备份(接受丢失无法恢复的旧历史;如果当前备份密钥无法加载,也可以重新创建 secret storage):

openclaw matrix verify backup reset --yes

仅当你有意让之前的恢复密钥停止解锁新的备份基线时,才添加 --rotate-recovery-key

列出、请求和响应验证

openclaw matrix verify list

列出所选账户的待处理验证请求。

openclaw matrix verify request --own-user
openclaw matrix verify request --user-id @ops:example.org --device-id ABCDEF

从此 OpenClaw 账户发送验证请求。--own-user 请求自验证(你在同一用户的另一个 Matrix 客户端中接受提示);--user-id/--device-id/--room-id 定位其他人。--own-user 不能与其他定位标志组合使用。

对于更底层的生命周期处理,通常是在跟随来自另一个客户端的入站请求时,这些命令会作用于特定请求 <id>(由 verify listverify request 打印):

命令 用途
openclaw matrix verify accept <id> 接受入站请求
openclaw matrix verify start <id> 启动 SAS 流程
openclaw matrix verify sas <id> 打印 SAS emoji 或十进制数字
openclaw matrix verify confirm-sas <id> 确认 SAS 与另一个客户端显示的内容匹配
openclaw matrix verify mismatch-sas <id> 当 emoji 或十进制数字不匹配时拒绝 SAS
openclaw matrix verify cancel <id> 取消;接受可选的 --reason <text>--code <matrix-code>

当验证锚定到特定私信房间时,acceptstartsasconfirm-sasmismatch-sascancel 都接受 --user-id--room-id 作为私信后续提示。

多账户注意事项

如果没有 --account <id>,Matrix CLI 命令会使用隐式默认账户。如果你有多个命名账户且未设置 channels.matrix.defaultAccount,它们会拒绝猜测并要求你选择。当某个命名账户的 E2EE 被禁用或不可用时,错误会指向该账户的配置键,例如 channels.matrix.accounts.assistant.encryption



使用 encryption: true 时,startupVerification 默认值为 "if-unverified"。启动时,未验证设备会在另一个 Matrix 客户端中请求自验证,跳过重复请求并应用冷却时间(默认 24 小时)。使用 startupVerificationCooldownHours 调整,或使用 startupVerification: "off" 禁用。

启动时还会运行一次保守的加密引导流程,复用当前的 secret storage 和 cross-signing 身份。如果引导状态损坏,即使没有 `channels.matrix.password`,OpenClaw 也会尝试受保护的修复;如果 homeserver 要求密码 UIA,启动会记录警告并保持非致命。已由所有者签名的设备会被保留。

请参阅 [Matrix 迁移](/zh-CN/channels/matrix-migration) 了解完整升级流程。


Matrix 会将验证生命周期通知作为 m.notice 消息发布到严格的私信验证房间中:请求、就绪(带有“通过 emoji 验证”指引)、开始/完成,以及可用时的 SAS(emoji/十进制)详情。

来自另一个 Matrix 客户端的传入请求会被跟踪并自动接受。对于自验证,OpenClaw 会自动启动 SAS 流程,并在 emoji 验证可用后确认自己这一侧;你仍需要在自己的 Matrix 客户端中比较并确认“它们匹配”。

验证系统通知不会转发到智能体聊天管道。


如果 verify status 表示当前设备不再列在 homeserver 上,请创建新的 OpenClaw Matrix 设备。对于密码登录:

openclaw matrix account add \
  --account assistant \
  --homeserver https://matrix.example.org \
  --user-id '@assistant:example.org' \
  --password '<password>' \
  --device-name OpenClaw-Gateway
对于令牌认证,请在你的 Matrix 客户端或管理 UI 中创建新的访问令牌,然后更新 OpenClaw:
openclaw matrix account add \
  --account assistant \
  --homeserver https://matrix.example.org \
  --access-token '<token>'
 `assistant` 替换为失败命令中的账户 ID,或省略 `--account` 以使用默认账户。


旧的 OpenClaw 管理设备可能会累积。列出并清理:

openclaw matrix devices list
openclaw matrix devices prune-stale


Matrix E2EE 使用官方 matrix-js-sdk Rust 加密路径,并以 fake-indexeddb 作为 IndexedDB shim。加密状态会持久化到 crypto-idb-snapshot.json(限制性文件权限)。

加密运行时状态位于 `~/.openclaw/matrix/accounts/<account>/<homeserver>__<user>/<token-hash>/` 下,包含同步存储、加密存储、恢复密钥、IDB 快照、线程绑定和启动验证状态。当令牌发生变化但账户身份保持不变时,OpenClaw 会复用最佳现有根目录,因此先前状态仍然可见。


个人资料管理

更新所选账户的 Matrix 自身个人资料:

openclaw matrix profile set --name "OpenClaw Assistant"
openclaw matrix profile set --avatar-url https://cdn.example.org/avatar.png

你可以在一次调用中同时传入两个选项。Matrix 会直接接受 mxc:// 头像 URL;当你传入 http://https:// 时,OpenClaw 会先上传文件,并将解析后的 mxc:// URL 存入 channels.matrix.avatarUrl(或每账户覆盖项)。

线程

Matrix 支持原生 Matrix 线程,可用于自动回复和消息工具发送。两个独立开关控制行为:

会话路由(sessionScope

dm.sessionScope 决定 Matrix 私信房间如何映射到 OpenClaw 会话:

  • "per-user"(默认):与同一路由对端相关的所有私信房间共享一个会话。
  • "per-room":每个 Matrix 私信房间都有自己的会话键,即使对端相同也是如此。

显式对话绑定始终优先于 sessionScope,因此已绑定的房间和线程会保留其所选目标会话。

回复线程化(threadReplies

threadReplies 决定机器人在哪里发布回复:

  • "off":回复位于顶层。入站线程消息保留在父会话中。
  • "inbound":仅当入站消息已经在线程中时,才在线程内回复。
  • "always":在线程内回复,线程根为触发消息;从首次触发开始,该对话会通过匹配的线程作用域会话进行路由。

dm.threadReplies 仅针对私信覆盖此设置,例如让房间线程保持隔离,同时让私信保持扁平。

线程继承和斜杠命令

  • 入站线程消息会把线程根消息作为额外智能体上下文包含进来。
  • 消息工具发送在目标为同一房间(或同一私信用户目标)时,会自动继承当前 Matrix 线程,除非提供了显式的 threadId
  • 只有当前会话元数据能证明同一 Matrix 账号上的同一私信对端时,才会复用私信用户目标;否则 OpenClaw 会回退到正常的用户作用域路由。
  • /focus/unfocus/agents/session idle/session max-age 和绑定线程的 /acp spawn 都可在 Matrix 房间和私信中使用。
  • threadBindings.spawnSessions 启用时,顶层 /focus 会创建一个新的 Matrix 线程,并将其绑定到目标会话。
  • 在现有 Matrix 线程中运行 /focus/acp spawn --thread here 会就地绑定该线程。

当 OpenClaw 检测到某个 Matrix 私信房间与同一共享会话上的另一个私信房间冲突时,它会在该房间发布一次性 m.notice,指向 /focus 逃生通道,并建议更改 dm.sessionScope。该通知只会在线程绑定启用时出现。

ACP 对话绑定

Matrix 房间、私信和现有 Matrix 线程可以转换为持久 ACP 工作区,而不改变聊天界面。

快速操作流程:

  • 在你想继续使用的 Matrix 私信、房间或现有线程中运行 /acp spawn codex --bind here
  • 在顶层 Matrix 私信或房间中,当前私信/房间会保持为聊天界面,未来消息会路由到生成的 ACP 会话。
  • 在现有 Matrix 线程中,--bind here 会就地绑定当前线程。
  • /new/reset 会就地重置同一个已绑定的 ACP 会话。
  • /acp close 会关闭 ACP 会话并移除绑定。

注意:

  • --bind here 不会创建子 Matrix 线程。
  • threadBindings.spawnSessions 控制 /acp spawn --thread auto|here,这时 OpenClaw 需要创建或绑定子 Matrix 线程。

线程绑定配置

Matrix 会继承 session.threadBindings 的全局默认值,也支持按渠道覆盖:

  • threadBindings.enabled
  • threadBindings.idleHours
  • threadBindings.maxAgeHours
  • threadBindings.spawnSessions
  • threadBindings.defaultSpawnContext

Matrix 绑定线程的会话生成默认开启:

  • 设置 threadBindings.spawnSessions: false 可阻止顶层 /focus/acp spawn --thread auto|here 创建/绑定 Matrix 线程。
  • 当原生子智能体线程生成不应 fork 父级转录内容时,设置 threadBindings.defaultSpawnContext: "isolated"

反应

Matrix 支持出站反应、入站反应通知和确认反应。

出站反应工具由 channels.matrix.actions.reactions 控制:

  • react 向 Matrix 事件添加反应。
  • reactions 列出 Matrix 事件的当前反应摘要。
  • emoji="" 移除该事件上机器人的自身反应。
  • remove: true 只移除机器人指定的 emoji 反应。

解析顺序(第一个已定义值获胜):

设置 顺序
ackReaction 按账号 → 渠道 → messages.ackReaction → 智能体身份 emoji 回退
ackReactionScope 按账号 → 渠道 → messages.ackReactionScope → 默认 "group-mentions"
reactionNotifications 按账号 → 渠道 → 默认 "own"

reactionNotifications: "own" 会在新增的 m.reaction 事件指向机器人所写 Matrix 消息时转发它们;"off" 会禁用反应系统事件。反应移除不会被合成为系统事件,因为 Matrix 将这些表示为撤回,而不是独立的 m.reaction 移除。

历史上下文

  • channels.matrix.historyLimit 控制当 Matrix 房间消息触发智能体时,作为 InboundHistory 包含的最近房间消息数量。会回退到 messages.groupChat.historyLimit;如果两者都未设置,有效默认值为 0。设置为 0 可禁用。
  • Matrix 房间历史仅限房间。私信继续使用正常会话历史。
  • Matrix 房间历史仅限待处理消息:OpenClaw 会缓冲尚未触发回复的房间消息,然后在提及或其他触发到达时快照该窗口。
  • 当前触发消息不会包含在 InboundHistory 中;它会保留在该轮次的主入站正文中。
  • 同一 Matrix 事件的重试会复用原始历史快照,而不是向前漂移到更新的房间消息。

上下文可见性

Matrix 支持共享的 contextVisibility 控制,用于补充房间上下文,例如获取的回复文本、线程根和待处理历史。

  • contextVisibility: "all" 是默认值。补充上下文会按接收时保留。
  • contextVisibility: "allowlist" 会过滤补充上下文,只发送通过活跃房间/用户 allowlist 检查的发送者内容。
  • contextVisibility: "allowlist_quote" 的行为类似 allowlist,但仍保留一条显式引用回复。

此设置影响补充上下文的可见性,不影响入站消息本身是否可以触发回复。
触发授权仍来自 groupPolicygroupsgroupAllowFrom 和私信策略设置。

私信和房间策略

{
  channels: {
    matrix: {
      dm: {
        policy: "allowlist",
        allowFrom: ["@admin:example.org"],
        threadReplies: "off",
      },
      groupPolicy: "allowlist",
      groupAllowFrom: ["@admin:example.org"],
      groups: {
        "!roomid:example.org": { requireMention: true },
      },
    },
  },
}

要在保持房间可用的同时完全静默私信,请设置 dm.enabled: false

{
  channels: {
    matrix: {
      dm: { enabled: false },
      groupPolicy: "allowlist",
      groupAllowFrom: ["@admin:example.org"],
    },
  },
}

有关提及门控和 allowlist 行为,请参阅 Groups

Matrix 私信的配对示例:

openclaw pairing list matrix
openclaw pairing approve matrix <CODE>

如果未经批准的 Matrix 用户在批准前持续给你发消息,OpenClaw 会复用同一个待处理配对码,并可能在短暂冷却后发送提醒回复,而不是生成新代码。

有关共享私信配对流程和存储布局,请参阅 配对

直接房间修复

如果直接消息状态不同步,OpenClaw 可能会得到过期的 m.direct 映射,指向旧的单人房间,而不是当前可用的私信。检查某个对端的当前映射:

openclaw matrix direct inspect --user-id @alice:example.org

修复它:

openclaw matrix direct repair --user-id @alice:example.org

这两个命令都接受 --account <id>,用于多账号设置。修复流程:

  • 优先选择已经映射在 m.direct 中的严格 1:1 私信
  • 回退到与该用户当前已加入的任何严格 1:1 私信
  • 如果不存在健康的私信,则创建新的直接房间并重写 m.direct

它不会自动删除旧房间。它会选择健康的私信并更新映射,使未来的 Matrix 发送、验证通知和其他直接消息流程指向正确的房间。

Exec 批准

Matrix 可以充当原生批准客户端。在 channels.matrix.execApprovals 下配置(或在 channels.matrix.accounts.<account>.execApprovals 下配置按账号覆盖):

  • enabled:通过 Matrix 原生提示传递批准。未设置或为 "auto" 时,只要至少能解析出一个批准者,Matrix 就会自动启用。设置为 false 可显式禁用。
  • approvers:允许批准 exec 请求的 Matrix 用户 ID(@owner:example.org)。可选,回退到 channels.matrix.dm.allowFrom
  • target:提示发送位置。"dm"(默认)发送到批准者私信;"channel" 发送到发起的 Matrix 房间或私信;"both" 同时发送到两者。
  • agentFilter / sessionFilter:可选 allowlist,用于指定哪些智能体/会话触发 Matrix 投递。

不同批准类型的授权略有差异:

  • Exec 批准使用 execApprovals.approvers,回退到 dm.allowFrom
  • 插件批准只通过 dm.allowFrom 授权。

两种类型共享 Matrix 反应快捷方式和消息更新。批准者会在主批准消息上看到反应快捷方式:

  • 允许一次
  • 拒绝
  • ♾️ 始终允许(当有效 exec 策略允许时)

回退斜杠命令:/approve <id> allow-once/approve <id> allow-always/approve <id> deny

只有已解析的批准者可以批准或拒绝。exec 批准的渠道投递会包含命令文本,请只在受信任房间中启用 channelboth

相关:Exec approvals

斜杠命令

斜杠命令(/new/reset/model/focus/unfocus/agents/session/acp/approve 等)可直接在私信中使用。在房间中,OpenClaw 也会识别以机器人自身 Matrix 提及为前缀的命令,因此 @bot:server /new 会触发命令路径,而不需要自定义提及正则。这让机器人能够响应 Element 和类似客户端在用户输入命令前通过 Tab 补全机器人时发出的房间风格 @mention /command 帖子。

授权规则仍然适用:命令发送者必须满足与普通消息相同的私信或房间 allowlist/所有者策略。

多账号

{
  channels: {
    matrix: {
      enabled: true,
      defaultAccount: "assistant",
      dm: { policy: "pairing" },
      accounts: {
        assistant: {
          homeserver: "https://matrix.example.org",
          accessToken: "syt_assistant_xxx",
          encryption: true,
        },
        alerts: {
          homeserver: "https://matrix.example.org",
          accessToken: "syt_alerts_xxx",
          dm: {
            policy: "allowlist",
            allowFrom: ["@ops:example.org"],
            threadReplies: "off",
          },
        },
      },
    },
  },
}

继承:

  • 顶层 channels.matrix 值会作为命名账号的默认值,除非账号覆盖它们。
  • 使用 groups.<room>.account 将继承的房间条目限定到特定账号。没有 account 的条目会在账号之间共享;当默认账号在顶层配置时,account: "default" 仍然可用。

默认账号选择:

  • 设置 defaultAccount 可选择隐式路由、探测和 CLI 命令优先使用的命名账号。
  • 如果你有多个账号,并且其中一个字面上命名为 default,即使未设置 defaultAccount,OpenClaw 也会隐式使用它。
  • 如果你有多个命名账号且未选择默认账号,CLI 命令会拒绝猜测,请设置 defaultAccount 或传递 --account <id>
  • 只有当其认证完整(homeserver + accessToken,或 homeserver + userId + password)时,顶层 channels.matrix.* 块才会被视为隐式 default 账号。只要缓存凭据覆盖认证,命名账号仍可通过 homeserver + userId 发现。

提升:

  • 当 OpenClaw 在修复或设置期间将单账号配置提升为多账号时,如果已有命名账号或 defaultAccount 已指向某个账号,它会保留该账号。只有 Matrix 认证/引导键会移动到提升后的账号;共享投递策略键会留在顶层。

有关共享多账号模式,请参阅 配置参考

私有/LAN homeserver

默认情况下,出于 SSRF 防护,OpenClaw 会阻止私有/内部 Matrix homeserver,除非你按账号显式选择启用。

如果你的 homeserver 运行在 localhost、LAN/Tailscale IP 或内部主机名上,请为该 Matrix 账号启用 network.dangerouslyAllowPrivateNetwork

{
  channels: {
    matrix: {
      homeserver: "http://matrix-synapse:8008",
      network: {
        dangerouslyAllowPrivateNetwork: true,
      },
      accessToken: "syt_internal_xxx",
    },
  },
}

CLI 设置示例:

openclaw matrix account add \
  --account ops \
  --homeserver http://matrix-synapse:8008 \
  --allow-private-network \
  --access-token syt_ops_xxx

此选择启用项仅允许受信任的私有/内部目标。公共明文 homeserver,例如
http://matrix.example.org:8008,仍会被阻止。尽可能优先使用 https://

代理 Matrix 流量

如果你的 Matrix 部署需要显式的出站 HTTP(S) 代理,请设置 channels.matrix.proxy

{
  channels: {
    matrix: {
      homeserver: "https://matrix.example.org",
      accessToken: "syt_bot_xxx",
      proxy: "http://127.0.0.1:7890",
    },
  },
}

具名账号可以用 channels.matrix.accounts.<id>.proxy 覆盖顶层默认值。
OpenClaw 对运行时 Matrix 流量和账号状态探测使用相同的代理设置。

目标解析

在 OpenClaw 要求你提供房间或用户目标的任何位置,Matrix 都接受以下目标形式:

  • 用户:@user:serveruser:@user:servermatrix:user:@user:server
  • 房间:!room:serverroom:!room:servermatrix:room:!room:server
  • 别名:#alias:serverchannel:#alias:servermatrix:channel:#alias:server

Matrix 房间 ID 区分大小写。配置显式投递目标、cron 作业、绑定或允许列表时,
请使用 Matrix 中准确的房间 ID 大小写。OpenClaw 会将内部会话键规范化后用于存储,
因此这些小写键不能作为 Matrix 投递 ID 的可靠来源。

实时目录查找使用已登录的 Matrix 账号:

  • 用户查找会查询该 homeserver 上的 Matrix 用户目录。
  • 房间查找会直接接受显式房间 ID 和别名。已加入房间的名称查找是尽力而为的,并且仅在设置了 dangerouslyAllowNameMatching: true 时适用于运行时房间允许列表。
  • 如果房间名称无法解析为 ID 或别名,运行时允许列表解析会忽略它。

配置参考

允许列表式用户字段(groupAllowFromdm.allowFromgroups.<room>.users)接受完整的 Matrix 用户 ID(最安全)。默认会忽略非 ID 用户条目。如果你设置 dangerouslyAllowNameMatching: true,会在启动时以及监控器运行期间允许列表发生变化时,解析精确匹配的 Matrix 目录显示名称;运行时会忽略无法解析的条目。

房间允许列表键(groups,旧版 rooms)应为房间 ID 或别名。默认会忽略普通房间名称键;dangerouslyAllowNameMatching: true 会恢复基于已加入房间名称的尽力查找。

账号和连接

  • enabled:启用或禁用该渠道。
  • name:账号的可选显示标签。
  • defaultAccount:配置多个 Matrix 账号时首选的账号 ID。
  • accounts:具名的逐账号覆盖。顶层 channels.matrix 值会作为默认值继承。
  • homeserver:homeserver URL,例如 https://matrix.example.org
  • network.dangerouslyAllowPrivateNetwork:允许此账号连接到 localhost、LAN/Tailscale IP 或内部主机名。
  • proxy:Matrix 流量的可选 HTTP(S) 代理 URL。支持逐账号覆盖。
  • userId:完整的 Matrix 用户 ID(@bot:example.org)。
  • accessToken:基于令牌认证的访问令牌。支持跨 env/file/exec 提供商的明文值和 SecretRef 值(密钥管理)。
  • password:基于密码登录的密码。支持明文值和 SecretRef 值。
  • deviceId:显式 Matrix 设备 ID。
  • deviceName:密码登录时使用的设备显示名称。
  • avatarUrl:用于资料同步和 profile set 更新的已存储自头像 URL。
  • initialSyncLimit:启动同步期间获取的最大事件数。

加密

  • encryption:启用 E2EE。默认值:false
  • startupVerification"if-unverified"(E2EE 开启时的默认值)或 "off"。当此设备未验证时,启动时自动请求自验证。
  • startupVerificationCooldownHours:下一次自动启动请求之前的冷却时间。默认值:24

访问和策略

  • groupPolicy"open""allowlist""disabled"。默认值:"allowlist"
  • groupAllowFrom:房间流量的用户 ID 允许列表。
  • dm.enabled:当为 false 时,忽略所有私信。默认值:true
  • dm.policy"pairing"(默认)、"allowlist""open""disabled"。在机器人已加入并将房间分类为私信之后应用;不会影响邀请处理。
  • dm.allowFrom:私信流量的用户 ID 允许列表。
  • dm.sessionScope"per-user"(默认)或 "per-room"
  • dm.threadReplies:仅私信用的回复线程覆盖("off""inbound""always")。
  • allowBots:接受来自其他已配置 Matrix 机器人账号的消息(true"mentions")。
  • allowlistOnly:当为 true 时,会强制所有活动私信策略(除了 "disabled")和 "open" 群组策略改为 "allowlist"。不会更改 "disabled" 策略。
  • dangerouslyAllowNameMatching:当为 true 时,允许对用户允许列表条目执行 Matrix 显示名称目录查找,并对房间允许列表键执行已加入房间名称查找。优先使用完整的 @user:server ID 以及房间 ID 或别名。
  • autoJoin"always""allowlist""off"。默认值:"off"。适用于每个 Matrix 邀请,包括私信式邀请。
  • autoJoinAllowlist:当 autoJoin"allowlist" 时允许的房间/别名。别名条目会针对 homeserver 解析,而不是针对受邀房间声明的状态解析。
  • contextVisibility:补充上下文可见性(默认 "all""allowlist""allowlist_quote")。

回复行为

  • replyToMode"off""first""all""batched"
  • threadReplies"off""inbound""always"
  • threadBindings:线程绑定会话路由和生命周期的逐渠道覆盖。
  • streaming"off"(默认)、"partial""quiet",或对象形式 { mode, preview: { toolProgress } }true"partial"false"off"
  • blockStreaming:当为 true 时,已完成的 assistant 块会保留为单独的进度消息。
  • markdown:出站文本的可选 Markdown 渲染配置。
  • responsePrefix:追加到出站回复前的可选字符串。
  • textChunkLimit:当 chunkMode: "length" 时,出站分块的字符大小。默认值:4000
  • chunkMode"length"(默认,按字符数拆分)或 "newline"(按行边界拆分)。
  • historyLimit:当房间消息触发智能体时,作为 InboundHistory 包含的最近房间消息数量。回退到 messages.groupChat.historyLimit;有效默认值为 0(禁用)。
  • mediaMaxMb:出站发送和入站处理的媒体大小上限,单位为 MB。

表情回应设置

  • ackReaction:此渠道/账号的确认表情回应覆盖。
  • ackReactionScope:范围覆盖(默认 "group-mentions""group-all""direct""all""none""off")。
  • reactionNotifications:入站表情回应通知模式(默认 "own""off")。

工具和逐房间覆盖

  • actions:逐操作工具门控(messagesreactionspinsprofilememberInfochannelInfoverification)。
  • groups:逐房间策略映射。解析后,会话身份使用稳定的房间 ID。(rooms 是旧版别名。)
  • groups.<room>.account:将一个继承的房间条目限制到特定账号。
  • groups.<room>.allowBots:渠道级设置的逐房间覆盖(true"mentions")。
  • groups.<room>.users:逐房间发送者允许列表。
  • groups.<room>.tools:逐房间工具允许/拒绝覆盖。
  • groups.<room>.autoReply:逐房间提及门控覆盖。true 会禁用该房间的提及要求;false 会强制重新开启。
  • groups.<room>.skills:逐房间技能过滤器。
  • groups.<room>.systemPrompt:逐房间系统提示片段。

Exec 审批设置

  • execApprovals.enabled:通过 Matrix 原生提示投递 exec 审批。
  • execApprovals.approvers:允许审批的 Matrix 用户 ID。回退到 dm.allowFrom
  • execApprovals.target"dm"(默认)、"channel""both"
  • execApprovals.agentFilter / execApprovals.sessionFilter:用于投递的可选智能体/会话允许列表。

相关内容


📄 Mattermost

原文:https://docs.openclaw.ai/zh-CN/channels/mattermost

Status:可下载插件(bot token + WebSocket 事件)。支持频道、群组和私信。Mattermost 是一个可自托管的团队消息平台;产品详情和下载请参见官方网站 mattermost.com

安装

在配置渠道之前安装 Mattermost:



bash
openclaw plugins install @openclaw/mattermost



bash
openclaw plugins install ./path/to/local/mattermost-plugin


详情:插件

快速设置



当前打包的 OpenClaw 版本已经内置它。较旧版本/自定义安装可以用上面的命令手动添加。


创建一个 Mattermost bot 账号并复制 bot token


复制 Mattermost base URL(例如 https://chat.example.com)。


最小配置:

```json5
{
  channels: {
    mattermost: {
      enabled: true,
      botToken: "mm-token",
      baseUrl: "https://chat.example.com",
      dmPolicy: "pairing",
    },
  },
}
```


原生斜杠命令

原生斜杠命令是选择启用的。启用后,OpenClaw 会通过 Mattermost API 注册 oc_* 斜杠命令,并在 Gateway 网关 HTTP 服务器上接收回调 POST。

{
  channels: {
    mattermost: {
      commands: {
        native: true,
        nativeSkills: true,
        callbackPath: "/api/channels/mattermost/command",
        // Use when Mattermost cannot reach the gateway directly (reverse proxy/public URL).
        callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
      },
    },
  },
}



- 对于 Mattermost,native: "auto" 默认禁用。设置 native: true 以启用。
- 如果省略 callbackUrl,OpenClaw 会根据 Gateway 网关 host/port + callbackPath 推导一个。
- 对于多账号设置,commands 可以设在顶层,也可以设在 channels.mattermost.accounts.<id>.commands 下(账号值会覆盖顶层字段)。
- 命令回调会使用 Mattermost 在 OpenClaw 注册 oc_* 命令时返回的每条命令 token 进行验证。
- OpenClaw 会在接受每个回调前刷新当前 Mattermost 命令注册,因此已删除或重新生成的斜杠命令的过期 token 无需重启 Gateway 网关也会停止被接受。
- 如果 Mattermost API 无法确认该命令仍是当前命令,回调验证会失败关闭;失败的验证会被短暂缓存,并发查找会被合并,新的查找启动会按命令限速,以限制重放压力。
- 当注册失败、启动不完整,或回调 token 与解析出的命令注册 token 不匹配时,斜杠回调会失败关闭(对一个命令有效的 token 不能到达另一个命令的上游验证)。



回调端点必须能从 Mattermost 服务器访问。

- 不要将 `callbackUrl` 设置为 `localhost`,除非 Mattermost  OpenClaw 运行在同一主机/网络命名空间中。
- 不要将 `callbackUrl` 设置为你的 Mattermost base URL,除非该 URL  `/api/channels/mattermost/command` 反向代理到 OpenClaw。
- 快速检查方式是 `curl https://<gateway-host>/api/channels/mattermost/command`;GET 应返回 OpenClaw  `405 Method Not Allowed`,而不是 `404`



如果你的回调目标是私有/tailnet/内部地址,请将 Mattermost ServiceSettings.AllowedUntrustedInternalConnections 设置为包含回调主机/域名。

使用主机/域名条目,而不是完整 URL。

- 正确:`gateway.tailnet-name.ts.net`
- 错误:`https://gateway.tailnet-name.ts.net`


环境变量(默认账号)

如果你偏好环境变量,请在 Gateway 网关主机上设置这些:

  • MATTERMOST_BOT_TOKEN=...
  • MATTERMOST_URL=https://chat.example.com


环境变量只适用于默认账号(default)。其他账号必须使用配置值。

MATTERMOST_URL 不能从工作区 .env 设置;请参见工作区 .env 文件

聊天模式

Mattermost 会自动响应私信。频道行为由 chatmode 控制:



仅在频道中被 @提及时响应。


响应每条频道消息。


当消息以触发前缀开头时响应。

配置示例:

{
  channels: {
    mattermost: {
      chatmode: "onchar",
      oncharPrefixes: [">", "!"],
    },
  },
}

说明:

  • onchar 仍会响应显式 @提及。
  • 旧版配置会继续遵循 channels.mattermost.requireMention,但推荐使用 chatmode

线程和会话

使用 channels.mattermost.replyToMode 控制频道和群组回复是留在主频道中,还是在触发帖下启动线程。

  • off(默认):仅当入站帖已经在线程中时,才在线程中回复。
  • first:对于顶层频道/群组帖,在该帖下启动线程,并将对话路由到线程作用域的会话。
  • all:目前在 Mattermost 中行为与 first 相同。
  • 直接消息会忽略此设置并保持非线程化。

配置示例:

{
  channels: {
    mattermost: {
      replyToMode: "all",
    },
  },
}

说明:

  • 线程作用域的会话使用触发帖 id 作为线程根。
  • firstall 目前等价,因为一旦 Mattermost 有了线程根,后续分块和媒体会继续在同一线程中发送。

访问控制(私信)

  • 默认:channels.mattermost.dmPolicy = "pairing"(未知发送者会收到配对码)。
  • 通过以下命令批准:
  • openclaw pairing list mattermost
  • openclaw pairing approve mattermost <CODE>
  • 公开私信:channels.mattermost.dmPolicy="open"channels.mattermost.allowFrom=["*"]
  • channels.mattermost.allowFrom 接受 accessGroup:<name> 条目。参见访问组

频道(群组)

  • 默认:channels.mattermost.groupPolicy = "allowlist"(需要提及)。
  • 使用 channels.mattermost.groupAllowFrom 对发送者进行 allowlist(推荐使用用户 ID)。
  • channels.mattermost.groupAllowFrom 接受 accessGroup:<name> 条目。参见访问组
  • 每个频道的提及覆盖项位于 channels.mattermost.groups.<channelId>.requireMention 下,默认值可用 channels.mattermost.groups["*"].requireMention
  • @username 匹配是可变的,并且仅在 channels.mattermost.dangerouslyAllowNameMatching: true 时启用。
  • 开放频道:channels.mattermost.groupPolicy="open"(需要提及)。
  • 运行时说明:如果完全缺少 channels.mattermost,运行时会回退到 groupPolicy="allowlist" 进行群组检查(即使已设置 channels.defaults.groupPolicy)。

示例:

{
  channels: {
    mattermost: {
      groupPolicy: "open",
      groups: {
        "*": { requireMention: true },
        "team-channel-id": { requireMention: false },
      },
    },
  },
}

出站投递目标

将这些目标格式用于 openclaw message send 或 cron/webhooks:

  • channel:<id> 表示频道
  • user:<id> 表示私信
  • @username 表示私信(通过 Mattermost API 解析)


裸不透明 ID(如 64ifufp...)在 Mattermost 中是有歧义的(用户 ID 与频道 ID)。

OpenClaw 会按用户优先解析它们:

  • 如果该 ID 作为用户存在(GET /api/v4/users/<id> 成功),OpenClaw 会通过 /api/v4/channels/direct 解析直接频道并发送私信
  • 否则,该 ID 会被视为频道 ID

如果你需要确定性行为,请始终使用显式前缀(user:<id> / channel:<id>)。

私信频道重试

当 OpenClaw 发送到 Mattermost 私信目标且需要先解析直接频道时,它默认会重试短暂性的直接频道创建失败。

使用 channels.mattermost.dmChannelRetry 为 Mattermost 插件全局调整该行为,或使用 channels.mattermost.accounts.<id>.dmChannelRetry 为某个账号调整。

{
  channels: {
    mattermost: {
      dmChannelRetry: {
        maxRetries: 3,
        initialDelayMs: 1000,
        maxDelayMs: 10000,
        timeoutMs: 30000,
      },
    },
  },
}

说明:

  • 这仅适用于私信频道创建(/api/v4/channels/direct),不是每个 Mattermost API 调用。
  • 重试适用于短暂性失败,例如限速、5xx 响应以及网络或超时错误。
  • 429 之外的 4xx 客户端错误会被视为永久错误,不会重试。

预览流式传输

Mattermost 会将思考、工具活动和部分回复文本流式传输到单个草稿预览帖中,并在最终答案可安全发送时就地完成。预览会在同一个帖 id 上更新,而不是用逐块消息刷屏频道。媒体/错误最终消息会取消待处理的预览编辑,并使用正常投递,而不是刷新一个一次性预览帖。

通过 channels.mattermost.streaming 启用:

{
  channels: {
    mattermost: {
      streaming: "partial", // off | partial | block | progress
    },
  },
}



- partial 是常用选择:一个预览帖会随着回复增长而被编辑,然后用完整答案最终完成。
- block 在预览帖内使用追加式草稿分块。
- progress 在生成时显示状态预览,并且只在完成时发布最终答案。
- off 禁用预览流式传输。



- 如果流无法就地完成(例如帖在流式传输中途被删除),OpenClaw 会回退到发送新的最终帖,确保回复不会丢失。
- 仅推理载荷会从频道帖中抑制,包括作为 > Reasoning: blockquote 到达的文本。设置 /reasoning on 可在其他表面查看思考;Mattermost 最终帖只保留答案。
- 有关频道映射矩阵,请参见流式传输


表情回应(消息工具)

  • 使用 message action=react 并设置 channel=mattermost
  • messageId 是 Mattermost 帖 id。
  • emoji 接受类似 thumbsup:+1: 的名称(冒号可选)。
  • 设置 remove=true(布尔值)以移除表情回应。
  • 添加/移除表情回应事件会作为系统事件转发到被路由的 agent 会话。

示例:

message action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsup
message action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsup remove=true

配置:

  • channels.mattermost.actions.reactions:启用/禁用表情回应操作(默认 true)。
  • 每账号覆盖:channels.mattermost.accounts.<id>.actions.reactions

交互式按钮(消息工具)

发送带可点击按钮的消息。当用户点击按钮时,agent 会收到选择并可以响应。

通过向渠道能力添加 inlineButtons 来启用按钮:

{
  channels: {
    mattermost: {
      capabilities: ["inlineButtons"],
    },
  },
}

message action=sendbuttons 参数一起使用。按钮是二维数组(按钮行):

message action=send channel=mattermost target=channel:<channelId> buttons=[[{"text":"Yes","callback_data":"yes"},{"text":"No","callback_data":"no"}]]

按钮字段:


显示标签。


点击时回传的值(用作操作 ID)。


按钮样式。

当用户点击按钮时:



所有按钮都会被替换为一行确认信息(例如,“✓ Yes selected by @user”)。


智能体会以入站消息形式接收该选择并作出响应。



- 按钮回调使用 HMAC-SHA256 验证(自动完成,无需配置)。
- Mattermost 会从其 API 响应中剥离回调数据(安全功能),因此点击后会移除所有按钮,无法部分移除。
- 包含连字符或下划线的操作 ID 会自动清理(Mattermost 路由限制)。



- channels.mattermost.capabilities:能力字符串数组。添加 "inlineButtons" 可在智能体系统提示中启用按钮工具说明。
- channels.mattermost.interactions.callbackBaseUrl:可选的外部基础 URL,用于按钮回调(例如 https://gateway.example.com)。当 Mattermost 无法直接通过 Gateway 网关的绑定主机访问它时使用此项。
- 在多账号设置中,你也可以在 channels.mattermost.accounts.<id>.interactions.callbackBaseUrl 下设置相同字段。
- 如果省略 interactions.callbackBaseUrl,OpenClaw 会从 gateway.customBindHost + gateway.port 派生回调 URL,然后回退到 http://localhost:<port>
- 可达性规则:按钮回调 URL 必须可从 Mattermost 服务器访问。只有当 Mattermost 和 OpenClaw 在同一主机/网络命名空间中运行时,localhost 才有效。
- 如果你的回调目标是私有/tailnet/内部目标,请将其主机/域名添加到 Mattermost ServiceSettings.AllowedUntrustedInternalConnections


直接 API 集成(外部脚本)

外部脚本和 webhook 可以通过 Mattermost REST API 直接发布按钮,而不必经过智能体的 message 工具。尽可能使用插件中的 buildButtonAttachments();如果发布原始 JSON,请遵循这些规则:

载荷结构:

{
  channel_id: "<channelId>",
  message: "Choose an option:",
  props: {
    attachments: [
      {
        actions: [
          {
            id: "mybutton01", // alphanumeric only - see below
            type: "button", // required, or clicks are silently ignored
            name: "Approve", // display label
            style: "primary", // optional: "default", "primary", "danger"
            integration: {
              url: "https://gateway.example.com/mattermost/interactions/default",
              context: {
                action_id: "mybutton01", // must match button id (for name lookup)
                action: "approve",
                // ... any custom fields ...
                _token: "<hmac>", // see HMAC section below
              },
            },
          },
        ],
      },
    ],
  },
}


关键规则

  1. 附件放在 props.attachments 中,而不是顶层 attachments(否则会被静默忽略)。
  2. 每个操作都需要 type: "button",没有它时点击会被静默吞掉。
  3. 每个操作都需要 id 字段,Mattermost 会忽略没有 ID 的操作。
  4. 操作 id 必须仅包含字母数字字符[a-zA-Z0-9])。连字符和下划线会破坏 Mattermost 的服务端操作路由(返回 404)。使用前请去除它们。
  5. context.action_id 必须匹配按钮的 id,这样确认消息会显示按钮名称(例如 “Approve”),而不是原始 ID。
  6. context.action_id 是必需的,没有它时交互处理器会返回 400。

HMAC 令牌生成

Gateway 网关使用 HMAC-SHA256 验证按钮点击。外部脚本必须生成与 Gateway 网关验证逻辑匹配的令牌:



HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)


使用除 _token 之外的所有字段构建上下文对象。


使用排序后的键无空格进行序列化(Gateway 网关使用带排序键的 JSON.stringify,会生成紧凑输出)。


HMAC-SHA256(key=secret, data=serializedContext)


将生成的十六进制摘要作为 _token 添加到上下文中。

Python 示例:

import hmac, hashlib, json

secret = hmac.new(
    b"openclaw-mattermost-interactions",
    bot_token.encode(), hashlib.sha256
).hexdigest()

ctx = {"action_id": "mybutton01", "action": "approve"}
payload = json.dumps(ctx, sort_keys=True, separators=(",", ":"))
token = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()

context = {**ctx, "_token": token}



- Python 的 json.dumps 默认会添加空格({"key": "val"})。使用 separators=(",", ":") 以匹配 JavaScript 的紧凑输出({"key":"val"})。
- 始终签名所有上下文字段(不含 _token)。Gateway 网关会移除 _token,然后签名剩余所有内容。只签名子集会导致静默验证失败。
- 使用 sort_keys=True,Gateway 网关会在签名前对键排序,而 Mattermost 在存储载荷时可能会重新排列上下文字段。
- 从机器人令牌派生密钥(确定性),不要使用随机字节。创建按钮的进程和执行验证的 Gateway 网关必须使用相同密钥。


目录适配器

Mattermost 插件包含一个目录适配器,会通过 Mattermost API 解析渠道和用户名。这会在 openclaw message send 以及 cron/webhook 投递中启用 #channel-name@username 目标。

无需配置,适配器使用账号配置中的机器人令牌。

多账号

Mattermost 支持在 channels.mattermost.accounts 下配置多个账号:

{
  channels: {
    mattermost: {
      accounts: {
        default: { name: "Primary", botToken: "mm-token", baseUrl: "https://chat.example.com" },
        alerts: { name: "Alerts", botToken: "mm-token-2", baseUrl: "https://alerts.example.com" },
      },
    },
  },
}

故障排除



确保机器人在该渠道中,并提及它(oncall)、使用触发前缀(onchar),或设置 chatmode: "onmessage"


- 检查机器人令牌、基础 URL,以及账号是否已启用。
- 多账号问题:环境变量只应用于 default 账号。



- Unauthorized: invalid command token.:OpenClaw 未接受回调令牌。典型原因:
- 斜杠命令注册失败,或启动时只部分完成
- 回调命中了错误的 Gateway 网关/账号
- Mattermost 仍有指向先前回调目标的旧命令
- Gateway 网关重启后没有重新激活斜杠命令
- 如果原生斜杠命令停止工作,请检查日志中是否有 mattermost: failed to register slash commandsmattermost: native slash commands enabled but no commands could be registered
- 如果省略 callbackUrl,且日志警告回调解析为 http://127.0.0.1:18789/...,那么该 URL 可能只有在 Mattermost 与 OpenClaw 运行在同一主机/网络命名空间时才可达。请改为设置显式的外部可达 commands.callbackUrl



- 按钮显示为空白框:智能体可能正在发送格式错误的按钮数据。检查每个按钮是否同时具有 textcallback_data 字段。
- 按钮会渲染但点击无效:验证 Mattermost 服务器配置中的 AllowedUntrustedInternalConnections 是否包含 127.0.0.1 localhost,并且 ServiceSettings 中的 EnablePostActionIntegration 是否为 true
- 点击按钮返回 404:按钮 id 很可能包含连字符或下划线。Mattermost 的操作路由器会在非字母数字 ID 上失效。仅使用 [a-zA-Z0-9]
- Gateway 网关日志显示 invalid _token:HMAC 不匹配。检查你是否签名了所有上下文字段(而不是子集)、是否使用排序键,以及是否使用紧凑 JSON(无空格)。参见上面的 HMAC 部分。
- Gateway 网关日志显示 missing _token in context:按钮上下文中没有 _token 字段。构建集成载荷时确保包含它。
- 确认消息显示原始 ID 而不是按钮名称:context.action_id 与按钮的 id 不匹配。将两者设置为同一个清理后的值。
- 智能体不知道按钮:将 capabilities: ["inlineButtons"] 添加到 Mattermost 渠道配置。


相关内容


📄 Microsoft Teams

原文:https://docs.openclaw.ai/zh-CN/channels/msteams

Status: 支持文本 + 私信附件;频道/群组文件发送需要 sharePointSiteId + Graph 权限(请参阅在群组聊天中发送文件)。投票通过 Adaptive Cards 发送。消息操作会公开显式的 upload-file,用于以文件优先方式发送。

内置插件

Microsoft Teams 在当前 OpenClaw 版本中作为内置插件提供,因此在常规打包构建中不需要单独安装。

如果你使用的是较旧构建,或自定义安装排除了内置 Teams,请直接安装 npm 包:

openclaw plugins install @openclaw/msteams

使用裸包名以跟随当前官方发布标签。仅在需要可复现安装时,才固定精确版本。

本地 checkout(从 git 仓库运行时):

openclaw plugins install ./path/to/local/msteams-plugin

详情:插件

快速设置

@microsoft/teams.cli 会通过单个命令处理机器人注册、清单创建和凭证生成。

1. 安装并登录

npm install -g @microsoft/teams.cli@preview
teams login
teams status   # verify you're logged in and see your tenant info


Teams CLI 目前处于预览阶段。命令和标志可能会随版本变化。

2. 启动隧道(Teams 无法访问 localhost)

如果你还没有安装和认证 devtunnel CLI,请先完成(入门指南)。

# One-time setup (persistent URL across sessions):
devtunnel create my-openclaw-bot --allow-anonymous
devtunnel port create my-openclaw-bot -p 3978 --protocol auto

# Each dev session:
devtunnel host my-openclaw-bot
# Your endpoint: https://<tunnel-id>.devtunnels.ms/api/messages


需要 --allow-anonymous,因为 Teams 无法使用 devtunnels 进行认证。每个传入的机器人请求仍会由 Teams SDK 自动验证。

替代方案:ngrok http 3978tailscale funnel 3978(但这些可能在每个会话中更改 URL)。

3. 创建应用

teams app create \
  --name "OpenClaw" \
  --endpoint "https://<your-tunnel-url>/api/messages"

这个单一命令会:

  • 创建 Entra ID(Azure AD)应用
  • 生成客户端密钥
  • 构建并上传 Teams 应用清单(含图标)
  • 注册机器人(默认由 Teams 托管 - 不需要 Azure 订阅)

输出会显示 CLIENT_IDCLIENT_SECRETTENANT_ID 和一个 Teams App ID - 请记下这些信息供后续步骤使用。它也会提供直接在 Teams 中安装应用的选项。

4. 使用输出中的凭证配置 OpenClaw

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<CLIENT_ID>",
      appPassword: "<CLIENT_SECRET>",
      tenantId: "<TENANT_ID>",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

或者直接使用环境变量:MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_ID

5. 在 Teams 中安装应用

teams app create 会提示你安装应用 - 选择 “Install in Teams”。如果你跳过了,可以稍后获取链接:

teams app get <teamsAppId> --install-link

6. 验证一切正常工作

teams app doctor <teamsAppId>

这会针对机器人注册、AAD 应用配置、清单有效性和 SSO 设置运行诊断。

对于生产部署,建议使用联合认证(证书或托管身份)代替客户端密钥。


群组聊天默认被阻止(channels.msteams.groupPolicy: "allowlist")。要允许群组回复,请设置 channels.msteams.groupAllowFrom,或使用 groupPolicy: "open" 允许任何成员(仍受提及门控)。

目标

  • 通过 Teams 私信、群组聊天或频道与 OpenClaw 对话。
  • 保持路由确定性:回复始终回到其来源频道。
  • 默认采用安全的频道行为(除非另有配置,否则需要提及)。

配置写入

默认情况下,Microsoft Teams 允许写入由 /config set|unset 触发的配置更新(需要 commands.config: true)。

使用以下配置禁用:

{
  channels: { msteams: { configWrites: false } },
}

访问控制(私信 + 群组)

私信访问

  • 默认:channels.msteams.dmPolicy = "pairing"。未知发送者会被忽略,直到获批。
  • channels.msteams.allowFrom 应使用稳定的 AAD 对象 ID,或静态发送者访问组,例如 accessGroup:core-team
  • 不要依赖 UPN/显示名称匹配来做允许列表 - 它们可能会变化。OpenClaw 默认禁用直接名称匹配;需要用 channels.msteams.dangerouslyAllowNameMatching: true 显式启用。
  • 当凭证允许时,向导可以通过 Microsoft Graph 将名称解析为 ID。

群组访问

  • 默认:channels.msteams.groupPolicy = "allowlist"(除非添加 groupAllowFrom,否则阻止)。使用 channels.defaults.groupPolicy 覆盖未设置时的默认值。
  • channels.msteams.groupAllowFrom 控制哪些发送者或静态发送者访问组可以在群组聊天/频道中触发(回退到 channels.msteams.allowFrom)。
  • 设置 groupPolicy: "open" 以允许任何成员(默认仍受提及门控)。
  • 要不允许任何频道,请设置 channels.msteams.groupPolicy: "disabled"

示例:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["00000000-0000-0000-0000-000000000000", "accessGroup:core-team"],
    },
  },
}

Teams + 频道允许列表

  • 通过在 channels.msteams.teams 下列出团队和频道,限定群组/频道回复范围。
  • 键应使用来自 Teams 链接的稳定 Teams 会话 ID,而不是可变的显示名称。
  • groupPolicy="allowlist" 且存在 teams 允许列表时,仅接受列出的团队/频道(受提及门控)。
  • 配置向导接受 Team/Channel 条目并为你存储它们。
  • 启动时,OpenClaw 会将团队/频道和用户允许列表名称解析为 ID(当 Graph 权限允许时)
    并记录映射;未解析的团队/频道名称会按输入保留,但默认会在路由中忽略,除非启用 channels.msteams.dangerouslyAllowNameMatching: true

示例:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      teams: {
        "My Team": {
          channels: {
            General: { requireMention: true },
          },
        },
      },
    },
  },
}
手动设置(不使用 Teams CLI)

如果你无法使用 Teams CLI,可以通过 Azure Portal 手动设置机器人。

### 工作方式

1. 确保 Microsoft Teams 插件可用(当前版本中已内置)。
2. 创建一个 **Azure Bot**(App ID + 密钥 + 租户 ID)。
3. 构建一个引用该机器人并包含下方 RSC 权限的 **Teams 应用包**。
4. 将 Teams 应用上传/安装到团队中(或安装到个人范围以用于私信)。
5. 在 `~/.openclaw/openclaw.json`(或环境变量)中配置 `msteams` 并启动 Gateway 网关。
6. Gateway 网关默认在 `/api/messages` 上监听 Bot Framework webhook 流量。

### 第 1 步:创建 Azure Bot

1. 前往[创建 Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot)
2. 填写 **Basics** 标签页:

| 字段 | 值 |
| ------------------ | -------------------------------------------------------- |
| **Bot handle** | 你的机器人名称,例如 `openclaw-msteams`(必须唯一) |
| **Subscription** | 选择你的 Azure 订阅 |
| **Resource group** | 新建或使用现有资源组 |
| **Pricing tier** | 开发/测试使用 **Free** |
| **Type of App** | **Single Tenant**(推荐 - 请参阅下方说明) |
| **Creation type** | **Create new Microsoft App ID** |


新建多租户机器人的能力已在 2025-07-31 之后弃用。新机器人请使用 **Single Tenant**。

3. 点击 **Review + create** → **Create**(等待约 1-2 分钟)

### 第 2 步:获取凭证

1. 前往你的 Azure Bot 资源 → **Configuration**
2. 复制 **Microsoft App ID** → 这就是你的 `appId`
3. 点击 **Manage Password** → 前往 App Registration
4. 在 **Certificates & secrets** 下 → **New client secret** → 复制 **Value** → 这就是你的 `appPassword`
5. 前往 **Overview** → 复制 **Directory (tenant) ID** → 这就是你的 `tenantId`

### 第 3 步:配置消息端点

1. 在 Azure Bot → **Configuration**
2. 将 **Messaging endpoint** 设置为你的 webhook URL:
- 生产:`https://your-domain.com/api/messages`
- 本地开发:使用隧道(请参阅下方[本地开发](#local-development-tunneling))

### 第 4 步:启用 Teams 频道

1. 在 Azure Bot → **Channels**
2. 点击 **Microsoft Teams** → Configure → Save
3. 接受服务条款

### 第 5 步:构建 Teams 应用清单

- 包含一个 `bot` 条目,其中 `botId = `。
- 范围:`personal`、`team`、`groupChat`。
- `supportsFiles: true`(个人范围文件处理必需)。
- 添加 RSC 权限(请参阅 [RSC 权限](#current-teams-rsc-permissions-manifest))。
- 创建图标:`outline.png`(32x32)和 `color.png`(192x192)。
- 将三个文件一起压缩:`manifest.json`、`outline.png`、`color.png`。

### 第 6 步:配置 OpenClaw

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      appPassword: "<APP_PASSWORD>",
      tenantId: "<TENANT_ID>",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

环境变量:`MSTEAMS_APP_ID`、`MSTEAMS_APP_PASSWORD`、`MSTEAMS_TENANT_ID`。

### 第 7 步:运行 Gateway 网关

当插件可用且存在带凭证的 `msteams` 配置时,Teams 频道会自动启动。

联合认证(证书加托管身份)

添加于 2026.4.11

对于生产部署,OpenClaw 支持联合认证,作为比客户端密钥更安全的替代方案。可使用两种方法:

选项 A:基于证书的认证

使用已在你的 Entra ID 应用注册中登记的 PEM 证书。

设置:

  1. 生成或获取证书(带私钥的 PEM 格式)。
  2. 在 Entra ID → App Registration → Certificates & secretsCertificates → 上传公钥证书。

配置:

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      tenantId: "<TENANT_ID>",
      authType: "federated",
      certificatePath: "/path/to/cert.pem",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

环境变量:

  • MSTEAMS_AUTH_TYPE=federated
  • MSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem

选项 B:Azure 托管身份

使用 Azure Managed Identity 进行无密码认证。这非常适合部署在 Azure 基础设施(AKS、App Service、Azure VM)上且可用托管身份的场景。

工作方式:

  1. 机器人 pod/VM 拥有托管身份(系统分配或用户分配)。
  2. 联合身份凭证将托管身份链接到 Entra ID 应用注册。
  3. 运行时,OpenClaw 使用 @azure/identity 从 Azure IMDS 端点(169.254.169.254)获取令牌。
  4. 令牌会传递给 Teams SDK,用于机器人认证。

前置条件:

  • 已启用托管身份的 Azure 基础设施(AKS 工作负载身份、App Service、VM)
  • 已在 Entra ID 应用注册上创建联合身份凭证
  • pod/VM 可访问 IMDS 网络(169.254.169.254:80

配置(系统分配的托管身份):

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      tenantId: "<TENANT_ID>",
      authType: "federated",
      useManagedIdentity: true,
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

配置(用户分配的托管身份):

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      tenantId: "<TENANT_ID>",
      authType: "federated",
      useManagedIdentity: true,
      managedIdentityClientId: "<MI_CLIENT_ID>",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

环境变量:

  • MSTEAMS_AUTH_TYPE=federated
  • MSTEAMS_USE_MANAGED_IDENTITY=true
  • MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>(仅适用于用户分配)

AKS 工作负载身份设置

对于使用工作负载身份的 AKS 部署:

  1. 在你的 AKS 集群上启用工作负载身份
  2. 在 Entra ID 应用注册上创建联合身份凭据

bash
az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{
"name": "my-bot-workload-identity",
"issuer": "<AKS_OIDC_ISSUER_URL>",
"subject": "system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT>",
"audiences": ["api://AzureADTokenExchange"]
}'

  1. 用应用客户端 ID 标注 Kubernetes 服务账号

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-bot-sa
annotations:
azure.workload.identity/client-id: "<APP_CLIENT_ID>"

  1. 为工作负载身份注入标记 Pod

yaml
metadata:
labels:
azure.workload.identity/use: "true"

  1. 确保网络可访问 IMDS(169.254.169.254)- 如果使用 NetworkPolicy,请添加一条出口规则,允许到端口 80 上 169.254.169.254/32 的流量。

身份验证类型对比

方法 配置 优点 缺点
客户端密钥 appPassword 设置简单 需要轮换密钥,安全性较低
证书 authType: "federated" + certificatePath 网络中没有共享密钥 证书管理开销
托管身份 authType: "federated" + useManagedIdentity 无密码,无需管理密钥 需要 Azure 基础设施

默认行为: 未设置 authType 时,OpenClaw 默认使用客户端密钥身份验证。现有配置无需更改即可继续工作。

本地开发(隧道)

Teams 无法访问 localhost。使用持久开发隧道,让你的 URL 在不同会话中保持不变:

# One-time setup:
devtunnel create my-openclaw-bot --allow-anonymous
devtunnel port create my-openclaw-bot -p 3978 --protocol auto

# Each dev session:
devtunnel host my-openclaw-bot

替代方案:ngrok http 3978tailscale funnel 3978(URL 可能每个会话都会变化)。

如果你的隧道 URL 变化,请更新端点:

teams app update <teamsAppId> --endpoint "https://<new-url>/api/messages"

测试机器人

运行诊断:

teams app doctor <teamsAppId>

一次性检查机器人注册、AAD 应用、清单和 SSO 配置。

发送测试消息:

  1. 安装 Teams 应用(使用 teams app get <id> --install-link 中的安装链接)
  2. 在 Teams 中找到机器人并发送私信
  3. 检查 Gateway 网关日志中是否有传入活动

环境变量

所有配置键都可以改用环境变量设置:

  • MSTEAMS_APP_ID
  • MSTEAMS_APP_PASSWORD
  • MSTEAMS_TENANT_ID
  • MSTEAMS_AUTH_TYPE(可选:"secret""federated"
  • MSTEAMS_CERTIFICATE_PATH(联合身份 + 证书)
  • MSTEAMS_CERTIFICATE_THUMBPRINT(可选,身份验证不需要)
  • MSTEAMS_USE_MANAGED_IDENTITY(联合身份 + 托管身份)
  • MSTEAMS_MANAGED_IDENTITY_CLIENT_ID(仅限用户分配的 MI)

成员信息操作

OpenClaw 为 Microsoft Teams 暴露了由 Graph 支持的 member-info 操作,让智能体和自动化能够直接从 Microsoft Graph 解析频道成员详情(显示名称、电子邮件、角色)。

要求:

  • Member.Read.Group RSC 权限(已包含在推荐清单中)
  • 对于跨团队查找:需要带管理员同意的 User.Read.All Graph 应用程序权限

该操作由 channels.msteams.actions.memberInfo 控制(默认值:当 Graph 凭据可用时启用)。

历史上下文

  • channels.msteams.historyLimit 控制有多少最近的频道/群组消息会被包装进提示。
  • 回退到 messages.groupChat.historyLimit。设置为 0 可禁用(默认值 50)。
  • 获取到的线程历史会按发送者允许列表(allowFrom / groupAllowFrom)过滤,因此线程上下文播种只包含来自允许发送者的消息。
  • 引用附件上下文(派生自 Teams 回复 HTML 的 ReplyTo*)目前会按接收内容传递。
  • 换句话说,允许列表控制谁可以触发智能体;目前只有特定补充上下文路径会被过滤。
  • 私信历史可通过 channels.msteams.dmHistoryLimit 限制(用户轮次)。按用户覆盖:channels.msteams.dms["<user_id>"].historyLimit

当前 Teams RSC 权限(清单)

这些是我们 Teams 应用清单中的现有 resourceSpecific 权限。它们只在安装应用的团队/聊天内适用。

对于频道(团队范围):

  • ChannelMessage.Read.Group(Application)- 无需 @提及即可接收所有频道消息
  • ChannelMessage.Send.Group(Application)
  • Member.Read.Group(Application)
  • Owner.Read.Group(Application)
  • ChannelSettings.Read.Group(Application)
  • TeamMember.Read.Group(Application)
  • TeamSettings.Read.Group(Application)

对于群聊:

  • ChatMessage.Read.Chat(Application)- 无需 @提及即可接收所有群聊消息

通过 Teams CLI 添加 RSC 权限:

teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type Application

Teams 清单示例(已脱敏)

包含所需字段的最小有效示例。替换 ID 和 URL。

{
  $schema: "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
  manifestVersion: "1.23",
  version: "1.0.0",
  id: "00000000-0000-0000-0000-000000000000",
  name: { short: "OpenClaw" },
  developer: {
    name: "Your Org",
    websiteUrl: "https://example.com",
    privacyUrl: "https://example.com/privacy",
    termsOfUseUrl: "https://example.com/terms",
  },
  description: { short: "OpenClaw in Teams", full: "OpenClaw in Teams" },
  icons: { outline: "outline.png", color: "color.png" },
  accentColor: "#5B6DEF",
  bots: [
    {
      botId: "11111111-1111-1111-1111-111111111111",
      scopes: ["personal", "team", "groupChat"],
      isNotificationOnly: false,
      supportsCalling: false,
      supportsVideo: false,
      supportsFiles: true,
    },
  ],
  webApplicationInfo: {
    id: "11111111-1111-1111-1111-111111111111",
  },
  authorization: {
    permissions: {
      resourceSpecific: [
        { name: "ChannelMessage.Read.Group", type: "Application" },
        { name: "ChannelMessage.Send.Group", type: "Application" },
        { name: "Member.Read.Group", type: "Application" },
        { name: "Owner.Read.Group", type: "Application" },
        { name: "ChannelSettings.Read.Group", type: "Application" },
        { name: "TeamMember.Read.Group", type: "Application" },
        { name: "TeamSettings.Read.Group", type: "Application" },
        { name: "ChatMessage.Read.Chat", type: "Application" },
      ],
    },
  },
}

清单注意事项(必备字段)

  • bots[].botId 必须匹配 Azure Bot 应用 ID。
  • webApplicationInfo.id 必须匹配 Azure Bot 应用 ID。
  • bots[].scopes 必须包含你计划使用的界面(personalteamgroupChat)。
  • 个人范围内的文件处理需要 bots[].supportsFiles: true
  • 如果你想要频道流量,authorization.permissions.resourceSpecific 必须包含频道读取/发送权限。

更新现有应用

更新已安装的 Teams 应用(例如添加 RSC 权限):

# Download, edit, and re-upload the manifest
teams app manifest download <teamsAppId> manifest.json
# Edit manifest.json locally...
teams app manifest upload manifest.json <teamsAppId>
# Version is auto-bumped if content changed

更新后,请在每个团队中重新安装应用,使新权限生效,并且完全退出并重新启动 Teams(而不只是关闭窗口)以清除缓存的应用元数据。

手动更新清单(不使用 CLI)

1. 用新设置更新你的 `manifest.json`
2. **递增 `version` 字段**(例如 `1.0.0` → `1.1.0`)
3. 用图标(`manifest.json`、`outline.png`、`color.png`)**重新打包为 zip**
4. 上传新的 zip:
- **Teams 管理中心:** Teams 应用 → 管理应用 → 找到你的应用 → 上传新版本
- **旁加载:** 在 Teams 中 → 应用 → 管理你的应用 → 上传自定义应用

能力:仅 RSC 与 Graph 对比

仅使用 Teams RSC(应用已安装,无 Graph API 权限)

可工作:

  • 读取频道消息文本内容。
  • 发送频道消息文本内容。
  • 接收个人(私信)文件附件。

不可工作:

  • 频道/群组图像或文件内容(载荷只包含 HTML 占位片段)。
  • 下载存储在 SharePoint/OneDrive 中的附件。
  • 读取消息历史(实时 webhook 事件之外)。

使用 Teams RSC + Microsoft Graph 应用程序权限

增加:

  • 下载托管内容(粘贴到消息中的图像)。
  • 下载存储在 SharePoint/OneDrive 中的文件附件。
  • 通过 Graph 读取频道/聊天消息历史。

RSC 与 Graph API

能力 RSC 权限 Graph API
实时消息 是(通过 webhook) 否(仅轮询)
历史消息 是(可查询历史)
设置复杂度 仅应用清单 需要管理员同意 + 令牌流程
离线可用 否(必须正在运行) 是(随时查询)

结论: RSC 用于实时监听;Graph API 用于历史访问。要在离线期间补收错过的消息,你需要使用带 ChannelMessage.Read.All 的 Graph API(需要管理员同意)。

启用 Graph 的媒体 + 历史(频道必需)

如果你需要频道中的图像/文件,或想要获取消息历史,必须启用 Microsoft Graph 权限并授予管理员同意。

  1. 在 Entra ID(Azure AD)应用注册中,添加 Microsoft Graph 应用程序权限
    - ChannelMessage.Read.All(频道附件 + 历史)
    - Chat.Read.AllChatMessage.Read.All(群聊)
  2. 为租户授予管理员同意
  3. 提升 Teams 应用的清单版本,重新上传,并在 Teams 中重新安装应用
  4. 完全退出并重新启动 Teams,以清除缓存的应用元数据。

用户提及的额外权限: 对于对话中的用户,用户 @提及开箱即用。不过,如果你想动态搜索并提及不在当前对话中的用户,请添加 User.Read.All(Application)权限并授予管理员同意。

已知限制

Webhook 超时

Teams 通过 HTTP webhook 传递消息。如果处理耗时过长(例如 LLM 响应很慢),你可能会看到:

  • Gateway 网关超时
  • Teams 重试消息(导致重复)
  • 回复丢失

OpenClaw 通过快速返回并主动发送回复来处理这个问题,但非常慢的响应仍可能导致问题。

格式

Teams markdown 比 Slack 或 Discord 更受限:

  • 基本格式可用:粗体斜体code、链接
  • 复杂 markdown(表格、嵌套列表)可能无法正确渲染
  • Adaptive Cards 支持投票和语义化呈现发送(见下文)

配置

关键设置(共享渠道模式见 /gateway/configuration):

  • channels.msteams.enabled:启用/禁用该渠道。
  • channels.msteams.appIdchannels.msteams.appPasswordchannels.msteams.tenantId:机器人凭证。
  • channels.msteams.webhook.port(默认 3978
  • channels.msteams.webhook.path(默认 /api/messages
  • channels.msteams.dmPolicypairing | allowlist | open | disabled(默认:配对)
  • channels.msteams.allowFrom:私信允许列表(推荐使用 AAD 对象 ID)。当 Graph 访问可用时,向导会在设置期间将名称解析为 ID。
  • channels.msteams.dangerouslyAllowNameMatching:应急开关,用于重新启用可变的 UPN/显示名称匹配,以及直接的团队/频道名称路由。
  • channels.msteams.textChunkLimit:出站文本分块大小。
  • channels.msteams.chunkModelength(默认)或 newline,用于在按长度分块前先按空行(段落边界)拆分。
  • channels.msteams.mediaAllowHosts:入站附件主机允许列表(默认 Microsoft/Teams 域)。
  • channels.msteams.mediaAuthAllowHosts:媒体重试时可附加 Authorization 标头的允许列表(默认 Graph + Bot Framework 主机)。
  • channels.msteams.requireMention:在频道/群组中要求 @提及(默认 true)。
  • channels.msteams.replyStylethread | top-level(见 回复样式)。
  • channels.msteams.teams.<teamId>.replyStyle:按团队覆盖。
  • channels.msteams.teams.<teamId>.requireMention:按团队覆盖。
  • channels.msteams.teams.<teamId>.tools:默认的按团队工具策略覆盖(allow/deny/alsoAllow),在缺少频道覆盖时使用。
  • channels.msteams.teams.<teamId>.toolsBySender:默认的按团队、按发送者工具策略覆盖(支持 "*" 通配符)。
  • channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle:按频道覆盖。
  • channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention:按频道覆盖。
  • channels.msteams.teams.<teamId>.channels.<conversationId>.tools:按频道工具策略覆盖(allow/deny/alsoAllow)。
  • channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender:按频道、按发送者工具策略覆盖(支持 "*" 通配符)。
  • toolsBySender 键应使用显式前缀:
    channel:id:e164:username:name:(旧版未加前缀的键仍只映射到 id:)。
  • channels.msteams.actions.memberInfo:启用或禁用由 Graph 支持的成员信息操作(默认:当 Graph 凭证可用时启用)。
  • channels.msteams.authType:身份验证类型 - "secret"(默认)或 "federated"
  • channels.msteams.certificatePath:PEM 证书文件路径(联合 + 证书身份验证)。
  • channels.msteams.certificateThumbprint:证书指纹(可选,身份验证不要求)。
  • channels.msteams.useManagedIdentity:启用托管标识身份验证(联合模式)。
  • channels.msteams.managedIdentityClientId:用户分配的托管标识的客户端 ID。
  • channels.msteams.sharePointSiteId:用于在群聊/频道中上传文件的 SharePoint 站点 ID(见 在群聊中发送文件)。

路由和会话

  • 会话键遵循标准智能体格式(见 /concepts/session):
  • 直接消息共享主会话(agent:<agentId>:<mainKey>)。
  • 频道/群组消息使用会话 ID:
    • agent:<agentId>:msteams:channel:<conversationId>
    • agent:<agentId>:msteams:group:<conversationId>

回复样式:线程与帖子

Teams 最近在同一底层数据模型上引入了两种频道 UI 样式:

样式 描述 推荐的 replyStyle
帖子(经典) 消息显示为卡片,下面带有线程回复 thread(默认)
线程(类似 Slack) 消息线性流动,更像 Slack top-level

问题: Teams API 不会公开频道使用哪种 UI 样式。如果使用了错误的 replyStyle

  • 在线程样式频道中使用 thread → 回复会以别扭的嵌套形式显示
  • 在帖子样式频道中使用 top-level → 回复会显示为单独的顶层帖子,而不是在线程内

解决方案: 根据频道的设置方式为每个频道配置 replyStyle

{
  channels: {
    msteams: {
      replyStyle: "thread",
      teams: {
        "19:abc...@thread.tacv2": {
          channels: {
            "19:xyz...@thread.tacv2": {
              replyStyle: "top-level",
            },
          },
        },
      },
    },
  },
}

解析优先级

当机器人向频道发送回复时,replyStyle 会从最具体的覆盖项向下解析到默认值。第一个非 undefined 值生效:

  1. 按频道channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle
  2. 按团队channels.msteams.teams.<teamId>.replyStyle
  3. 全局channels.msteams.replyStyle
  4. 隐式默认值 — 从 requireMention 派生:
    - requireMention: truethread
    - requireMention: falsetop-level

如果你在全局设置 requireMention: false,但没有显式设置 replyStyle,那么帖子样式频道中的提及会显示为顶层帖子,即使入站消息是线程回复。请在全局、团队或频道级别固定 replyStyle: "thread" 以避免意外。

线程上下文保留

replyStyle: "thread" 生效,并且机器人是在频道线程内被 @提及时,OpenClaw 会将原始线程根重新附加到出站会话引用(19:…@thread.tacv2;messageid=<root>),使回复落在同一线程内。这同时适用于实时(轮次内)发送,以及 Bot Framework 轮次上下文过期后进行的主动发送(例如长时间运行的智能体、通过 mcp__openclaw__message 排队的工具调用回复)。

线程根取自会话引用中存储的 threadId。早于 threadId 的旧存储引用会回退到 activityId(也就是最后一次为会话播种的任何入站活动),因此现有部署无需重新播种即可继续工作。

replyStyle: "top-level" 生效时,频道线程入站会被有意作为新的顶层帖子回答,不会附加线程后缀。这是线程样式频道的正确行为;如果你看到顶层帖子,但预期是线程回复,那么该频道的 replyStyle 设置不正确。

附件和图片

当前限制:

  • 私信: 图片和文件附件通过 Teams 机器人文件 API 工作。
  • 频道/群组: 附件位于 M365 存储(SharePoint/OneDrive)中。Webhook 载荷只包含 HTML 存根,而不包含实际文件字节。需要 Graph API 权限才能下载频道附件。
  • 对于显式以文件为主的发送,请使用 action=upload-file 搭配 media / filePath / path;可选的 message 会成为随附文本/评论,filename 会覆盖上传名称。

如果没有 Graph 权限,带图片的频道消息将以纯文本形式接收(机器人无法访问图片内容)。
默认情况下,OpenClaw 只会从 Microsoft/Teams 主机名下载媒体。可通过 channels.msteams.mediaAllowHosts 覆盖(使用 ["*"] 允许任何主机)。
Authorization 标头只会附加到 channels.msteams.mediaAuthAllowHosts 中的主机(默认 Graph + Bot Framework 主机)。请保持此列表严格(避免多租户后缀)。

在群聊中发送文件

机器人可以使用 FileConsentCard 流程(内置)在私信中发送文件。不过,在群聊/频道中发送文件需要额外设置:

上下文 文件发送方式 所需设置
私信 FileConsentCard → 用户接受 → 机器人上传 开箱即用
群聊/频道 上传到 SharePoint → 共享链接 需要 sharePointSiteId + Graph 权限
图片(任何上下文) Base64 编码内联 开箱即用

为什么群聊需要 SharePoint

机器人没有个人 OneDrive 驱动器(/me/drive Graph API 端点不适用于应用程序标识)。要在群聊/频道中发送文件,机器人会上传到一个 SharePoint 站点并创建共享链接。

设置

  1. 添加 Graph API 权限,位置在 Entra ID(Azure AD)→ 应用注册:
    - Sites.ReadWrite.All(应用程序)- 将文件上传到 SharePoint
    - Chat.Read.All(应用程序)- 可选,启用按用户共享链接

  2. 为租户授予管理员同意

  3. 获取你的 SharePoint 站点 ID:

```bash
# Via Graph Explorer or curl with a valid token:
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"

# Example: for a site at "contoso.sharepoint.com/sites/BotFiles"
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"

# Response includes: "id": "contoso.sharepoint.com,guid1,guid2"
```

  1. 配置 OpenClaw:

json5
{
channels: {
msteams: {
// ... other config ...
sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",
},
},
}

共享行为

权限 共享行为
Sites.ReadWrite.All 组织范围共享链接(组织中的任何人都可访问)
Sites.ReadWrite.All + Chat.Read.All 按用户共享链接(只有聊天成员可访问)

按用户共享更安全,因为只有聊天参与者可以访问文件。如果缺少 Chat.Read.All 权限,机器人会回退到组织范围共享。

回退行为

场景 结果
群聊 + 文件 + 已配置 sharePointSiteId 上传到 SharePoint,发送共享链接
群聊 + 文件 + 未配置 sharePointSiteId 尝试 OneDrive 上传(可能失败),仅发送文本
个人聊天 + 文件 FileConsentCard 流程(无需 SharePoint 即可工作)
任何上下文 + 图片 Base64 编码内联(无需 SharePoint 即可工作)

文件存储位置

上传的文件会存储在已配置 SharePoint 站点默认文档库中的 /OpenClawShared/ 文件夹内。

投票(Adaptive Cards)

OpenClaw 以 Adaptive Cards 形式发送 Teams 投票(没有原生 Teams 投票 API)。

  • CLI:openclaw message poll --channel msteams --target conversation:<id> ...
  • 投票由 Gateway 网关记录在 ~/.openclaw/msteams-polls.json 中。
  • Gateway 网关必须保持在线才能记录投票。
  • 投票目前还不会自动发布结果摘要(如果需要,请检查存储文件)。

呈现卡片

使用 message 工具或 CLI 向 Teams 用户或对话发送语义化呈现载荷。OpenClaw 会根据通用呈现契约将其渲染为 Teams Adaptive Cards。

presentation 参数接受语义块。提供 presentation 时,消息文本是可选的。

Agent 工具:

{
  action: "send",
  channel: "msteams",
  target: "user:<id>",
  presentation: {
    title: "Hello",
    blocks: [{ type: "text", text: "Hello!" }],
  },
}

CLI:

openclaw message send --channel msteams \
  --target "conversation:19:abc...@thread.tacv2" \
  --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'

有关目标格式的详细信息,请参见下方的目标格式

目标格式

MSTeams 目标使用前缀来区分用户和对话:

目标类型 格式 示例
用户(按 ID) user:<aad-object-id> user:40a1a0ed-4ff2-4164-a219-55518990c197
用户(按名称) user:<display-name> user:John Smith(需要 Graph API)
群组/频道 conversation:<conversation-id> conversation:19:abc123...@thread.tacv2
群组/频道(原始) <conversation-id> 19:abc123...@thread.tacv2(如果包含 @thread

CLI 示例:

# Send to a user by ID
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"

# Send to a user by display name (triggers Graph API lookup)
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"

# Send to a group chat or channel
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"

# Send a presentation card to a conversation
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \
  --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello"}]}'

Agent 工具示例:

{
  action: "send",
  channel: "msteams",
  target: "user:John Smith",
  message: "Hello!",
}
{
  action: "send",
  channel: "msteams",
  target: "conversation:19:abc...@thread.tacv2",
  presentation: {
    title: "Hello",
    blocks: [{ type: "text", text: "Hello" }],
  },
}


如果没有 user: 前缀,名称默认按群组或团队解析。按显示名称定位人员时,请始终使用 user:

主动消息发送

  • 主动消息仅在用户交互之后才可用,因为我们会在那时存储对话引用。
  • 关于 dmPolicy 和允许列表门控,请参见 /gateway/configuration

团队和频道 ID(常见陷阱)

Teams URL 中的 groupId 查询参数不是用于配置的团队 ID。请改为从 URL 路径中提取 ID:

团队 URL:

https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
                                    └────────────────────────────┘
                                    Team conversation ID (URL-decode this)

频道 URL:

https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
                                      └─────────────────────────┘
                                      Channel ID (URL-decode this)

用于配置:

  • 团队键 = /team/ 之后的路径段(URL 解码后,例如 19:Bk4j...@thread.tacv2;较旧租户可能显示 @thread.skype,这同样有效)
  • 频道键 = /channel/ 之后的路径段(URL 解码后)
  • 忽略 OpenClaw 路由中的 groupId 查询参数。它是 Microsoft Entra 群组 ID,而不是传入 Teams 活动中使用的 Bot Framework 对话 ID。

私有频道

机器人对私有频道的支持有限:

功能 标准频道 私有频道
机器人安装 有限
实时消息(webhook) 可能无法工作
RSC 权限 行为可能不同
@ 提及 如果机器人可访问
Graph API 历史记录 是(需要权限)

如果私有频道无法工作,可采用的解决方法:

  1. 使用标准频道进行机器人交互
  2. 使用私信 - 用户始终可以直接向机器人发送消息
  3. 使用 Graph API 进行历史访问(需要 ChannelMessage.Read.All

故障排除

常见问题

  • 频道中不显示图片: 缺少 Graph 权限或管理员同意。重新安装 Teams 应用,并完全退出/重新打开 Teams。
  • 频道中没有响应: 默认需要提及;设置 channels.msteams.requireMention=false,或按团队/频道配置。
  • 版本不匹配(Teams 仍显示旧清单): 移除并重新添加应用,然后完全退出 Teams 以刷新。
  • webhook 返回 401 Unauthorized: 手动测试时没有 Azure JWT 属于预期情况,表示端点可访问但认证失败。请使用 Azure Web Chat 正确测试。

清单上传错误

  • “Icon file cannot be empty”: 清单引用的图标文件为 0 字节。创建有效的 PNG 图标(outline.png 为 32x32,color.png 为 192x192)。
  • “webApplicationInfo.Id already in use”: 应用仍安装在另一个团队/聊天中。先找到并卸载它,或等待 5-10 分钟以完成传播。
  • 上传时出现 “Something went wrong”: 改用 https://admin.teams.microsoft.com 上传,打开浏览器 DevTools(F12)→ Network 选项卡,并检查响应正文中的实际错误。
  • 侧载失败: 尝试使用 “Upload an app to your org's app catalog”,而不是 “Upload a custom app” - 这通常可以绕过侧载限制。

RSC 权限无法工作

  1. 验证 webApplicationInfo.id 与你的机器人 App ID 完全匹配
  2. 重新上传应用,并在团队/聊天中重新安装
  3. 检查你的组织管理员是否已阻止 RSC 权限
  4. 确认你使用了正确的作用域:团队使用 ChannelMessage.Read.Group,群聊使用 ChatMessage.Read.Chat

参考

相关


📄 Nextcloud Talk

原文:https://docs.openclaw.ai/zh-CN/channels/nextcloud-talk

Status: 内置插件(webhook bot)。支持私信、房间、回应和 Markdown 消息。

内置插件

Nextcloud Talk 在当前 OpenClaw 版本中作为内置插件发布,因此
正常的打包构建不需要单独安装。

如果你使用较旧构建,或自定义安装中排除了 Nextcloud Talk,
请直接安装 npm 包:

通过 CLI 安装(npm 注册表):

openclaw plugins install @openclaw/nextcloud-talk

使用裸包可跟随当前官方发布标签。只有在需要可复现安装时才固定精确
版本。

本地检出(从 git 仓库运行时):

openclaw plugins install ./path/to/local/nextcloud-talk-plugin

详情:插件

快速设置(新手)

  1. 确保 Nextcloud Talk 插件可用。
    - 当前打包的 OpenClaw 版本已经内置它。
    - 较旧/自定义安装可以使用上面的命令手动添加它。
  2. 在你的 Nextcloud 服务器上创建一个 bot:

bash
./occ talk:bot:install "OpenClaw" "<shared-secret>" "<webhook-url>" --feature webhook --feature response --feature reaction

  1. 在目标房间设置中启用该 bot。
  2. 配置 OpenClaw:
    - 配置:channels.nextcloud-talk.baseUrl + channels.nextcloud-talk.botSecret
    - 或环境变量:NEXTCLOUD_TALK_BOT_SECRET(仅默认账号)

CLI 设置:

bash
openclaw channels add --channel nextcloud-talk \
--url https://cloud.example.com \
--token "<shared-secret>"

等效的显式字段:

bash
openclaw channels add --channel nextcloud-talk \
--base-url https://cloud.example.com \
--secret "<shared-secret>"

文件支持的 secret:

bash
openclaw channels add --channel nextcloud-talk \
--base-url https://cloud.example.com \
--secret-file /path/to/nextcloud-talk-secret

  1. 重启 Gateway 网关(或完成设置)。

最小配置:

{
  channels: {
    "nextcloud-talk": {
      enabled: true,
      baseUrl: "https://cloud.example.com",
      botSecret: "shared-secret",
      dmPolicy: "pairing",
    },
  },
}

注意事项

  • Bot 无法主动发起私信。用户必须先给 bot 发送消息。
  • webhook URL 必须能被 Gateway 网关访问;如果位于代理之后,请设置 webhookPublicUrl
  • bot API 不支持媒体上传;媒体会以 URL 形式发送。
  • webhook 载荷不会区分私信和房间;设置 apiUser + apiPassword 以启用房间类型查找(否则私信会被视为房间)。

访问控制(私信)

  • 默认:channels.nextcloud-talk.dmPolicy = "pairing"。未知发送者会收到配对码。
  • 通过以下方式批准:
  • openclaw pairing list nextcloud-talk
  • openclaw pairing approve nextcloud-talk <CODE>
  • 公开私信:channels.nextcloud-talk.dmPolicy="open" 加上 channels.nextcloud-talk.allowFrom=["*"]
  • allowFrom 只匹配 Nextcloud 用户 ID;显示名称会被忽略。

房间(群组)

  • 默认:channels.nextcloud-talk.groupPolicy = "allowlist"(提及门控)。
  • 使用 channels.nextcloud-talk.rooms 将房间加入 allowlist:
{
  channels: {
    "nextcloud-talk": {
      rooms: {
        "room-token": { requireMention: true },
      },
    },
  },
}
  • 若不允许任何房间,请保持 allowlist 为空,或设置 channels.nextcloud-talk.groupPolicy="disabled"

能力

功能 Status
私信 支持
房间 支持
线程 不支持
媒体 仅 URL
回应 支持
原生命令 不支持

配置参考(Nextcloud Talk)

完整配置:配置

提供商选项:

  • channels.nextcloud-talk.enabled:启用/禁用渠道启动。
  • channels.nextcloud-talk.baseUrl:Nextcloud 实例 URL。
  • channels.nextcloud-talk.botSecret:bot 共享 secret。
  • channels.nextcloud-talk.botSecretFile:常规文件 secret 路径。符号链接会被拒绝。
  • channels.nextcloud-talk.apiUser:用于房间查找(私信检测)的 API 用户。
  • channels.nextcloud-talk.apiPassword:用于房间查找的 API/app 密码。
  • channels.nextcloud-talk.apiPasswordFile:API 密码文件路径。
  • channels.nextcloud-talk.webhookPort:webhook 监听端口(默认:8788)。
  • channels.nextcloud-talk.webhookHost:webhook 主机(默认:0.0.0.0)。
  • channels.nextcloud-talk.webhookPath:webhook 路径(默认:/nextcloud-talk-webhook)。
  • channels.nextcloud-talk.webhookPublicUrl:外部可访问的 webhook URL。
  • channels.nextcloud-talk.dmPolicypairing | allowlist | open | disabled
  • channels.nextcloud-talk.allowFrom:私信 allowlist(用户 ID)。open 需要 "*"
  • channels.nextcloud-talk.groupPolicyallowlist | open | disabled
  • channels.nextcloud-talk.groupAllowFrom:群组 allowlist(用户 ID)。
  • channels.nextcloud-talk.rooms:按房间设置和 allowlist。
  • 静态发送者访问组可以通过 accessGroup:<name>allowFromgroupAllowFrom 引用。
  • channels.nextcloud-talk.historyLimit:群组历史限制(0 表示禁用)。
  • channels.nextcloud-talk.dmHistoryLimit:私信历史限制(0 表示禁用)。
  • channels.nextcloud-talk.dms:按私信覆盖(historyLimit)。
  • channels.nextcloud-talk.textChunkLimit:出站文本分块大小(字符)。
  • channels.nextcloud-talk.chunkModelength(默认)或 newline,用于先按空行(段落边界)拆分,再按长度分块。
  • channels.nextcloud-talk.blockStreaming:为此渠道禁用分块流式传输。
  • channels.nextcloud-talk.blockStreamingCoalesce:分块流式传输合并调优。
  • channels.nextcloud-talk.mediaMaxMb:入站媒体上限(MB)。

相关


📄 Nostr

原文:https://docs.openclaw.ai/zh-CN/channels/nostr

Status: 可选内置插件(默认禁用,直到配置后启用)。

Nostr 是一种用于社交网络的去中心化协议。此渠道让 OpenClaw 能够通过 NIP-04 接收并回复加密私信。

内置插件

当前 OpenClaw 版本以内置插件形式随附 Nostr,因此常规打包构建
不需要单独安装。

较旧/自定义安装

  • 新手引导(openclaw onboard)和 openclaw channels add 仍会从共享渠道目录中展示
    Nostr。
  • 如果你的构建排除了内置 Nostr,请直接安装 npm 包。
openclaw plugins install @openclaw/nostr

使用裸包名可跟随当前官方发布标签。仅在需要可复现安装时才固定精确
版本。

使用本地检出(开发工作流):

openclaw plugins install --link <path-to-local-nostr-plugin>

安装或启用插件后,重启 Gateway 网关。

非交互式设置

openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY"
openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" --relay-urls "wss://relay.damus.io,wss://relay.primal.net"

使用 --use-env 可将 NOSTR_PRIVATE_KEY 保留在环境中,而不是把密钥存入配置。

快速设置

  1. 生成 Nostr 密钥对(如果需要):
# Using nak
nak key generate
  1. 添加到配置:
{
  channels: {
    nostr: {
      privateKey: "${NOSTR_PRIVATE_KEY}",
    },
  },
}
  1. 导出密钥:
export NOSTR_PRIVATE_KEY="nsec1..."
  1. 重启 Gateway 网关。

配置参考

类型 默认值 描述
privateKey string required nsec 或十六进制格式的私钥
relays string[] ['wss://relay.damus.io', 'wss://nos.lol'] 中继 URL(WebSocket)
dmPolicy string pairing 私信访问策略
allowFrom string[] [] 允许的发送者公钥
enabled boolean true 启用/禁用渠道
name string - 显示名称
profile object - NIP-01 个人资料元数据

个人资料元数据

个人资料数据会作为 NIP-01 kind:0 事件发布。你可以从控制界面(渠道 -> Nostr -> 个人资料)管理它,或直接在配置中设置。

示例:

{
  channels: {
    nostr: {
      privateKey: "${NOSTR_PRIVATE_KEY}",
      profile: {
        name: "openclaw",
        displayName: "OpenClaw",
        about: "Personal assistant DM bot",
        picture: "https://example.com/avatar.png",
        banner: "https://example.com/banner.png",
        website: "https://example.com",
        nip05: "openclaw@example.com",
        lud16: "openclaw@example.com",
      },
    },
  },
}

注意:

  • 个人资料 URL 必须使用 https://
  • 从中继导入会合并字段,并保留本地覆盖项。

访问控制

私信策略

  • pairing(默认):未知发送者会收到配对码。
  • allowlist:只有 allowFrom 中的公钥可以发送私信。
  • open:公开入站私信(需要 allowFrom: ["*"])。
  • disabled:忽略入站私信。

强制执行说明:

  • 在发送者策略和 NIP-04 解密之前会验证入站事件签名,因此伪造事件会被提前拒绝。
  • 配对回复会在不处理原始私信正文的情况下发送。
  • 入站私信会受到速率限制,过大的载荷会在解密前被丢弃。

允许列表示例

{
  channels: {
    nostr: {
      privateKey: "${NOSTR_PRIVATE_KEY}",
      dmPolicy: "allowlist",
      allowFrom: ["npub1abc...", "npub1xyz..."],
    },
  },
}

密钥格式

接受的格式:

  • 私钥: nsec... 或 64 字符十六进制
  • 公钥(allowFrom): npub... 或十六进制

中继

默认值:relay.damus.ionos.lol

{
  channels: {
    nostr: {
      privateKey: "${NOSTR_PRIVATE_KEY}",
      relays: ["wss://relay.damus.io", "wss://relay.primal.net", "wss://nostr.wine"],
    },
  },
}

提示:

  • 使用 2-3 个中继以提供冗余。
  • 避免使用过多中继(延迟、重复)。
  • 付费中继可以提高可靠性。
  • 本地中继适合测试(ws://localhost:7777)。

协议支持

NIP Status 描述
NIP-01 Supported 基本事件格式 + 个人资料元数据
NIP-04 Supported 加密私信(kind:4
NIP-17 Planned 礼物包装私信
NIP-44 Planned 版本化加密

测试

本地中继

# Start strfry
docker run -p 7777:7777 ghcr.io/hoytech/strfry
{
  channels: {
    nostr: {
      privateKey: "${NOSTR_PRIVATE_KEY}",
      relays: ["ws://localhost:7777"],
    },
  },
}

手动测试

  1. 从日志中记下机器人的公钥(npub)。
  2. 打开一个 Nostr 客户端(Damus、Amethyst 等)。
  3. 向机器人公钥发送私信。
  4. 验证响应。

故障排除

未收到消息

  • 验证私钥有效。
  • 确保中继 URL 可访问并使用 wss://(本地可使用 ws://)。
  • 确认 enabled 不是 false
  • 检查 Gateway 网关日志中的中继连接错误。

未发送响应

  • 检查中继是否接受写入。
  • 验证出站连接。
  • 留意中继速率限制。

重复响应

  • 使用多个中继时属于预期情况。
  • 消息会按事件 ID 去重;只有首次投递会触发响应。

安全

  • 切勿提交私钥。
  • 对密钥使用环境变量。
  • 对生产机器人考虑使用 allowlist
  • 会在发送者策略之前验证签名,并在解密之前强制执行发送者策略,因此伪造事件会被提前拒绝,未知发送者也无法强制执行完整加密工作。

限制(MVP)

  • 仅支持直接消息(不支持群聊)。
  • 不支持媒体附件。
  • 仅支持 NIP-04(计划支持 NIP-17 礼物包装)。

相关内容


📄 Signal

原文:https://docs.openclaw.ai/zh-CN/channels/signal

Status:外部 CLI 集成。Gateway 网关通过 HTTP 与 signal-cli 通信——可以是原生守护进程(JSON-RPC + SSE),也可以是 bbernhard/signal-cli-rest-api 容器(REST + WebSocket)。

前置条件

  • OpenClaw 已安装在你的服务器上(下面的 Linux 流程已在 Ubuntu 24 上测试)。
  • 以下之一:
  • 主机上可用的 signal-cli(原生模式),
  • bbernhard/signal-cli-rest-api Docker 容器(容器模式)。
  • 一个可以接收一次验证短信的电话号码(用于短信注册路径)。
  • 注册期间可访问浏览器以完成 Signal 验证码(signalcaptchas.org)。

快速设置(初学者)

  1. 为机器人使用一个单独的 Signal 号码(推荐)。
  2. 安装 signal-cli(如果使用 JVM 构建版本,则需要 Java)。
  3. 选择一种设置路径:
    - 路径 A(二维码链接): signal-cli link -n "OpenClaw" 并用 Signal 扫描。
    - 路径 B(短信注册): 使用验证码 + 短信验证注册一个专用号码。
  4. 配置 OpenClaw 并重启 Gateway 网关。
  5. 发送第一条私信并批准配对(openclaw pairing approve signal <CODE>)。

最小配置:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      cliPath: "signal-cli",
      dmPolicy: "pairing",
      allowFrom: ["+15557654321"],
    },
  },
}

字段参考:

字段 描述
account E.164 格式的机器人电话号码(+15551234567
cliPath signal-cli 路径(如果在 PATH 中则为 signal-cli
dmPolicy 私信访问策略(推荐 pairing
allowFrom 允许发送私信的电话号码或 uuid:<id>

它是什么

  • 通过 signal-cli 实现的 Signal 渠道(不是嵌入式 libsignal)。
  • 确定性路由:回复始终回到 Signal。
  • 私信共享智能体的主会话;群组是隔离的(agent:<agentId>:signal:group:<groupId>)。

配置写入

默认情况下,Signal 允许写入由 /config set|unset 触发的配置更新(需要 commands.config: true)。

使用以下配置禁用:

{
  channels: { signal: { configWrites: false } },
}

号码模型(重要)

  • Gateway 网关连接到一个 Signal 设备signal-cli 账号)。
  • 如果你在自己的个人 Signal 账号上运行机器人,它会忽略你自己的消息(循环保护)。
  • 对于“我给机器人发短信,它回复我”这种场景,请使用一个单独的机器人号码

设置路径 A:链接现有 Signal 账号(二维码)

  1. 安装 signal-cli(JVM 或原生构建版本)。
  2. 链接机器人账号:
    - signal-cli link -n "OpenClaw",然后在 Signal 中扫描二维码。
  3. 配置 Signal 并启动 Gateway 网关。

示例:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      cliPath: "signal-cli",
      dmPolicy: "pairing",
      allowFrom: ["+15557654321"],
    },
  },
}

多账号支持:使用 channels.signal.accounts,为每个账号设置配置和可选的 name。共享模式见 gateway/configuration

设置路径 B:注册专用机器人号码(短信,Linux)

当你想使用专用机器人号码,而不是链接现有 Signal 应用账号时,使用此路径。

  1. 获取一个可以接收短信的号码(座机可使用语音验证)。
    - 使用专用机器人号码以避免账号/会话冲突。
  2. 在 Gateway 网关主机上安装 signal-cli
VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/AsamK/signal-cli/releases/latest | sed -e 's/^.*\/v//')
curl -L -O "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}-Linux-native.tar.gz"
sudo tar xf "signal-cli-${VERSION}-Linux-native.tar.gz" -C /opt
sudo ln -sf /opt/signal-cli /usr/local/bin/
signal-cli --version

如果使用 JVM 构建版本(signal-cli-${VERSION}.tar.gz),请先安装 JRE 25+。
保持 signal-cli 更新;上游说明旧版本可能会因 Signal 服务器 API 变化而失效。

  1. 注册并验证号码:
signal-cli -a +<BOT_PHONE_NUMBER> register

如果需要验证码:

  1. 打开 https://signalcaptchas.org/registration/generate.html
  2. 完成验证码,并从“Open Signal”复制 signalcaptcha://... 链接目标。
  3. 尽可能从与浏览器会话相同的外部 IP 运行。
  4. 立即再次运行注册(验证码令牌很快过期):
signal-cli -a +<BOT_PHONE_NUMBER> register --captcha '<SIGNALCAPTCHA_URL>'
signal-cli -a +<BOT_PHONE_NUMBER> verify <VERIFICATION_CODE>
  1. 配置 OpenClaw,重启 Gateway 网关,并验证渠道:
# If you run the gateway as a user systemd service:
systemctl --user restart openclaw-gateway.service

# Then verify:
openclaw doctor
openclaw channels status --probe
  1. 配对你的私信发送者:
    - 向机器人号码发送任意消息。
    - 在服务器上批准代码:openclaw pairing approve signal <PAIRING_CODE>
    - 将机器人号码保存为你手机上的联系人,以避免出现“未知联系人”。


使用 signal-cli 注册电话号码账号可能会取消该号码的主 Signal 应用会话认证。优先使用专用机器人号码;如果需要保留现有手机应用设置,请使用二维码链接模式。

上游参考:

  • signal-cli README: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)

外部守护进程模式(httpUrl)

如果你想自行管理 signal-cli(JVM 冷启动较慢、容器初始化或共享 CPU),请单独运行守护进程,并让 OpenClaw 指向它:

{
  channels: {
    signal: {
      httpUrl: "http://127.0.0.1:8080",
      autoStart: false,
    },
  },
}

这会跳过 OpenClaw 内部的自动生成进程和启动等待。自动生成进程时如果启动较慢,请设置 channels.signal.startupTimeoutMs

容器模式(bbernhard/signal-cli-rest-api)

你可以使用 bbernhard/signal-cli-rest-api Docker 容器,而不是原生运行 signal-cli。它将 signal-cli 包装在 REST API 和 WebSocket 接口之后。

要求:

  • 容器必须MODE=json-rpc 运行,才能实时接收消息。
  • 在连接 OpenClaw 之前,请在容器内注册或链接你的 Signal 账号。

示例 docker-compose.yml 服务:

signal-cli:
  image: bbernhard/signal-cli-rest-api:latest
  environment:
    MODE: json-rpc
  ports:
    - "8080:8080"
  volumes:
    - signal-cli-data:/home/.local/share/signal-cli

OpenClaw 配置:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      httpUrl: "http://signal-cli:8080",
      autoStart: false,
      apiMode: "container", // or "auto" to detect automatically
    },
  },
}

apiMode 字段控制 OpenClaw 使用哪种协议:

行为
"auto" (默认)探测两种传输;流式传输会验证容器 WebSocket 接收
"native" 强制使用原生 signal-cli(/api/v1/rpc 上的 JSON-RPC,/api/v1/events 上的 SSE)
"container" 强制使用 bbernhard 容器(/v2/send 上的 REST,/v1/receive/{account} 上的 WebSocket)

apiMode"auto" 时,OpenClaw 会将检测到的模式缓存 30 秒,以避免重复探测。只有在 /v1/receive/{account} 升级到 WebSocket 之后,才会为流式传输选择容器接收,而这需要 MODE=json-rpc

在容器暴露匹配 API 的情况下,容器模式支持与原生模式相同的 Signal 渠道操作:发送、接收、附件、输入指示器、已读/已查看回执、表情回应、群组和样式文本。OpenClaw 会将其原生 Signal RPC 调用转换为容器的 REST 负载,包括 group.{base64(internal_id)} 群组 ID 和用于格式化文本的 text_mode: "styled"

运维注意事项:

  • 在容器模式下使用 autoStart: false。选择 apiMode: "container" 时,OpenClaw 不应生成原生守护进程。
  • 使用 MODE=json-rpc 进行接收。MODE=normal 可能让 /v1/about 看起来健康,但 /v1/receive/{account} 不会升级到 WebSocket,因此 OpenClaw 在 auto 模式下不会选择容器接收流式传输。
  • 当你知道 httpUrl 指向 bbernhard 的 REST API 时,设置 apiMode: "container"。当你知道它指向原生 signal-cli JSON-RPC/SSE 时,设置 apiMode: "native"。部署可能变化时,使用 "auto"
  • 容器附件下载遵循与原生模式相同的媒体字节限制。当服务器发送 Content-Length 时,过大的响应会在完全缓冲前被拒绝;否则会在流式传输期间被拒绝。

访问控制(私信 + 群组)

私信:

  • 默认值: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>" | "*"] 可以使用 requireMentiontoolstoolsBySender 覆盖群组行为。
  • 在多账号设置中,使用 channels.signal.accounts.<id>.groups 进行按账号覆盖。
  • 通过 groupAllowFrom 将 Signal 群组加入允许列表,本身不会禁用提及门控。除非设置了 requireMention=true,否则专门配置的 channels.signal.groups["<group-id>"] 条目会处理每条群组消息。
  • 运行时说明:如果完全缺少 channels.signal,运行时会在群组检查中回退到 groupPolicy="allowlist"(即使设置了 channels.defaults.groupPolicy)。

工作方式(行为)

  • 原生模式:signal-cli 作为守护进程运行;Gateway 网关通过 SSE 读取事件。
  • 容器模式:Gateway 网关通过 REST API 发送,并通过 WebSocket 接收。
  • 入站消息会被规范化为共享渠道信封。
  • 回复始终路由回同一号码或群组。

媒体 + 限制

  • 出站文本会按 channels.signal.textChunkLimit 分块(默认 4000)。
  • 可选换行分块:设置 channels.signal.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 不会暴露群组的已读回执。

表情回应(消息工具)

  • message action=reactchannel=signal 搭配使用。
  • 目标:发送者 E.164 或 UUID(使用配对输出中的 uuid:<id>;不带前缀的 UUID 也可以)。
  • messageId 是你要回应的消息的 Signal 时间戳。
  • 群组回应需要 targetAuthortargetAuthorUuid

示例:

message action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=🔥
message action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=🔥 remove=true
message action=react channel=signal target=signal:group:<groupId> targetAuthor=uuid:<sender-uuid> messageId=1737630212345 emoji=

配置:

  • channels.signal.actions.reactions:启用/禁用回应操作(默认 true)。
  • channels.signal.reactionLeveloff | ack | minimal | extensive
  • off/ack 会禁用智能体回应(消息工具 react 会报错)。
  • minimal/extensive 会启用智能体回应并设置引导级别。
  • 按账户覆盖:channels.signal.accounts.<id>.actions.reactionschannels.signal.accounts.<id>.reactionLevel

交付目标(CLI/cron)

  • 私信:signal:+15551234567(或纯 E.164)。
  • UUID 私信:uuid:<id>(或不带前缀的 UUID)。
  • 群组:signal:group:<groupId>
  • 用户名:username:<name>(如果你的 Signal 账户支持)。

故障排除

先运行这组排查命令:

openclaw status
openclaw gateway status
openclaw logs --follow
openclaw doctor
openclaw channels status --probe

然后按需确认私信配对状态:

openclaw pairing list signal

常见故障:

  • 守护进程可访问但没有回复:验证账户/守护进程设置(httpUrlaccount)和接收模式。
  • 私信被忽略:发送者正在等待配对批准。
  • 群组消息被忽略:群组发送者/提及门控阻止了交付。
  • 编辑后出现配置验证错误:运行 openclaw doctor --fix
  • 诊断中缺少 Signal:确认 channels.signal.enabled: true

额外检查:

openclaw pairing list signal
pgrep -af signal-cli
grep -i "signal" "/tmp/openclaw/openclaw-$(date +%Y-%m-%d).log" | tail -20

分流排查流程:/channels/troubleshooting

安全说明

  • signal-cli 会在本地存储账户密钥(通常是 ~/.local/share/signal-cli/data/)。
  • 在服务器迁移或重建前,备份 Signal 账户状态。
  • 除非你明确想要更宽泛的私信访问权限,否则保留 channels.signal.dmPolicy: "pairing"
  • SMS 验证只在注册或恢复流程中需要,但如果失去对号码/账户的控制,重新注册可能会变得复杂。

配置参考(Signal)

完整配置:配置

提供商选项:

  • channels.signal.enabled:启用/禁用渠道启动。
  • channels.signal.apiModeauto | native | container(默认:auto)。参见容器模式
  • channels.signal.account:机器人账户的 E.164。
  • channels.signal.cliPathsignal-cli 的路径。
  • channels.signal.httpUrl:完整守护进程 URL(覆盖 host/port)。
  • channels.signal.httpHostchannels.signal.httpPort:守护进程绑定地址(默认 127.0.0.1:8080)。
  • channels.signal.autoStart:自动生成守护进程(如果未设置 httpUrl,默认 true)。
  • channels.signal.startupTimeoutMs:启动等待超时时间,单位 ms(上限 120000)。
  • channels.signal.receiveModeon-start | manual
  • channels.signal.ignoreAttachments:跳过附件下载。
  • channels.signal.ignoreStories:忽略来自守护进程的 stories。
  • channels.signal.sendReadReceipts:转发已读回执。
  • channels.signal.dmPolicypairing | allowlist | open | disabled(默认:pairing)。
  • channels.signal.allowFrom:私信允许列表(E.164 或 uuid:<id>)。open 需要 "*"。Signal 没有用户名;请使用手机号/UUID ID。
  • channels.signal.groupPolicyopen | allowlist | disabled(默认:allowlist)。
  • channels.signal.groupAllowFrom:群组允许列表;接受 Signal 群组 ID(原始值、group:<id>signal:group:<id>)、发送者 E.164 号码,或 uuid:<id> 值。
  • channels.signal.groups:按群组覆盖,以 Signal 群组 ID(或 "*")为键。支持的字段:requireMentiontoolstoolsBySender
  • channels.signal.accounts.<id>.groups:多账户设置中 channels.signal.groups 的按账户版本。
  • channels.signal.historyLimit:作为上下文纳入的最大群组消息数(0 表示禁用)。
  • channels.signal.dmHistoryLimit:以用户轮次计的私信历史限制。按用户覆盖:channels.signal.dms["<phone_or_uuid>"].historyLimit
  • channels.signal.textChunkLimit:出站分块大小(字符数)。
  • channels.signal.chunkModelength(默认)或 newline,用于在按长度分块前按空行(段落边界)拆分。
  • channels.signal.mediaMaxMb:入站/出站媒体上限(MB)。

相关全局选项:

  • agents.list[].groupChat.mentionPatterns(Signal 不支持原生提及)。
  • messages.groupChat.mentionPatterns(全局回退)。
  • messages.responsePrefix

相关内容


📄 Slack

原文:https://docs.openclaw.ai/zh-CN/channels/slack

可用于 Slack 应用集成下的私信和频道,已达到生产就绪状态。默认模式是 Socket Mode;也支持 HTTP Request URLs。



Slack 私信默认使用配对模式。


原生命令行为和命令目录。


跨频道诊断和修复手册。

选择 Socket Mode 或 HTTP Request URLs

两种传输方式都已达到生产就绪状态,并且在消息传递、斜杠命令、App Home 和交互性方面功能等同。应根据部署形态选择,而不是根据功能选择。

关注点 Socket Mode(默认) HTTP Request URLs
公共 Gateway 网关 URL 不需要 需要(DNS、TLS、反向代理或隧道)
出站网络 必须能够访问到 wss-primary.slack.com 的出站 WSS 无出站 WS;仅入站 HTTPS
所需令牌 Bot 令牌(xoxb-...)+ App-Level Token(xapp-...),带 connections:write Bot 令牌(xoxb-...)+ Signing Secret
开发笔记本 / 位于防火墙后 可直接使用 需要公共隧道(ngrok、Cloudflare Tunnel、Tailscale Funnel)或预发布 Gateway 网关
水平扩展 每个主机上的每个应用一个 Socket Mode 会话;多个 Gateway 网关需要单独的 Slack 应用 无状态 POST 处理器;多个 Gateway 网关副本可以在负载均衡器后共享一个应用
一个 Gateway 网关上的多账号 支持;每个账号会打开自己的 WS 支持;每个账号需要唯一的 webhookPath(默认 /slack/events),这样注册不会冲突
斜杠命令传输 通过 WS 连接投递;slash_commands[].url 会被忽略 Slack POST 到 slash_commands[].url;该字段是命令分发所必需的
请求签名 不使用(认证使用 App-Level Token) Slack 会为每个请求签名;OpenClaw 使用 signingSecret 验证
连接断开后的恢复 Slack SDK 自动重连;会应用 Gateway 网关的 pong-timeout 传输调优 没有会掉线的持久连接;重试由 Slack 按请求执行


对于单 Gateway 网关主机、开发笔记本,以及可出站访问 *.slack.com 但不能接受入站 HTTPS 的本地网络,选择 Socket Mode

在负载均衡器后运行多个 Gateway 网关副本、出站 WSS 被阻止但允许入站 HTTPS,或者你已经在反向代理处终止 Slack webhook 时,选择 HTTP Request URLs

快速设置





打开 api.slack.com/appsCreate New AppFrom a manifest → 选择你的工作区 → 粘贴下面的任一清单 → NextCreate

    <CodeGroup>

```json 推荐
{
"display_information": {
"name": "OpenClaw",
"description": "Slack connector for OpenClaw"
},
"features": {
"bot_user": { "display_name": "OpenClaw", "always_online": true },
"app_home": {
"home_tab_enabled": true,
"messages_tab_enabled": true,
"messages_tab_read_only_enabled": false
},
"slash_commands": [
{
"command": "/openclaw",
"description": "Send a message to OpenClaw",
"should_escape": false
}
]
},
"oauth_config": {
"scopes": {
"bot": [
"app_mentions:read",
"assistant:write",
"channels:history",
"channels:read",
"chat:write",
"commands",
"emoji:read",
"files:read",
"files:write",
"groups:history",
"groups:read",
"im:history",
"im:read",
"im:write",
"mpim:history",
"mpim:read",
"mpim:write",
"pins:read",
"pins:write",
"reactions:read",
"reactions:write",
"usergroups:read",
"users:read"
]
}
},
"settings": {
"socket_mode_enabled": true,
"event_subscriptions": {
"bot_events": [
"app_home_opened",
"app_mention",
"channel_rename",
"member_joined_channel",
"member_left_channel",
"message.channels",
"message.groups",
"message.im",
"message.mpim",
"pin_added",
"pin_removed",
"reaction_added",
"reaction_removed"
]
}
}
}

```json 最小配置
{
  "display_information": {
    "name": "OpenClaw",
    "description": "Slack connector for OpenClaw"
  },
  "features": {
    "bot_user": { "display_name": "OpenClaw", "always_online": true },
    "app_home": {
      "home_tab_enabled": true,
      "messages_tab_enabled": true,
      "messages_tab_read_only_enabled": false
    },
    "slash_commands": [
      {
        "command": "/openclaw",
        "description": "Send a message to OpenClaw",
        "should_escape": false
      }
    ]
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "app_mentions:read",
        "assistant:write",
        "channels:history",
        "channels:read",
        "chat:write",
        "commands",
        "groups:history",
        "groups:read",
        "im:history",
        "im:read",
        "im:write",
        "users:read"
      ]
    }
  },
  "settings": {
    "socket_mode_enabled": true,
    "event_subscriptions": {
      "bot_events": [
        "app_home_opened",
        "app_mention",
        "message.channels",
        "message.groups",
        "message.im"
      ]
    }
  }
}
    </CodeGroup>

    <Note>
      **推荐**与内置 Slack 插件的完整功能集匹配:App Home、斜杠命令、文件、回应、置顶、群组私信,以及 emoji/usergroup 读取。当工作区策略限制 scopes 时,选择**最小配置**——它覆盖私信、频道/群组历史、提及和斜杠命令,但会去掉文件、回应、置顶、群组私信(`mpim:*`)、`emoji:read`  `usergroups:read`。请参阅[清单和 scope 检查清单](#manifest-and-scope-checklist),了解每个 scope 的理由,以及额外斜杠命令等增量选项。
    </Note>

    Slack 创建应用后:

    - **Basic Information  App-Level Tokens  Generate Token and Scopes**:添加 `connections:write`,保存,并复制 `xapp-...` 值。
    - **Install App  Install to Workspace**:复制 `xoxb-...` Bot User OAuth Token。

  </Step>

  <Step title="配置 OpenClaw">

    推荐的 SecretRef 设置:
export SLACK_APP_TOKEN=xapp-...
export SLACK_BOT_TOKEN=xoxb-...
cat > slack.socket.patch.json5 <<'JSON5'
{
  channels: {
    slack: {
      enabled: true,
      mode: "socket",
      appToken: { source: "env", provider: "default", id: "SLACK_APP_TOKEN" },
      botToken: { source: "env", provider: "default", id: "SLACK_BOT_TOKEN" },
    },
  },
}
JSON5
openclaw config patch --file ./slack.socket.patch.json5 --dry-run
openclaw config patch --file ./slack.socket.patch.json5
    环境变量回退(仅默认账号):
SLACK_APP_TOKEN=xapp-...
SLACK_BOT_TOKEN=xoxb-...
  </Step>

  <Step title="启动 Gateway 网关">
openclaw gateway
  </Step>
</Steps>




打开 api.slack.com/appsCreate New AppFrom a manifest → 选择你的工作区 → 粘贴下面的任一清单 → 将 https://gateway-host.example.com/slack/events 替换为你的公共 Gateway 网关 URL → NextCreate

    <CodeGroup>

```json 推荐
{
"display_information": {
"name": "OpenClaw",
"description": "Slack connector for OpenClaw"
},
"features": {
"bot_user": { "display_name": "OpenClaw", "always_online": true },
"app_home": {
"home_tab_enabled": true,
"messages_tab_enabled": true,
"messages_tab_read_only_enabled": false
},
"slash_commands": [
{
"command": "/openclaw",
"description": "Send a message to OpenClaw",
"should_escape": false,
"url": "https://gateway-host.example.com/slack/events"
}
]
},
"oauth_config": {
"scopes": {
"bot": [
"app_mentions:read",
"assistant:write",
"channels:history",
"channels:read",
"chat:write",
"commands",
"emoji:read",
"files:read",
"files:write",
"groups:history",
"groups:read",
"im:history",
"im:read",
"im:write",
"mpim:history",
"mpim:read",
"mpim:write",
"pins:read",
"pins:write",
"reactions:read",
"reactions:write",
"usergroups:read",
"users:read"
]
}
},
"settings": {
"event_subscriptions": {
"request_url": "https://gateway-host.example.com/slack/events",
"bot_events": [
"app_home_opened",
"app_mention",
"channel_rename",
"member_joined_channel",
"member_left_channel",
"message.channels",
"message.groups",
"message.im",
"message.mpim",
"pin_added",
"pin_removed",
"reaction_added",
"reaction_removed"
]
},
"interactivity": {
"is_enabled": true,
"request_url": "https://gateway-host.example.com/slack/events",
"message_menu_options_url": "https://gateway-host.example.com/slack/events"
}
}
}

```json Minimal
{
  "display_information": {
    "name": "OpenClaw",
    "description": "Slack connector for OpenClaw"
  },
  "features": {
    "bot_user": { "display_name": "OpenClaw", "always_online": true },
    "app_home": {
      "home_tab_enabled": true,
      "messages_tab_enabled": true,
      "messages_tab_read_only_enabled": false
    },
    "slash_commands": [
      {
        "command": "/openclaw",
        "description": "Send a message to OpenClaw",
        "should_escape": false,
        "url": "https://gateway-host.example.com/slack/events"
      }
    ]
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "app_mentions:read",
        "assistant:write",
        "channels:history",
        "channels:read",
        "chat:write",
        "commands",
        "groups:history",
        "groups:read",
        "im:history",
        "im:read",
        "im:write",
        "users:read"
      ]
    }
  },
  "settings": {
    "event_subscriptions": {
      "request_url": "https://gateway-host.example.com/slack/events",
      "bot_events": [
        "app_home_opened",
        "app_mention",
        "message.channels",
        "message.groups",
        "message.im"
      ]
    },
    "interactivity": {
      "is_enabled": true,
      "request_url": "https://gateway-host.example.com/slack/events",
      "message_menu_options_url": "https://gateway-host.example.com/slack/events"
    }
  }
}
    </CodeGroup>

    <Note>
      **推荐**匹配内置 Slack 插件的完整功能集;**最小配置**会移除文件、回应、置顶、群组私信(`mpim:*`)、`emoji:read`  `usergroups:read`,适用于限制严格的工作区。请参阅[清单和权限范围检查清单](#manifest-and-scope-checklist),了解每个权限范围的原因。
    </Note>

    <Info>
      这三个 URL 字段(`slash_commands[].url``event_subscriptions.request_url`  `interactivity.request_url` / `message_menu_options_url`)都指向同一个 OpenClaw 端点。Slack 的清单 schema 要求它们分别命名,但 OpenClaw 会按 payload 类型路由,因此一个 `webhookPath`(默认 `/slack/events`)就足够了。没有 `slash_commands[].url` 的斜杠命令在 HTTP 模式下会静默无操作。
    </Info>

    Slack 创建应用后:

    - **基本信息  应用凭证**:复制用于请求验证的 **Signing Secret**
    - **安装应用  安装到工作区**:复制 `xoxb-...` Bot User OAuth Token。

  </Step>

  <Step title="Configure OpenClaw">

    推荐的 SecretRef 设置:
export SLACK_BOT_TOKEN=xoxb-...
export SLACK_SIGNING_SECRET=...
cat > slack.http.patch.json5 <<'JSON5'
{
  channels: {
    slack: {
      enabled: true,
      mode: "http",
      botToken: { source: "env", provider: "default", id: "SLACK_BOT_TOKEN" },
      signingSecret: { source: "env", provider: "default", id: "SLACK_SIGNING_SECRET" },
      webhookPath: "/slack/events",
    },
  },
}
JSON5
openclaw config patch --file ./slack.http.patch.json5 --dry-run
openclaw config patch --file ./slack.http.patch.json5
    <Note>
    为多账号 HTTP 使用唯一 webhook 路径

    为每个账号指定不同的 `webhookPath`(默认 `/slack/events`),避免注册相互冲突。
    </Note>

  </Step>

  <Step title="Start gateway">
openclaw gateway
  </Step>
</Steps>


Socket Mode 传输调优

OpenClaw 默认将 Socket Mode 的 Slack SDK 客户端 pong 超时设置为 15 秒。只有在需要针对工作区或主机进行特定调优时,才覆盖传输设置:

{
  channels: {
    slack: {
      mode: "socket",
      socketMode: {
        clientPingTimeout: 20000,
        serverPingTimeout: 30000,
        pingPongLoggingEnabled: false,
      },
    },
  },
}

仅当 Socket Mode 工作区记录 Slack websocket pong/server-ping 超时,或运行在已知存在事件循环饥饿的主机上时,才使用此配置。clientPingTimeout 是 SDK 发送客户端 ping 后等待 pong 的时间;serverPingTimeout 是等待 Slack 服务器 ping 的时间。应用消息和事件仍然是应用状态,而不是传输活跃性信号。

清单和权限范围检查清单

基础 Slack 应用清单在 Socket Mode 和 HTTP Request URLs 中相同。只有 settings 块(以及斜杠命令的 url)不同。

基础清单(Socket Mode 默认):

{
  "display_information": {
    "name": "OpenClaw",
    "description": "Slack connector for OpenClaw"
  },
  "features": {
    "bot_user": { "display_name": "OpenClaw", "always_online": true },
    "app_home": {
      "home_tab_enabled": true,
      "messages_tab_enabled": true,
      "messages_tab_read_only_enabled": false
    },
    "slash_commands": [
      {
        "command": "/openclaw",
        "description": "Send a message to OpenClaw",
        "should_escape": false
      }
    ]
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "app_mentions:read",
        "assistant:write",
        "channels:history",
        "channels:read",
        "chat:write",
        "commands",
        "emoji:read",
        "files:read",
        "files:write",
        "groups:history",
        "groups:read",
        "im:history",
        "im:read",
        "im:write",
        "mpim:history",
        "mpim:read",
        "mpim:write",
        "pins:read",
        "pins:write",
        "reactions:read",
        "reactions:write",
        "usergroups:read",
        "users:read"
      ]
    }
  },
  "settings": {
    "socket_mode_enabled": true,
    "event_subscriptions": {
      "bot_events": [
        "app_home_opened",
        "app_mention",
        "channel_rename",
        "member_joined_channel",
        "member_left_channel",
        "message.channels",
        "message.groups",
        "message.im",
        "message.mpim",
        "pin_added",
        "pin_removed",
        "reaction_added",
        "reaction_removed"
      ]
    }
  }
}

对于 HTTP Request URLs 模式,将 settings 替换为 HTTP 变体,并为每个斜杠命令添加 url。需要公共 URL:

{
  "features": {
    "slash_commands": [
      {
        "command": "/openclaw",
        "description": "Send a message to OpenClaw",
        "should_escape": false,
        "url": "https://gateway-host.example.com/slack/events"
      }
    ]
  },
  "settings": {
    "event_subscriptions": {
      "request_url": "https://gateway-host.example.com/slack/events",
      "bot_events": [
        "app_home_opened",
        "app_mention",
        "channel_rename",
        "member_joined_channel",
        "member_left_channel",
        "message.channels",
        "message.groups",
        "message.im",
        "message.mpim",
        "pin_added",
        "pin_removed",
        "reaction_added",
        "reaction_removed"
      ]
    },
    "interactivity": {
      "is_enabled": true,
      "request_url": "https://gateway-host.example.com/slack/events",
      "message_menu_options_url": "https://gateway-host.example.com/slack/events"
    }
  }
}

其他清单设置

公开不同的功能,用于扩展上述默认值。

默认清单会启用 Slack App Home 的 Home 标签页,并订阅 app_home_opened。当工作区成员打开 Home 标签页时,OpenClaw 会使用 views.publish 发布一个安全的默认 Home 视图;不包含会话 payload 或私有配置。Messages 标签页仍为 Slack 私信启用。


可以使用多个[原生斜杠命令](#commands-and-slash-behavior)来替代单个已配置命令,但需要注意:

- 使用 `/agentstatus` 而不是 `/status`,因为 `/status` 命令是保留命令。
- 一次最多可以开放 25 个斜杠命令。

将现有的 `features.slash_commands` 部分替换为[可用命令](/zh-CN/tools/slash-commands#command-list)的一个子集:

<Tabs>
  <Tab title="Socket Mode (default)">
{
  "slash_commands": [
    {
      "command": "/new",
      "description": "Start a new session",
      "usage_hint": "[model]"
    },
    {
      "command": "/reset",
      "description": "Reset the current session"
    },
    {
      "command": "/compact",
      "description": "Compact the session context",
      "usage_hint": "[instructions]"
    },
    {
      "command": "/stop",
      "description": "Stop the current run"
    },
    {
      "command": "/session",
      "description": "Manage thread-binding expiry",
      "usage_hint": "idle <duration|off> or max-age <duration|off>"
    },
    {
      "command": "/think",
      "description": "Set the thinking level",
      "usage_hint": "<level>"
    },
    {
      "command": "/verbose",
      "description": "Toggle verbose output",
      "usage_hint": "on|off|full"
    },
    {
      "command": "/fast",
      "description": "Show or set fast mode",
      "usage_hint": "[status|on|off]"
    },
    {
      "command": "/reasoning",
      "description": "Toggle reasoning visibility",
      "usage_hint": "[on|off|stream]"
    },
    {
      "command": "/elevated",
      "description": "Toggle elevated mode",
      "usage_hint": "[on|off|ask|full]"
    },
    {
      "command": "/exec",
      "description": "Show or set exec defaults",
      "usage_hint": "host=<auto|sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>"
    },
    {
      "command": "/model",
      "description": "Show or set the model",
      "usage_hint": "[name|#|status]"
    },
    {
      "command": "/models",
      "description": "List providers/models",
      "usage_hint": "[provider] [page] [limit=<n>|size=<n>|all]"
    },
    {
      "command": "/help",
      "description": "Show the short help summary"
    },
    {
      "command": "/commands",
      "description": "Show the generated command catalog"
    },
    {
      "command": "/tools",
      "description": "Show what the current agent can use right now",
      "usage_hint": "[compact|verbose]"
    },
    {
      "command": "/agentstatus",
      "description": "Show runtime status, including provider usage/quota when available"
    },
    {
      "command": "/tasks",
      "description": "List active/recent background tasks for the current session"
    },
    {
      "command": "/context",
      "description": "Explain how context is assembled",
      "usage_hint": "[list|detail|json]"
    },
    {
      "command": "/whoami",
      "description": "Show your sender identity"
    },
    {
      "command": "/skill",
      "description": "Run a skill by name",
      "usage_hint": "<name> [input]"
    },
    {
      "command": "/btw",
      "description": "Ask a side question without changing session context",
      "usage_hint": "<question>"
    },
    {
      "command": "/side",
      "description": "Ask a side question without changing session context",
      "usage_hint": "<question>"
    },
    {
      "command": "/usage",
      "description": "Control the usage footer or show cost summary",
      "usage_hint": "off|tokens|full|cost"
    }
  ]
}
  </Tab>
  <Tab title="HTTP Request URLs">
    使用与上方 Socket Mode 相同的 `slash_commands` 列表,并为每个条目添加 `"url": "https://gateway-host.example.com/slack/events"`。示例:
{
  "slash_commands": [
    {
      "command": "/new",
      "description": "Start a new session",
      "usage_hint": "[model]",
      "url": "https://gateway-host.example.com/slack/events"
    },
    {
      "command": "/help",
      "description": "Show the short help summary",
      "url": "https://gateway-host.example.com/slack/events"
    }
  ]
}
    在列表中的每个命令上重复该 `url` 值。

  </Tab>
</Tabs>



如果你希望传出消息使用当前智能体身份(自定义用户名和图标)而不是默认 Slack 应用身份,请添加 chat:write.customize bot 范围。

如果你使用表情符号图标,Slack 需要 `:emoji_name:` 语法。



如果你配置了 channels.slack.userToken,典型读取范围包括:

- `channels:history`, `groups:history`, `im:history`, `mpim:history`
- `channels:read`, `groups:read`, `im:read`, `mpim:read`
- `users:read`
- `reactions:read`
- `pins:read`
- `emoji:read`
- `search:read`(如果你依赖 Slack 搜索读取)


令牌模型

  • Socket Mode 需要 botToken + appToken
  • HTTP 模式需要 botToken + signingSecret
  • botTokenappTokensigningSecretuserToken 接受明文
    字符串或 SecretRef 对象。
  • 配置令牌会覆盖环境回退。
  • SLACK_BOT_TOKEN / SLACK_APP_TOKEN 环境变量回退仅适用于默认账户。
  • userToken (xoxp-...) 仅能通过配置提供(没有环境变量回退),并默认采用只读行为(userTokenReadOnly: true)。

Status 快照行为:

  • Slack 账户检查会跟踪每个凭据的 *Source*Status
    字段(botTokenappTokensigningSecretuserToken)。
  • Status 为 availableconfigured_unavailablemissing
  • configured_unavailable 表示账户通过 SecretRef
    或其他非内联密钥来源进行了配置,但当前命令/运行时路径
    无法解析实际值。
  • 在 HTTP 模式中,会包含 signingSecretStatus;在 Socket Mode 中,
    必需的配对是 botTokenStatus + appTokenStatus


对于操作/目录读取,配置后可以优先使用用户令牌。对于写入,仍优先使用 bot 令牌;只有当 userTokenReadOnly: false 且 bot 令牌不可用时,才允许用户令牌写入。

操作和门控

Slack 操作由 channels.slack.actions.* 控制。

当前 Slack 工具中可用的操作组:

默认值
messages 启用
reactions 启用
pins 启用
memberInfo 启用
emojiList 启用

当前 Slack 消息操作包括 sendupload-filedownload-filereadeditdeletepinunpinlist-pinsmember-infoemoji-listdownload-file 接受入站文件占位符中显示的 Slack 文件 ID,并为图片返回图片预览,或为其他文件类型返回本地文件元数据。

访问控制和路由



channels.slack.dmPolicy 控制私信访问。channels.slack.allowFrom 是规范私信允许列表。

- `pairing`(默认)
- `allowlist`
- `open`(要求 `channels.slack.allowFrom` 包含 `"*"`
- `disabled`

私信标志:

- `dm.enabled`(默认 true
- `channels.slack.allowFrom`
- `dm.allowFrom`(旧版)
- `dm.groupEnabled`(群组私信默认 false
- `dm.groupChannels`(可选 MPIM 允许列表)

多账户优先级:

- `channels.slack.accounts.default.allowFrom` 仅适用于 `default` 账户。
- 命名账户在自身 `allowFrom` 未设置时继承 `channels.slack.allowFrom`
- 命名账户不会继承 `channels.slack.accounts.default.allowFrom`

旧版 `channels.slack.dm.policy`  `channels.slack.dm.allowFrom` 仍会读取以保持兼容。`openclaw doctor --fix` 会在不改变访问权限的情况下,将它们迁移到 `dmPolicy`  `allowFrom`

私信中的配对使用 `openclaw pairing approve slack <code>`


channels.slack.groupPolicy 控制频道处理:

- `open`
- `allowlist`
- `disabled`

频道允许列表位于 `channels.slack.channels` 下,并且配置键**必须使用稳定的 Slack 频道 ID**(例如 `C12345678`)。

运行时注意事项:如果完全缺少 `channels.slack`(仅环境变量设置),运行时会回退到 `groupPolicy="allowlist"` 并记录警告(即使设置了 `channels.defaults.groupPolicy`)。

名称/ID 解析:

- 当令牌访问允许时,频道允许列表条目和私信允许列表条目会在启动时解析
- 未解析的频道名称条目会保留为已配置状态,但默认会在路由中忽略
- 入站授权和频道路由默认优先使用 ID;直接用户名/slug 匹配需要 `channels.slack.dangerouslyAllowNameMatching: true`

<Warning>
基于名称的键(`#channel-name`  `channel-name`)在 `groupPolicy: "allowlist"` **不会**匹配。频道查找默认优先使用 ID,因此基于名称的键永远无法成功路由,该频道中的所有消息都会被静默阻止。这与 `groupPolicy: "open"` 不同;在该模式下,路由不需要频道键,因此基于名称的键看起来可以工作。

始终使用 Slack 频道 ID 作为键。查找方法:在 Slack 中右键点击频道  **复制链接**  ID(`C...`)会出现在 URL 末尾。

正确:

```json5
{
  channels: {
    slack: {
      groupPolicy: "allowlist",
      channels: {
        C12345678: { allow: true, requireMention: true },
      },
    },
  },
}
```

错误(在 `groupPolicy: "allowlist"` 下会被静默阻止):

```json5
{
  channels: {
    slack: {
      groupPolicy: "allowlist",
      channels: {
        "#eng-my-channel": { allow: true, requireMention: true },
      },
    },
  },
}
```
</Warning>


频道消息默认由提及门控。

提及来源:

- 显式应用提及(`<@botId>`
- Slack 用户组提及(`<!subteam^S...>`),当 bot 用户是该用户组成员时可用;需要 `usergroups:read`
- 提及正则模式(`agents.list[].groupChat.mentionPatterns`,回退为 `messages.groupChat.mentionPatterns`
- 隐式回复 bot 线程行为(当 `thread.requireExplicitMention`  `true` 时禁用)

每频道控制项(`channels.slack.channels.<id>`;名称仅通过启动解析或 `dangerouslyAllowNameMatching` 使用):

- `requireMention`
- `users`(允许列表)
- `allowBots`
- `skills`
- `systemPrompt`
- `tools`, `toolsBySender`
- `toolsBySender` 键格式:`channel:``id:``e164:``username:``name:`  `"*"` 通配符
  (旧版无前缀键仍仅映射到 `id:`

`allowBots` 对频道和私有频道采取保守策略:只有当发送 bot 明确列在该房间的 `users` 允许列表中,或当来自 `channels.slack.allowFrom` 的至少一个显式 Slack 所有者 ID 当前是房间成员时,才接受 bot 作者的房间消息。通配符和显示名称所有者条目不满足所有者在场要求。所有者在场使用 Slack `conversations.members`;请确保应用拥有与房间类型匹配的读取范围(公共频道为 `channels:read`,私有频道为 `groups:read`)。如果成员查询失败,OpenClaw 会丢弃 bot 作者的房间消息。


线程、会话和回复标签

  • 私信路由为 direct;频道路由为 channel;MPIM 路由为 group
  • Slack 路由绑定接受原始对等 ID,以及 Slack 目标形式,例如 channel:C12345678user:U12345678<@U12345678>
  • 使用默认 session.dmScope=main 时,Slack 私信会折叠到智能体主会话。
  • 频道会话:agent:<agentId>:slack:channel:<channelId>
  • 适用时,线程回复可以创建线程会话后缀(:thread:<threadTs>)。
  • 在 OpenClaw 无需显式提及即可处理顶层消息的频道中,非 offreplyToMode 会将每个已处理根消息路由到 agent:<agentId>:slack:channel:<channelId>:thread:<rootTs>,使可见的 Slack 线程从第一轮开始映射到一个 OpenClaw 会话。
  • channels.slack.thread.historyScope 默认值为 threadthread.inheritParent 默认值为 false
  • channels.slack.thread.initialHistoryLimit 控制新线程会话启动时获取多少条现有线程消息(默认 20;设置为 0 可禁用)。
  • channels.slack.thread.requireExplicitMention(默认 false):当为 true 时,会抑制隐式线程提及,使 bot 在线程内仅响应显式 @bot 提及,即使 bot 已经参与了该线程。没有此项时,bot 参与过的线程中的回复会绕过 requireMention 门控。

回复线程控制项:

  • channels.slack.replyToMode: off|first|all|batched(默认 off
  • channels.slack.replyToModeByChatType: 按 direct|group|channel 设置
  • 直接聊天的旧版回退:channels.slack.dm.replyToMode

支持手动回复标签:

  • [[reply_to_current]]
  • [[reply_to:<id>]]

对于来自 message 工具的显式 Slack 线程回复,请将 replyBroadcast: trueaction: "send" 以及 threadIdreplyTo 一起设置,以请求 Slack 同时将线程回复广播到父频道。这会映射到 Slack 的 chat.postMessage reply_broadcast 标志,并且仅支持文本或 Block Kit 发送,不支持媒体上传。

message 工具调用在 Slack 线程内运行并以同一频道为目标时,OpenClaw 通常会根据 replyToMode 继承当前 Slack 线程。在 action: "send"action: "upload-file" 上设置 topLevel: true 可强制发送新的父频道消息。threadId: null 也会被接受为相同的顶层退出方式。


replyToMode="off" 会禁用 Slack 中的所有回复线程,包括显式 [[reply_to_*]] 标签。这与 Telegram 不同,后者在 "off" 模式下仍会遵循显式标签。Slack 线程会在频道中隐藏消息,而 Telegram 回复会以内联方式保持可见。

确认响应

ackReaction 会在 OpenClaw 处理入站消息时发送一个确认表情符号。

解析顺序:

  • channels.slack.accounts.<accountId>.ackReaction
  • channels.slack.ackReaction
  • messages.ackReaction
  • 智能体身份表情符号回退(agents.list[].identity.emoji,否则为 "👀")

注意:

  • Slack 需要短代码(例如 "eyes")。
  • 使用 "" 可为 Slack 账户或全局禁用该响应。

文本流式传输

channels.slack.streaming 控制实时预览行为:

  • off:禁用实时预览流式传输。
  • partial(默认):用最新的部分输出替换预览文本。
  • block:追加分块预览更新。
  • progress:生成时显示进度状态文本,然后发送最终文本。
  • streaming.preview.toolProgress:当草稿预览处于活动状态时,将工具/进度更新路由到同一条已编辑的预览消息中(默认:true)。设置为 false 可保留单独的工具/进度消息。
  • streaming.preview.commandText / streaming.progress.commandText:设置为 status 可在隐藏原始命令/执行文本的同时保留紧凑的工具进度行(默认:raw)。

隐藏原始命令/执行文本,同时保留紧凑的进度行:

{
  "channels": {
    "slack": {
      "streaming": {
        "mode": "progress",
        "progress": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}

channels.slack.streaming.nativeTransportchannels.slack.streaming.modepartial 时控制 Slack 原生文本流式传输(默认:true)。

  • 必须有回复线程,才能显示原生文本流式传输和 Slack 助手线程状态。线程选择仍遵循 replyToMode
  • 当原生流式传输不可用或不存在回复线程时,渠道、群聊和顶层私信根仍可使用普通草稿预览。
  • 顶层 Slack 私信默认保持非线程模式,因此不会显示 Slack 的线程式原生流/状态预览;OpenClaw 会改为在私信中发布并编辑草稿预览。
  • 媒体和非文本载荷会回退到普通投递。
  • 媒体/错误最终消息会取消待处理的预览编辑;符合条件的文本/分块最终消息只有在可以原地编辑预览时才会刷新。
  • 如果流式传输在回复过程中失败,OpenClaw 会对剩余载荷回退到普通投递。

使用草稿预览,而不是 Slack 原生文本流式传输:

{
  channels: {
    slack: {
      streaming: {
        mode: "partial",
        nativeTransport: false,
      },
    },
  },
}

旧键:

  • channels.slack.streamModereplace | status_final | append)是 channels.slack.streaming.mode 的旧版运行时别名。
  • 布尔值 channels.slack.streamingchannels.slack.streaming.modechannels.slack.streaming.nativeTransport 的旧版运行时别名。
  • 旧版 channels.slack.nativeStreamingchannels.slack.streaming.nativeTransport 的运行时别名。
  • 运行 openclaw doctor --fix,将持久化的 Slack 流式传输配置重写为规范键。

输入状态表情回应回退

typingReaction 会在 OpenClaw 处理回复时,向入站 Slack 消息添加一个临时表情回应,并在运行结束时移除它。这在线程回复之外最有用,因为线程回复会使用默认的 “is typing...” 状态指示器。

解析顺序:

  • channels.slack.accounts.<accountId>.typingReaction
  • channels.slack.typingReaction

注意:

  • Slack 预期使用短代码(例如 "hourglass_flowing_sand")。
  • 表情回应是尽力而为的;回复或失败路径完成后会自动尝试清理。

媒体、分块和投递



Slack 文件附件会从 Slack 托管的私有 URL 下载(令牌认证请求流程),并在获取成功且大小限制允许时写入媒体存储。文件占位符包含 Slack fileId,因此智能体可以使用 download-file 获取原始文件。

下载使用有界空闲超时和总超时。如果 Slack 文件检索停滞或失败,OpenClaw 会继续处理消息,并回退到文件占位符。

运行时入站大小上限默认为 `20MB`,除非被 `channels.slack.mediaMaxMb` 覆盖。


- 文本分块使用 channels.slack.textChunkLimit(默认 4000)
- channels.slack.chunkMode="newline" 启用段落优先拆分
- 文件发送使用 Slack 上传 API,并且可以包含线程回复(thread_ts
- 配置后,出站媒体上限遵循 channels.slack.mediaMaxMb;否则渠道发送会使用媒体管线中的 MIME 类型默认值


首选显式目标:

- `user:<id>` 用于私信
- `channel:<id>` 用于渠道

仅包含文本/分块的 Slack 私信可以直接发布到用户 ID;文件上传和线程发送会先通过 Slack conversation API 打开私信,因为这些路径需要具体的 conversation ID。


命令和斜杠行为

斜杠命令在 Slack 中显示为单个已配置命令或多个原生命令。配置 channels.slack.slashCommand 可更改命令默认值:

  • enabled: false
  • name: "openclaw"
  • sessionPrefix: "slack:slash"
  • ephemeral: true
/openclaw /help

原生命令需要在你的 Slack 应用中配置额外清单设置,并改用 channels.slack.commands.native: true 启用,或在全局配置中使用 commands.native: true 启用。

  • Slack 的原生命令自动模式为关闭,因此 commands.native: "auto" 不会启用 Slack 原生命令。
/help

原生参数菜单使用自适应渲染策略:在分派所选选项值之前显示确认模态框:

  • 最多 5 个选项:按钮块
  • 6-100 个选项:静态选择菜单
  • 超过 100 个选项:当交互选项处理器可用时,使用带异步选项过滤的外部选择
  • 超出 Slack 限制:编码后的选项值回退为按钮
/think

斜杠会话使用类似 agent:<agentId>:slack:slash:<userId> 的隔离键,并且仍使用 CommandTargetSessionKey 将命令执行路由到目标对话会话。

交互式回复

Slack 可以渲染智能体撰写的交互式回复控件,但此功能默认禁用。

全局启用:

{
  channels: {
    slack: {
      capabilities: {
        interactiveReplies: true,
      },
    },
  },
}

或者只为一个 Slack 账号启用:

{
  channels: {
    slack: {
      accounts: {
        ops: {
          capabilities: {
            interactiveReplies: true,
          },
        },
      },
    },
  },
}

启用后,智能体可以发出仅适用于 Slack 的回复指令:

  • [[slack_buttons: Approve:approve, Reject:reject]]
  • [[slack_select: Choose a target | Canary:canary, Production:production]]

这些指令会编译成 Slack Block Kit,并通过现有 Slack 交互事件路径将点击或选择路由回来。

注意:

  • 这是 Slack 专用 UI。其他渠道不会把 Slack Block Kit 指令转换成自己的按钮系统。
  • 交互回调值是 OpenClaw 生成的不透明令牌,而不是智能体编写的原始值。
  • 如果生成的交互块会超出 Slack Block Kit 限制,OpenClaw 会回退到原始文本回复,而不是发送无效的 blocks 载荷。

Slack 中的 Exec 审批

Slack 可以充当带有交互按钮和交互事件的原生审批客户端,而不是回退到 Web UI 或终端。

  • Exec 审批使用 channels.slack.execApprovals.* 进行原生私信/频道路由。
  • 当请求已经落在 Slack 中且审批 ID 类型是 plugin: 时,插件审批仍可通过同一个 Slack 原生按钮界面完成。
  • 仍会强制执行审批人授权:只有被识别为审批人的用户才能通过 Slack 批准或拒绝请求。

这使用与其他渠道相同的共享审批按钮界面。在你的 Slack 应用设置中启用 interactivity 后,审批提示会直接在对话中渲染为 Block Kit 按钮。
当这些按钮存在时,它们就是主要审批体验;只有在工具结果表明聊天审批不可用,或手动审批是唯一路径时,OpenClaw
才应包含手动 /approve 命令。

配置路径:

  • channels.slack.execApprovals.enabled
  • channels.slack.execApprovals.approvers(可选;可行时回退到 commands.ownerAllowFrom
  • channels.slack.execApprovals.targetdm | channel | both,默认:dm
  • agentFiltersessionFilter

enabled 未设置或为 "auto" 且至少解析出一个审批人时,Slack 会自动启用原生 exec 审批。设置 enabled: false 可明确禁用 Slack 作为原生审批客户端。
当审批人可解析时,设置 enabled: true 可强制启用原生审批。

没有显式 Slack exec 审批配置时的默认行为:

{
  commands: {
    ownerAllowFrom: ["slack:U12345678"],
  },
}

只有在你想覆盖审批人、添加筛选器,或选择启用来源聊天投递时,才需要显式 Slack 原生配置:

{
  channels: {
    slack: {
      execApprovals: {
        enabled: true,
        approvers: ["U12345678"],
        target: "both",
      },
    },
  },
}

共享 approvals.exec 转发是独立的。仅当 exec 审批提示还必须路由到其他聊天或显式带外目标时才使用它。共享 approvals.plugin 转发也是独立的;当这些请求已经落在 Slack 中时,Slack 原生按钮仍可完成插件审批。

同聊天 /approve 也可在已经支持命令的 Slack 频道和私信中使用。参见 Exec 审批,了解完整审批转发模型。

事件和运行行为

  • 消息编辑/删除会映射为系统事件。
  • 线程广播(“同时发送到频道”的线程回复)会按普通用户消息处理。
  • 表情反应添加/移除事件会映射为系统事件。
  • 成员加入/离开、频道创建/重命名,以及置顶添加/移除事件会映射为系统事件。
  • 启用 configWrites 时,channel_id_changed 可以迁移频道配置键。
  • 频道主题/用途元数据会被视为不可信上下文,并可注入到路由上下文中。
  • 适用时,线程发起者和初始线程历史上下文种子会按配置的发送者允许列表过滤。
  • 块操作和模态交互会发出结构化的 Slack interaction: ... 系统事件,并包含丰富的载荷字段:
  • 块操作:选中值、标签、选择器值,以及 workflow_* 元数据
  • 模态 view_submissionview_closed 事件,包含已路由的频道元数据和表单输入

配置参考

主要参考:配置参考 - Slack

  • 模式/认证:modebotTokenappTokensigningSecretwebhookPathaccounts.*
  • 私信访问:dm.enableddmPolicyallowFrom(旧版:dm.policydm.allowFrom)、dm.groupEnableddm.groupChannels
  • 兼容性开关:dangerouslyAllowNameMatching(应急开关;除非需要,否则保持关闭)
  • 频道访问:groupPolicychannels.*channels.*.userschannels.*.requireMention
  • 线程/历史:replyToModereplyToModeByChatTypethread.*historyLimitdmHistoryLimitdms.*.historyLimit
  • 投递:textChunkLimitchunkModemediaMaxMbstreamingstreaming.nativeTransportstreaming.preview.toolProgress
  • 链接预览:unfurlLinksunfurlMedia,用于控制 chat.postMessage 的链接/媒体预览
  • 运维/功能:configWritescommands.nativeslashCommand.*actions.*userTokenuserTokenReadOnly

故障排除



按顺序检查:

- `groupPolicy`
- 频道允许列表(`channels.slack.channels`)— **键必须是频道 ID**`C12345678`),而不是名称(`#channel-name`)。在 `groupPolicy: "allowlist"` 下,基于名称的键会静默失败,因为频道路由默认优先使用 ID。查找 ID:在 Slack 中右键点击频道  **复制链接**  URL 末尾的 `C...` 值就是频道 ID。
- `requireMention`
- 每频道 `users` 允许列表

有用的命令:
openclaw channels status --probe
openclaw logs --follow
openclaw doctor


检查:

- `channels.slack.dm.enabled`
- `channels.slack.dmPolicy`(或旧版 `channels.slack.dm.policy`- 配对审批 / 允许列表条目
- Slack Assistant 私信事件:提到 `drop message_changed` 的详细日志通常表示 Slack 发送了已编辑的 Assistant 线程事件,但消息元数据中没有可恢复的人类发送者
openclaw pairing list slack


在 Slack 应用设置中验证 bot + app 令牌以及 Socket Mode 是否已启用。

如果 `openclaw channels status --probe --json` 显示 `botTokenStatus` 
`appTokenStatus: "configured_unavailable"`,则表示 Slack 账号已配置,但当前运行时无法解析由 SecretRef 支持的值。


验证:

- signing secret
- webhook path
- Slack 请求 URL(Events + Interactivity + Slash Commands)
- 每个 HTTP 账号使用唯一的 `webhookPath`

如果账号快照中出现 `signingSecretStatus: "configured_unavailable"`
则表示 HTTP 账号已配置,但当前运行时无法解析由 SecretRef 支持的签名密钥。


确认你原本打算使用的是:

- 原生命令模式(`channels.slack.commands.native: true`),并且 Slack 中注册了匹配的斜杠命令
- 或单一斜杠命令模式(`channels.slack.slashCommand.enabled: true`)

另请检查 `commands.useAccessGroups` 以及频道/用户 allowlist。


附件视觉参考

当 Slack 文件下载成功且大小限制允许时,Slack 可以将下载的媒体附加到智能体轮次。图像文件可以通过媒体理解路径传递,或直接传递给支持视觉的回复模型;其他文件会作为可下载文件上下文保留,而不是被视为图像输入。

支持的媒体类型

媒体类型 来源 当前行为 备注
JPEG / PNG / GIF / WebP 图像 Slack 文件 URL 下载并附加到该轮次,用于支持视觉的处理 单文件上限:channels.slack.mediaMaxMb(默认 20 MB)
PDF 文件 Slack 文件 URL 下载并作为文件上下文暴露给 download-filepdf 等工具 Slack 入站不会自动将 PDF 转换为图像视觉输入
其他文件 Slack 文件 URL 尽可能下载并作为文件上下文暴露 二进制文件不会被视为图像输入
线程回复 线程起始消息文件 当回复没有直接媒体时,可以将根消息文件补水为上下文 仅包含文件的起始消息会使用附件占位符
多图像消息 多个 Slack 文件 每个文件都会被独立评估 Slack 处理每条消息最多八个文件

入站流水线

当带有文件附件的 Slack 消息到达时:

  1. OpenClaw 使用机器人令牌(xoxb-...)从 Slack 的私有 URL 下载文件。
  2. 下载成功后,文件会写入媒体存储。
  3. 已下载的媒体路径和内容类型会添加到入站上下文。
  4. 支持图像的模型/工具路径可以使用该上下文中的图像附件。
  5. 非图像文件仍可作为文件元数据或媒体引用提供给能够处理它们的工具。

线程根附件继承

当消息到达线程中时(具有 thread_ts 父级):

  • 如果回复本身没有直接媒体,而包含的根消息有文件,Slack 可以将根文件补水为线程起始上下文。
  • 直接回复附件优先于根消息附件。
  • 仅包含文件且没有文本的根消息会用附件占位符表示,以便回退逻辑仍能包含其文件。

多附件处理

当一条 Slack 消息包含多个文件附件时:

  • 每个附件都会通过媒体流水线独立处理。
  • 已下载的媒体引用会聚合到消息上下文中。
  • 处理顺序遵循事件载荷中的 Slack 文件顺序。
  • 一个附件下载失败不会阻塞其他附件。

大小、下载和模型限制

  • 大小上限:默认每个文件 20 MB。可通过 channels.slack.mediaMaxMb 配置。
  • 下载失败:Slack 无法提供的文件、过期 URL、无法访问的文件、超大文件以及 Slack 认证/登录 HTML 响应会被跳过,而不是报告为不支持的格式。
  • 视觉模型:图像分析会使用支持视觉的当前回复模型,或使用 agents.defaults.imageModel 配置的图像模型。

已知限制

场景 当前行为 解决方法
过期的 Slack 文件 URL 跳过文件;不显示错误 在 Slack 中重新上传文件
未配置视觉模型 图像附件会作为媒体引用存储,但不会作为图像分析 配置 agents.defaults.imageModel 或使用支持视觉的回复模型
非常大的图像(默认 > 20 MB) 按大小上限跳过 如果 Slack 允许,可提高 channels.slack.mediaMaxMb
转发/共享的附件 文本和 Slack 托管的图像/文件媒体按尽力而为处理 直接在 OpenClaw 线程中重新共享
PDF 附件 作为文件/媒体上下文存储,不会自动通过图像视觉路由 使用 download-file 查看文件元数据,或使用 pdf 工具进行 PDF 分析

相关文档

相关



将 Slack 用户配对到 Gateway 网关。


频道和群组私信行为。


将入站消息路由到智能体。


威胁模型和加固。


配置布局和优先级。


命令目录和行为。


📄 Telegram

原文:https://docs.openclaw.ai/zh-CN/channels/telegram

通过 grammY 支持可用于生产环境的机器人私信和群组。默认模式是长轮询;webhook 模式是可选的。



Telegram 的默认私信策略是配对。


跨渠道诊断和修复手册。


完整的渠道配置模式和示例。

快速设置



打开 Telegram 并与 @BotFather 聊天(确认用户名正好是 @BotFather)。

运行 `/newbot`,按提示操作,并保存令牌。

{
  channels: {
    telegram: {
      enabled: true,
      botToken: "123:abc",
      dmPolicy: "pairing",
      groups: { "*": { requireMention: true } },
    },
  },
}
环境变量回退:`TELEGRAM_BOT_TOKEN=...`(仅默认账户)。
Telegram **不**使用 `openclaw channels login telegram`;在配置/环境变量中配置令牌,然后启动 Gateway 网关。

openclaw gateway
openclaw pairing list telegram
openclaw pairing approve telegram <CODE>
配对码会在 1 小时后过期。


将机器人添加到你的群组,然后获取群组访问需要的两个 ID:

- 你的 Telegram 用户 ID,用于 `allowFrom` / `groupAllowFrom`
- Telegram 群组聊天 ID,用作 `channels.telegram.groups` 下的键

首次设置时,从 `openclaw logs --follow`、转发 ID 机器人或 Bot API `getUpdates` 获取群组聊天 ID。允许该群组后,`/whoami@<bot_username>` 可以确认用户和群组 ID。

 `-100` 开头的负数 Telegram 超级群组 ID 是群组聊天 ID。把它们放在 `channels.telegram.groups` 下,而不是 `groupAllowFrom` 下。



令牌解析顺序与账户相关。实践中,配置值优先于环境变量回退,并且 TELEGRAM_BOT_TOKEN 只应用于默认账户。

Telegram 端设置



Telegram 机器人默认启用 Privacy Mode,这会限制它们接收的群组消息。

如果机器人必须看到所有群组消息,可以:

- 通过 `/setprivacy` 禁用隐私模式,或
- 将机器人设为群组管理员。

切换隐私模式时,在每个群组中移除并重新添加机器人,以便 Telegram 应用更改。


管理员状态在 Telegram 群组设置中控制。

管理员机器人会接收所有群组消息,这对常驻群组行为很有用。

- `/setjoingroups` 用于允许/拒绝添加到群组
- `/setprivacy` 用于群组可见性行为


访问控制和激活



channels.telegram.dmPolicy 控制私信访问:

- `pairing`(默认)
- `allowlist`(要求 `allowFrom` 中至少有一个发送者 ID)
- `open`(要求 `allowFrom` 包含 `"*"`
- `disabled`

`dmPolicy: "open"` 搭配 `allowFrom: ["*"]` 会让任何找到或猜到机器人用户名的 Telegram 账户都能命令该机器人。只应将它用于有严格工具限制的有意公开机器人;单所有者机器人应使用 `allowlist` 并配置数字用户 ID。

`channels.telegram.allowFrom` 接受数字 Telegram 用户 ID。接受并规范化 `telegram:` / `tg:` 前缀。
在多账户配置中,限制性的顶层 `channels.telegram.allowFrom` 会被视为安全边界:账户级 `allowFrom: ["*"]` 条目不会让该账户公开,除非合并后的有效账户允许列表仍包含显式通配符。
`dmPolicy: "allowlist"` 搭配空 `allowFrom` 会阻止所有私信,并被配置验证拒绝。
设置只会询问数字用户 ID。
如果你已升级且配置包含 `@username` 允许列表条目,请运行 `openclaw doctor --fix` 来解析它们(尽力而为;需要 Telegram 机器人令牌)。
如果你之前依赖配对存储允许列表文件,`openclaw doctor --fix` 可以在允许列表流程中将条目恢复到 `channels.telegram.allowFrom`(例如当 `dmPolicy: "allowlist"` 还没有显式 ID 时)。

对于单所有者机器人,优先使用 `dmPolicy: "allowlist"` 并配置显式数字 `allowFrom` ID,以便在配置中保持访问策略持久(而不是依赖之前的配对批准)。

常见混淆:私信配对批准并不意味着“这个发送者在所有地方都已授权”。
配对授予私信访问权限。如果还没有命令所有者,第一次批准的配对还会设置 `commands.ownerAllowFrom`,从而为仅所有者命令和 exec 审批提供显式操作员账户。
群组发送者授权仍来自显式配置允许列表。
如果你想要“我授权一次,私信和群组命令都能用”,请将你的数字 Telegram 用户 ID 放入 `channels.telegram.allowFrom`;对于仅所有者命令,请确保 `commands.ownerAllowFrom` 包含 `telegram:<your user id>`

### 查找你的 Telegram 用户 ID

更安全(无第三方机器人):

1. 私信你的机器人。
2. 运行 `openclaw logs --follow`
3. 读取 `from.id`

官方 Bot API 方法:
curl "https://api.telegram.org/bot<bot_token>/getUpdates"
第三方方法隐私性较低):`@userinfobot`  `@getidsbot`。


两个控制项会一起生效:

1. **允许哪些群组**`channels.telegram.groups`
   - 没有 `groups` 配置:
     - 搭配 `groupPolicy: "open"`:任何群组都可以通过群组 ID 检查
     - 搭配 `groupPolicy: "allowlist"`(默认):群组会被阻止,直到你添加 `groups` 条目(或 `"*"`
   - 已配置 `groups`:作为允许列表生效(显式 ID  `"*"`

2. **群组中允许哪些发送者**`channels.telegram.groupPolicy`
   - `open`
   - `allowlist`(默认)
   - `disabled`

`groupAllowFrom` 用于群组发送者过滤。如果未设置,Telegram 会回退到 `allowFrom`
`groupAllowFrom` 条目应是数字 Telegram 用户 ID(`telegram:` / `tg:` 前缀会被规范化)。
不要把 Telegram 群组或超级群组聊天 ID 放入 `groupAllowFrom`。负数聊天 ID 属于 `channels.telegram.groups`
非数字条目会被发送者授权忽略。
安全边界(`2026.2.25+`):群组发送者身份验证**不会**继承私信配对存储批准。
配对仅适用于私信。对于群组,请设置 `groupAllowFrom` 或按群组/主题设置 `allowFrom`
如果未设置 `groupAllowFrom`,Telegram 会回退到配置 `allowFrom`,而不是配对存储。
单所有者机器人的实用模式:在 `channels.telegram.allowFrom` 中设置你的用户 ID,保持 `groupAllowFrom` 未设置,并在 `channels.telegram.groups` 下允许目标群组。
运行时注意事项:如果完全缺少 `channels.telegram`,运行时默认采用故障关闭的 `groupPolicy="allowlist"`,除非显式设置了 `channels.defaults.groupPolicy`

仅所有者群组设置:
{
  channels: {
    telegram: {
      enabled: true,
      dmPolicy: "pairing",
      allowFrom: ["<YOUR_TELEGRAM_USER_ID>"],
      groupPolicy: "allowlist",
      groups: {
        "<GROUP_CHAT_ID>": {
          requireMention: true,
        },
      },
    },
  },
}
在群组中用 `@<bot_username> ping` 测试。`requireMention: true` 时,普通群组消息不会触发机器人。

示例:允许某个特定群组中的任何成员:
{
  channels: {
    telegram: {
      groups: {
        "-1001234567890": {
          groupPolicy: "open",
          requireMention: false,
        },
      },
    },
  },
}
示例:只允许某个特定群组中的特定用户:
{
  channels: {
    telegram: {
      groups: {
        "-1001234567890": {
          requireMention: true,
          allowFrom: ["8734062810", "745123456"],
        },
      },
    },
  },
}
<Warning>
  常见错误:`groupAllowFrom` 不是 Telegram 群组允许列表。

  -  `-1001234567890` 这样的负数 Telegram 群组或超级群组聊天 ID 放在 `channels.telegram.groups` 下。
  - 当你想限制允许群组内哪些人可以触发机器人时,将 `8734062810` 这样的 Telegram 用户 ID 放在 `groupAllowFrom` 下。
  - 只有当你希望允许群组中的任何成员都能与机器人对话时,才使用 `groupAllowFrom: ["*"]`

</Warning>


群组回复默认需要提及。

提及可以来自

- 原生 `@botusername` 提及
- 以下位置中的提及模式
  - `agents.list[].groupChat.mentionPatterns`
  - `messages.groupChat.mentionPatterns`

会话级命令开关

- `/activation always`
- `/activation mention`

这些只更新会话状态使用配置实现持久化

持久化配置示例
{
  channels: {
    telegram: {
      groups: {
        "*": { requireMention: false },
      },
    },
  },
}
获取群组聊天 ID:

- 将群组消息转发给 `@userinfobot` / `@getidsbot`
- 或从 `openclaw logs --follow` 读取 `chat.id`
- 或检查 Bot API `getUpdates`
- 允许该群组后,如果原生命令已启用,请运行 `/whoami@<bot_username>`


运行时行为

  • Telegram 由 Gateway 网关进程拥有。
  • 路由是确定性的:Telegram 入站消息会回复到 Telegram(模型不会选择渠道)。
  • 入站消息会规范化到共享渠道信封中,包含回复元数据、媒体占位符,以及 Gateway 网关已观察到的 Telegram 回复的持久化回复链上下文。
  • 群组会话按群组 ID 隔离。论坛主题会追加 :topic:<threadId> 以保持主题隔离。
  • 私信消息可以携带 message_thread_id;OpenClaw 会为回复保留线程 ID,但默认将私信保留在扁平会话上。当你有意想要私信主题会话隔离时,请配置 channels.telegram.dm.threadReplies: "inbound"channels.telegram.direct.<chatId>.threadReplies: "inbound"requireTopic: true,或匹配的主题配置。
  • 长轮询使用 grammY runner,并按每个聊天/每个线程排序。整体 runner sink 并发使用 agents.defaults.maxConcurrent
  • 长轮询在每个 Gateway 网关进程内受保护,因此同一时间只有一个活跃轮询器可以使用一个机器人令牌。如果你仍看到 getUpdates 409 冲突,可能是另一个 OpenClaw Gateway 网关、脚本或外部轮询器正在使用同一个令牌。
  • 默认情况下,长轮询 watchdog 重启会在 120 秒内没有完成的 getUpdates 活性后触发。只有当你的部署在长时间运行工作期间仍出现误判的轮询停滞重启时,才增加 channels.telegram.pollingStallThresholdMs。该值以毫秒为单位,允许范围为 30000600000;支持按账户覆盖。
  • Telegram Bot API 不支持已读回执(sendReadReceipts 不适用)。

功能参考



OpenClaw 可以实时流式传输部分回复:

- 直接聊天:预览消息 + `editMessageText`
- 群组/主题:预览消息 + `editMessageText`

要求:

- `channels.telegram.streaming`  `off | partial | block | progress`(默认:`partial`
- `progress` 会为工具进度保留一条可编辑的状态草稿,在完成时清除它,并将最终回答作为普通消息发送
- `streaming.preview.toolProgress` 控制工具/进度更新是否复用同一条已编辑的预览消息(默认:预览流式传输启用时为 `true`
- `streaming.preview.commandText` 控制这些工具进度行内的命令/Exec 详情:`raw`(默认,保留已发布行为)或 `status`(仅工具标签)
- 会检测旧版 `channels.telegram.streamMode` 和布尔值 `streaming`;运行 `openclaw doctor --fix` 将它们迁移到 `channels.telegram.streaming.mode`

工具进度预览更新是在工具运行时显示的简短状态行,例如命令执行、文件读取、计划更新或补丁摘要。Telegram 默认保持启用这些更新,以匹配 `v2026.4.22` 及更高版本中已发布的 OpenClaw 行为。若要保留回答文本的已编辑预览,但隐藏工具进度行,请设置:

```json
{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "partial",
        "preview": {
          "toolProgress": false
        }
      }
    }
  }
}
```

若要保持工具进度可见,但隐藏命令/Exec 文本,请设置:

```json
{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "partial",
        "preview": {
          "commandText": "status"
        }
      }
    }
  }
}
```

当你希望显示可见的工具进度,但不将最终回答编辑到同一条消息中时,请使用 `progress` 模式。将命令文本策略放在 `streaming.progress` 下:

```json
{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "progress",
        "progress": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}
```

仅当你希望只交付最终内容时才使用 `streaming.mode: "off"`:Telegram 预览编辑会被禁用,通用工具/进度闲聊会被抑制,而不是作为独立状态消息发送。审批提示、媒体载荷和错误仍会通过普通最终交付路径路由。当你只想保留回答预览编辑,同时隐藏工具进度状态行时,请使用 `streaming.preview.toolProgress: false`

<Note>
  Telegram 选中引用回复是例外。当 `replyToMode`  `"first"``"all"`  `"batched"` 且入站消息包含选中的引用文本时,OpenClaw 会通过 Telegram 的原生引用回复路径发送最终回答,而不是编辑回答预览,因此 `streaming.preview.toolProgress` 无法显示该轮的简短状态行。没有选中引用文本的当前消息回复仍会保留预览流式传输。当工具进度可见性比原生引用回复更重要时,请设置 `replyToMode: "off"`;或设置 `streaming.preview.toolProgress: false` 以确认这个权衡。
</Note>

对于纯文本回复:

- 简短私信/群组/主题预览:OpenClaw 会保留同一条预览消息,并就地执行最终编辑
- 会拆分为多条 Telegram 消息的长文本最终内容,会在可能时复用现有预览作为第一个最终分块,然后只发送剩余分块
- 进度模式的最终内容会清除状态草稿,并使用普通最终交付,而不是将草稿编辑成回答
- 如果在完成文本被确认之前最终编辑失败,OpenClaw 会使用普通最终交付,并清理过期预览

对于复杂回复(例如媒体载荷),OpenClaw 会回退到普通最终交付,然后清理预览消息。

预览流式传输独立于分块流式传输。当 Telegram 明确启用分块流式传输时,OpenClaw 会跳过预览流,以避免双重流式传输。

仅限 Telegram 的推理流:

- `/reasoning stream` 会在生成期间将推理发送到实时预览
- 推理预览会在最终交付后删除;当推理应保持可见时,请使用 `/reasoning on`
- 最终回答发送时不包含推理文本


出站文本使用 Telegram parse_mode: "HTML"

- 类 Markdown 文本会渲染为 Telegram 安全的 HTML。
- 支持的 Telegram HTML 标签会保留;不支持的 HTML 会被转义。
- 如果 Telegram 拒绝解析后的 HTML,OpenClaw 会以纯文本重试。

链接预览默认启用,可通过 `channels.telegram.linkPreview: false` 禁用。


Telegram 命令菜单注册会在启动时通过 setMyCommands 处理。

原生命令默认值:

- `commands.native: "auto"` 为 Telegram 启用原生命令

添加自定义命令菜单条目:
{
  channels: {
    telegram: {
      customCommands: [
        { command: "backup", description: "Git backup" },
        { command: "generate", description: "Create an image" },
      ],
    },
  },
}
规则:

- 名称会被规范化(去除前导 `/`,转为小写)
- 有效模式:`a-z``0-9``_`,长度 `1..32`
- 自定义命令不能覆盖原生命令
- 冲突/重复项会被跳过并记录日志

注意:

- 自定义命令只是菜单条目;它们不会自动实现行为
- 即使未显示在 Telegram 菜单中,插件/技能命令在输入时仍然可以工作

如果原生命令被禁用,内置命令会被移除。自定义/插件命令如果已配置,仍可能注册。

常见设置失败:

- `setMyCommands failed` 且带有 `BOT_COMMANDS_TOO_MUCH` 表示 Telegram 菜单在裁剪后仍然溢出;减少插件/技能/自定义命令,或禁用 `channels.telegram.commands.native`
- 当直接 Bot API curl 命令可用,但 `deleteWebhook``deleteMyCommands`  `setMyCommands` 失败并显示 `404: Not Found` 时,可能表示 `channels.telegram.apiRoot` 被设置成了完整的 `/bot<TOKEN>` 端点。`apiRoot` 必须只是 Bot API 根路径,且 `openclaw doctor --fix` 会移除意外的尾随 `/bot<TOKEN>`
- `getMe returned 401` 表示 Telegram 拒绝了已配置的机器人令牌。使用当前 BotFather 令牌更新 `botToken``tokenFile`  `TELEGRAM_BOT_TOKEN`;OpenClaw 会在轮询前停止,因此这不会被报告为 webhook 清理失败。
- `setMyCommands failed` 且带有网络/fetch 错误通常表示到 `api.telegram.org` 的出站 DNS/HTTPS 被阻止。

### 设备配对命令(`device-pair` 插件)

安装 `device-pair` 插件后:

1. `/pair` 生成设置代码
2. 将代码粘贴到 iOS 应用
3. `/pair pending` 列出待处理请求(包括角色/范围)
4. 批准请求:
   - `/pair approve <requestId>` 用于显式批准
   - 当只有一个待处理请求时使用 `/pair approve`
   - `/pair approve latest` 用于最近的请求

设置代码携带一个短期有效的引导令牌。内置引导交接会将主节点令牌保持在 `scopes: []`;任何被交接的操作员令牌都会限制在 `operator.approvals``operator.read``operator.talk.secrets`  `operator.write` 内。引导范围检查带有角色前缀,因此该操作员允许列表只满足操作员请求;非操作员角色仍需要位于其自身角色前缀下的范围。

如果设备用变更后的身份验证详情重试(例如角色/范围/公钥),之前的待处理请求会被取代,新请求会使用不同的 `requestId`。批准前请重新运行 `/pair pending`

更多详情:[配对](/zh-CN/channels/pairing#pair-via-telegram-recommended-for-ios)。


配置内联键盘范围:

{
  channels: {
    telegram: {
      capabilities: {
        inlineButtons: "allowlist",
      },
    },
  },
}
按账户覆盖:
{
  channels: {
    telegram: {
      accounts: {
        main: {
          capabilities: {
            inlineButtons: "allowlist",
          },
        },
      },
    },
  },
}
范围:

- `off`
- `dm`
- `group`
- `all`
- `allowlist`(默认)

旧版 `capabilities: ["inlineButtons"]` 会映射到 `inlineButtons: "all"`

消息操作示例:
{
  action: "send",
  channel: "telegram",
  to: "123456789",
  message: "Choose an option:",
  buttons: [
    [
      { text: "Yes", callback_data: "yes" },
      { text: "No", callback_data: "no" },
    ],
    [{ text: "Cancel", callback_data: "cancel" }],
  ],
}
回调点击会作为文本传递给智能体:
`callback_data: <value>`


Telegram 工具操作包括:

- `sendMessage``to``content`、可选 `mediaUrl``replyToMessageId``messageThreadId`
- `react``chatId``messageId``emoji`
- `deleteMessage``chatId``messageId`
- `editMessage``chatId``messageId``content`
- `createForumTopic``chatId``name`、可选 `iconColor``iconCustomEmojiId`

频道消息操作提供符合人体工学的别名(`send``react``delete``edit``sticker``sticker-search``topic-create`)。

门控控制:

- `channels.telegram.actions.sendMessage`
- `channels.telegram.actions.deleteMessage`
- `channels.telegram.actions.reactions`
- `channels.telegram.actions.sticker`(默认:禁用)

注意:`edit`  `topic-create` 当前默认启用,且没有单独的 `channels.telegram.actions.*` 开关。
运行时发送使用活动的配置/密钥快照(启动/重新加载),因此操作路径不会在每次发送时执行临时 SecretRef 重新解析。

移除回应的语义:[/tools/reactions](/zh-CN/tools/reactions)


Telegram 支持在生成的输出中使用显式回复线程标签:

- `[[reply_to_current]]` 回复触发消息
- `[[reply_to:<id>]]` 回复特定 Telegram 消息 ID

`channels.telegram.replyToMode` 控制处理方式:

- `off`(默认)
- `first`
- `all`

当回复线程启用且原始 Telegram 文本或说明文字可用时,OpenClaw 会自动包含原生 Telegram 引用摘录。Telegram 将原生引用文本限制为 1024  UTF-16 代码单元,因此较长消息会从开头引用;如果 Telegram 拒绝引用,则回退到普通回复。

注意:`off` 会禁用隐式回复线程。显式 `[[reply_to_*]]` 标签仍会被遵循。


论坛超级群组:

- 主题会话键追加 `:topic:<threadId>`
- 回复和输入状态会面向主题线程
- 主题配置路径
  `channels.telegram.groups.<chatId>.topics.<threadId>`

通用主题(`threadId=1`)特殊情况

- 消息发送会省略 `message_thread_id`(Telegram 拒绝 `sendMessage(...thread_id=1)`)
- 输入状态操作仍包含 `message_thread_id`

主题继承主题条目会继承群组设置除非被覆盖(`requireMention`、`allowFrom`、`skills`、`systemPrompt`、`enabled`、`groupPolicy`)。
`agentId` 仅限主题不会从群组默认值继承

**按主题智能体路由**每个主题都可以通过在主题配置中设置 `agentId` 路由到不同智能体这会为每个主题提供自己的隔离工作区记忆和会话示例

```json5
{
  channels: {
    telegram: {
      groups: {
        "-1001234567890": {
          topics: {
            "1": { agentId: "main" },      // General topic → main agent
            "3": { agentId: "zu" },        // Dev topic → zu agent
            "5": { agentId: "coder" }      // Code review → coder agent
          }
        }
      }
    }
  }
}
```

然后每个话题都有自己的会话键:`agent:zu:telegram:group:-1001234567890:topic:3`

**持久 ACP 话题绑定**论坛话题可以通过顶层类型化 ACP 绑定(`bindings[]`,其中包含 `type: "acp"`、`match.channel: "telegram"`、`peer.kind: "group"`,以及像 `-1001234567890:topic:42` 这样带话题限定的 ID固定 ACP harness 会话目前范围限定为群组/超级群组中的论坛话题请参阅 [ACP Agents](/zh-CN/tools/acp-agents)

**从聊天中生成线程绑定的 ACP**:`/acp spawn <agent> --thread here|auto` 会将当前话题绑定到新的 ACP 会话后续消息会直接路由到那里OpenClaw 会在话题内固定生成确认消息需要保持启用 `channels.telegram.threadBindings.spawnSessions`(默认:`true`)。

模板上下文公开 `MessageThreadId`  `IsForum`。带有 `message_thread_id` 的私信聊天默认保留私信路由并在扁平会话上保留回复元数据只有在配置了 `threadReplies: "inbound"`、`threadReplies: "always"`、`requireTopic: true` 或匹配的话题配置时它们才会使用线程感知的会话键使用顶层 `channels.telegram.dm.threadReplies` 设置账号默认值或使用 `direct.<chatId>.threadReplies` 设置单个私信


### 音频消息

Telegram 会区分语音便笺和音频文件

- 默认音频文件行为
- 在智能体回复中添加标签 `[[audio_as_voice]]` 可强制以语音便笺发送
- 入站语音便笺转录会在智能体上下文中被标记为机器生成的不可信文本提及检测仍使用原始转录因此受提及门控的语音消息会继续工作

消息动作示例
{
  action: "send",
  channel: "telegram",
  to: "123456789",
  media: "https://example.com/voice.ogg",
  asVoice: true,
}
### 视频消息

Telegram 会区分视频文件和视频便笺。

消息动作示例:
{
  action: "send",
  channel: "telegram",
  to: "123456789",
  media: "https://example.com/video.mp4",
  asVideoNote: true,
}
视频便笺不支持字幕;提供的消息文本会单独发送。

### 贴纸

入站贴纸处理:

- 静态 WEBP:下载并处理(占位符 `<media:sticker>`)
- 动画 TGS:跳过
- 视频 WEBM:跳过

贴纸上下文字段:

- `Sticker.emoji`
- `Sticker.setName`
- `Sticker.fileId`
- `Sticker.fileUniqueId`
- `Sticker.cachedDescription`

贴纸缓存文件:

- `~/.openclaw/telegram/sticker-cache.json`

贴纸会被描述一次(如果可能)并缓存,以减少重复的视觉调用。

启用贴纸动作:
{
  channels: {
    telegram: {
      actions: {
        sticker: true,
      },
    },
  },
}
发送贴纸动作:
{
  action: "sticker",
  channel: "telegram",
  to: "123456789",
  fileId: "CAACAgIAAxkBAAI...",
}
搜索已缓存的贴纸:
{
  action: "sticker-search",
  channel: "telegram",
  query: "cat waving",
  limit: 5,
}


Telegram 回应会作为 message_reaction 更新到达(与消息载荷分离)。

启用后,OpenClaw 会将如下系统事件加入队列:

- `Telegram reaction added: 👍 by Alice (@alice) on msg 42`

配置:

- `channels.telegram.reactionNotifications``off | own | all`(默认:`own`
- `channels.telegram.reactionLevel``off | ack | minimal | extensive`(默认:`minimal`

说明:

- `own` 表示仅用户对机器人发送的消息做出的回应(通过已发送消息缓存尽力实现)。
- 回应事件仍遵守 Telegram 访问控制(`dmPolicy``allowFrom``groupPolicy``groupAllowFrom`);未经授权的发送者会被丢弃。
- Telegram 不会在回应更新中提供线程 ID。
  - 非论坛群组路由到群组聊天会话
  - 论坛群组路由到群组通用话题会话(`:topic:1`),而不是确切的原始话题

轮询/webhook  `allowed_updates` 会自动包含 `message_reaction`


ackReaction 会在 OpenClaw 处理入站消息时发送一个确认表情符号。

解析顺序:

- `channels.telegram.accounts.<accountId>.ackReaction`
- `channels.telegram.ackReaction`
- `messages.ackReaction`
- 智能体身份表情符号回退(`agents.list[].identity.emoji`,否则为 "👀")

说明:

- Telegram 需要 unicode 表情符号(例如 "👀")。
- 使用 `""` 可禁用某个渠道或账号的回应。


渠道配置写入默认启用(configWrites !== false)。

Telegram 触发的写入包括:

- 群组迁移事件(`migrate_to_chat_id`)用于更新 `channels.telegram.groups`
- `/config set`  `/config unset`(需要启用命令)

禁用:
{
  channels: {
    telegram: {
      configWrites: false,
    },
  },
}


默认是长轮询。对于 webhook 模式,请设置 channels.telegram.webhookUrlchannels.telegram.webhookSecret;可选项包括 webhookPathwebhookHostwebhookPort(默认值为 /telegram-webhook127.0.0.18787)。

在长轮询模式下,OpenClaw 仅在更新成功分发后才持久化其重启水位线。如果处理器失败,该更新会在同一进程中保持可重试状态,并且不会被写入为已完成以用于重启去重。

本地监听器绑定到 `127.0.0.1:8787`。对于公共入口,请在本地端口前放置反向代理,或有意设置 `webhookHost: "0.0.0.0"`。

Webhook 模式会在向 Telegram 返回 `200` 之前验证请求防护、Telegram 密钥令牌和 JSON 正文。
然后 OpenClaw 会通过与长轮询相同的按聊天/按话题机器人通道异步处理更新,因此缓慢的智能体轮次不会阻塞 Telegram 的投递 ACK。


- channels.telegram.textChunkLimit 默认值为 4000。
- channels.telegram.chunkMode="newline" 会在按长度拆分前优先使用段落边界(空行)。
- channels.telegram.mediaMaxMb(默认 100)限制入站和出站 Telegram 媒体大小。
- channels.telegram.mediaGroupFlushMs(默认 500)控制 Telegram 相册/媒体组在 OpenClaw 将其作为一条入站消息分发前缓冲多长时间。如果相册部分到达较晚,请增大该值;如果要降低相册回复延迟,请减小该值。
- channels.telegram.timeoutSeconds 会覆盖 Telegram API 客户端超时(如果未设置,则使用 grammY 默认值)。机器人客户端会将低于 60 秒出站文本/输入状态请求防护的配置值进行钳制,这样 grammY 就不会在 OpenClaw 的传输防护和回退运行之前中止可见回复投递。长轮询仍使用 45 秒的 getUpdates 请求防护,因此空闲轮询不会被无限期放弃。
- channels.telegram.pollingStallThresholdMs 默认值为 120000;仅在误报轮询停滞重启时,在 30000600000 之间调整。
- 群组上下文历史使用 channels.telegram.historyLimitmessages.groupChat.historyLimit(默认 50);0 会禁用。
- 当 Gateway 网关已观察到父消息时,回复/引用/转发的补充上下文会被规范化到一个选定的对话上下文窗口中;已观察消息缓存会与会话存储一起持久化。Telegram 在更新中只包含一个浅层 reply_to_message,因此早于缓存的链条受限于 Telegram 当前的更新载荷。
- Telegram 允许列表主要用于限制谁可以触发智能体,并不是完整的补充上下文脱敏边界。
- 私信历史控制:
- channels.telegram.dmHistoryLimit
- channels.telegram.dms["<user_id>"].historyLimit
- channels.telegram.retry 配置适用于 Telegram 发送辅助函数(CLI/工具/动作)中可恢复的出站 API 错误。入站最终回复投递也会针对 Telegram 预连接失败使用有界安全发送重试,但不会重试可能导致可见消息重复的模糊发送后网络信封。

CLI 和消息工具发送目标可以是数字聊天 ID、用户名或论坛话题目标:
openclaw message send --channel telegram --target 123456789 --message "hi"
openclaw message send --channel telegram --target @name --message "hi"
openclaw message send --channel telegram --target -1001234567890:topic:42 --message "hi topic"
Telegram 投票使用 `openclaw message poll`,并支持论坛话题:
openclaw message poll --channel telegram --target 123456789 \
  --poll-question "Ship it?" --poll-option "Yes" --poll-option "No"
openclaw message poll --channel telegram --target -1001234567890:topic:42 \
  --poll-question "Pick a time" --poll-option "10am" --poll-option "2pm" \
  --poll-duration-seconds 300 --poll-public
 Telegram 可用的投票标志:

- `--poll-duration-seconds`(5-600)
- `--poll-anonymous`
- `--poll-public`
- `--thread-id` 用于论坛话题(或使用 `:topic:` 目标)

Telegram 发送还支持:

-  `channels.telegram.capabilities.inlineButtons` 允许时,使用带有 `buttons` 块的 `--presentation` 创建内联键盘
- 使用 `--pin`  `--delivery '{"pin":true}'` 在机器人可以在该聊天中固定消息时请求固定投递
- 使用 `--force-document` 将出站图片、GIF 和视频作为文档发送,而不是作为压缩照片、动画媒体或视频上传发送

动作门控:

- `channels.telegram.actions.sendMessage=false` 会禁用出站 Telegram 消息,包括投票
- `channels.telegram.actions.poll=false` 会禁用 Telegram 投票创建,同时保留常规发送功能


Telegram 支持在审批者私信中进行 Exec 审批,也可以选择在原始聊天或话题中发布提示。审批者必须是数字 Telegram 用户 ID。

配置路径:

- `channels.telegram.execApprovals.enabled`(当至少一个审批者可解析时自动启用)
- `channels.telegram.execApprovals.approvers`(回退到来自 `commands.ownerAllowFrom` 的数字所有者 ID)
- `channels.telegram.execApprovals.target``dm`(默认)| `channel` | `both`
- `agentFilter``sessionFilter`

`channels.telegram.allowFrom``groupAllowFrom`  `defaultTo` 控制谁可以与机器人交谈,以及机器人在哪里发送常规回复。它们不会让某人成为 Exec 审批者。当还没有命令所有者时,第一个获批的私信配对会引导生成 `commands.ownerAllowFrom`,因此单所有者设置仍可工作,无需在 `execApprovals.approvers` 下重复 ID。

渠道投递会在聊天中显示命令文本;仅在受信任的群组/话题中启用 `channel`  `both`。当提示落在论坛话题中时,OpenClaw 会为审批提示和后续消息保留该话题。Exec 审批默认在 30 分钟后过期。

内联审批按钮还需要 `channels.telegram.capabilities.inlineButtons` 允许目标表面(`dm``group`  `all`)。带有 `plugin:` 前缀的审批 ID 会通过插件审批解析;其他 ID 会先通过 Exec 审批解析。

请参阅 [Exec 审批](/zh-CN/tools/exec-approvals)


错误回复控制

当 agent 遇到投递或提供商错误时,Telegram 可以回复错误文本,也可以抑制该错误。两个配置键控制此行为:

默认值 说明
channels.telegram.errorPolicy reply, silent reply reply 会向聊天发送一条友好的错误消息。silent 会完全抑制错误回复。
channels.telegram.errorCooldownMs number (ms) 60000 向同一聊天发送错误回复之间的最短时间。防止故障期间产生错误垃圾消息。

支持按账户、按群组和按主题覆盖(继承方式与其他 Telegram 配置键相同)。

{
  channels: {
    telegram: {
      errorPolicy: "reply",
      errorCooldownMs: 120000,
      groups: {
        "-1001234567890": {
          errorPolicy: "silent", // suppress errors in this group
        },
      },
    },
  },
}

故障排除


- 如果 `requireMention=false`,Telegram 隐私模式必须允许完全可见性。
  - BotFather:`/setprivacy` -> Disable
  - 然后将机器人从群组中移除并重新添加
- 当配置预期接收未提及的群组消息时,`openclaw channels status` 会发出警告。
- `openclaw channels status --probe` 可以检查明确的数字群组 ID;通配符 `"*"` 无法探测成员资格。
- 快速会话测试:`/activation always`。

-`channels.telegram.groups` 存在时,群组必须列出(或包含 `"*"`- 验证机器人在群组中的成员资格
- 查看日志:`openclaw logs --follow` 以了解跳过原因

- 授权你的发送者身份(配对和/或数字 `allowFrom`
- 即使群组策略为 `open`,命令授权仍然适用
- `setMyCommands failed` 并出现 `BOT_COMMANDS_TOO_MUCH` 表示原生命令菜单条目过多;减少插件/技能/自定义命令,或禁用原生菜单
- `deleteMyCommands` / `setMyCommands` 启动调用和 `sendChatAction` 输入状态调用都有边界限制,并会在请求超时时通过 Telegram 的传输回退重试一次。持续的网络/获取错误通常表示到 `api.telegram.org`  DNS/HTTPS 可达性问题

- `getMe returned 401` 是已配置机器人令牌的 Telegram 身份验证失败。
-  BotFather 中重新复制或重新生成机器人令牌,然后更新默认账户的 `channels.telegram.botToken``channels.telegram.tokenFile``channels.telegram.accounts.<id>.botToken`  `TELEGRAM_BOT_TOKEN`
- 启动期间的 `deleteWebhook 401 Unauthorized` 也是身份验证失败;将其视为“没有 webhook 存在”只会把同一个错误令牌失败推迟到之后的 API 调用。

- Node 22+ + 自定义 fetch/proxy  AbortSignal 类型不匹配时可能触发立即中止行为。
- 某些主机会先将 `api.telegram.org` 解析为 IPv6;损坏的 IPv6 出站可能导致间歇性 Telegram API 失败。
- 如果日志包含 `TypeError: fetch failed`  `Network request for 'getUpdates' failed!`,OpenClaw 现在会将这些作为可恢复的网络错误重试。
- 在轮询启动期间,OpenClaw 会为 grammY 复用成功的启动 `getMe` 探测,因此 runner 不需要在第一次 `getUpdates` 前再执行第二次 `getMe`
- 如果 `deleteWebhook` 在轮询启动期间因瞬时网络错误失败,OpenClaw 会继续进入长轮询,而不是再发起另一个预轮询控制平面调用。仍处于活动状态的 webhook 会表现为 `getUpdates` 冲突;随后 OpenClaw 会重建 Telegram 传输并重试 webhook 清理。
- 如果 Telegram 套接字按较短的固定周期回收,请检查是否存在过低的 `channels.telegram.timeoutSeconds`;机器人客户端会将低于出站和 `getUpdates` 请求保护值的配置值钳制到保护值之上,但旧版本在该值低于这些保护值时可能会中止每次轮询或回复。
- 如果日志包含 `Polling stall detected`,OpenClaw 默认会在 120 秒内没有完成长轮询活跃性后重启轮询并重建 Telegram 传输。
- `openclaw channels status --probe`  `openclaw doctor` 会在以下情况发出警告:正在运行的轮询账户在启动宽限期后尚未完成 `getUpdates`;正在运行的 webhook 账户在启动宽限期后尚未完成 `setWebhook`;或最近一次成功的轮询传输活动已过期。
- 仅当长时间运行的 `getUpdates` 调用健康、但你的主机仍报告误报的轮询停滞重启时,才提高 `channels.telegram.pollingStallThresholdMs`。持续停滞通常指向主机与 `api.telegram.org` 之间的代理、DNS、IPv6  TLS 出站问题。
- Telegram  Bot API 传输也遵循进程代理环境变量,包括 `HTTP_PROXY``HTTPS_PROXY``ALL_PROXY` 及其小写变体。`NO_PROXY` / `no_proxy` 仍可绕过 `api.telegram.org`
- 如果通过服务环境中的 `OPENCLAW_PROXY_URL` 配置了 OpenClaw 托管代理,且没有标准代理环境变量,则 Telegram 也会将该 URL 用于 Bot API 传输。
- 在直接出站/TLS 不稳定的 VPS 主机上,通过 `channels.telegram.proxy` 路由 Telegram API 调用:
channels:
  telegram:
    proxy: socks5://<user>:<password>@proxy-host:1080
- Node 22+ 默认使用 `autoSelectFamily=true`(WSL2 除外)。Telegram DNS 结果顺序依次遵循 `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER``channels.telegram.network.dnsResultOrder`,然后是进程默认值,例如 `NODE_OPTIONS=--dns-result-order=ipv4first`;如果都不适用,Node 22+ 会回退到 `ipv4first`
- 如果你的主机是 WSL2,或明确在仅 IPv4 行为下表现更好,请强制选择地址族:
channels:
  telegram:
    network:
      autoSelectFamily: false
- RFC 2544 基准测试范围应答(`198.18.0.0/15`)默认已允许
  用于 Telegram 媒体下载。如果受信任的 fake-IP 或
  透明代理在媒体下载期间将 `api.telegram.org` 重写为其他
  私有/内部/特殊用途地址,你可以选择启用
  仅限 Telegram 的绕过:
channels:
  telegram:
    network:
      dangerouslyAllowPrivateNetwork: true
- 同一选择性启用也可按账户在
  `channels.telegram.accounts.<accountId>.network.dangerouslyAllowPrivateNetwork` 中配置。
- 如果你的代理将 Telegram 媒体主机解析到 `198.18.x.x`,请先保持
  该危险标志关闭。Telegram 媒体默认已经允许 RFC 2544
  基准测试范围。

<Warning>
  `channels.telegram.network.dangerouslyAllowPrivateNetwork` 会削弱 Telegram
  媒体 SSRF 保护。仅在受信任且由操作者控制的代理环境中使用它,
  例如 Clash、Mihomo  Surge fake-IP 路由,并且它们会合成
  RFC 2544 基准测试范围以外的私有或特殊用途应答。对于普通公网
  Telegram 访问,请保持关闭。
</Warning>

- 环境变量覆盖(临时):
  - `OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1`
  - `OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1`
  - `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4first`
- 验证 DNS 应答:
dig +short api.telegram.org A
dig +short api.telegram.org AAAA


更多帮助:渠道故障排除

配置参考

主要参考:Configuration reference - Telegram

  • 启动/身份验证:enabledbotTokentokenFileaccounts.*tokenFile 必须指向常规文件;符号链接会被拒绝)
  • 访问控制:dmPolicyallowFromgroupPolicygroupAllowFromgroupsgroups.*.topics.*、顶层 bindings[]type: "acp"
  • Exec 审批:execApprovalsaccounts.*.execApprovals
  • 命令/菜单:commands.nativecommands.nativeSkillscustomCommands
  • 线程/回复:replyToModedm.threadRepliesdirect.*.threadReplies
  • 流式传输:streaming(预览)、streaming.preview.toolProgressblockStreaming
  • 格式化/投递:textChunkLimitchunkModelinkPreviewresponsePrefix
  • 媒体/网络:mediaMaxMbmediaGroupFlushMstimeoutSecondspollingStallThresholdMsretrynetwork.autoSelectFamilynetwork.dangerouslyAllowPrivateNetworkproxy
  • 自定义 API 根地址:apiRoot(仅 Bot API 根地址;不要包含 /bot<TOKEN>
  • webhook:webhookUrlwebhookSecretwebhookPathwebhookHost
  • 操作/能力:capabilities.inlineButtonsactions.sendMessage|editMessage|deleteMessage|reactions|sticker
  • 表情回应:reactionNotificationsreactionLevel
  • 错误:errorPolicyerrorCooldownMs
  • 写入/历史:configWriteshistoryLimitdmHistoryLimitdms.*.historyLimit


多账户优先级:当配置了两个或更多账户 ID 时,请设置 channels.telegram.defaultAccount(或包含 channels.telegram.accounts.default)以显式指定默认路由。否则 OpenClaw 会回退到第一个规范化账户 ID,并且 openclaw doctor 会发出警告。具名账户会继承 channels.telegram.allowFrom / groupAllowFrom,但不会继承 accounts.default.* 值。

相关内容



将 Telegram 用户配对到 Gateway 网关。


群组和主题允许列表行为。


将入站消息路由到智能体。


威胁模型和加固。


将群组和主题映射到智能体。


跨渠道诊断。


📄 Tlon

原文:https://docs.openclaw.ai/zh-CN/channels/tlon

Tlon 是构建在 Urbit 上的去中心化消息应用。OpenClaw 会连接到你的 Urbit ship,并可以
回复私信和群聊消息。默认情况下,群组回复需要 @ 提及,也可以
通过允许列表进一步限制。

Status:内置插件。支持私信、群组提及、线程回复、富文本格式和
图片上传。暂不支持表情回应和投票。

内置插件

在当前 OpenClaw 版本中,Tlon 作为内置插件随附,因此普通打包
构建不需要单独安装。

如果你使用的是旧版本构建,或排除了 Tlon 的自定义安装,请安装
当前 npm 包:

通过 CLI 安装(npm registry):

openclaw plugins install @openclaw/tlon

使用裸包名可跟随当前官方发布标签。仅在需要可复现安装时才固定精确
版本。

本地检出(从 git 仓库运行时):

openclaw plugins install ./path/to/local/tlon-plugin

详情:插件

设置

  1. 确保 Tlon 插件可用。
    - 当前打包的 OpenClaw 版本已内置它。
    - 旧版/自定义安装可以使用上面的命令手动添加它。
  2. 收集你的 ship URL 和登录码。
  3. 配置 channels.tlon
  4. 重启 Gateway 网关。
  5. 给机器人发私信,或在群组频道中提及它。

最小配置(单账户):

{
  channels: {
    tlon: {
      enabled: true,
      ship: "~sampel-palnet",
      url: "https://your-ship-host",
      code: "lidlut-tabwed-pillex-ridrup",
      ownerShip: "~your-main-ship", // recommended: your ship, always allowed
    },
  },
}

私有/LAN ships

默认情况下,OpenClaw 会阻止私有/内部主机名和 IP 范围以提供 SSRF 保护。
如果你的 ship 运行在私有网络上(localhost、LAN IP 或内部主机名),
你必须显式选择启用:

{
  channels: {
    tlon: {
      url: "http://localhost:8080",
      allowPrivateNetwork: true,
    },
  },
}

这适用于如下 URL:

  • http://localhost:8080
  • http://192.168.x.x:8080
  • http://my-ship.local:8080

⚠️ 仅在你信任本地网络时启用此项。该设置会对发送到你的 ship URL 的请求
禁用 SSRF 保护。

群组频道

默认启用自动发现。你也可以手动固定频道:

{
  channels: {
    tlon: {
      groupChannels: ["chat/~host-ship/general", "chat/~host-ship/support"],
    },
  },
}

禁用自动发现:

{
  channels: {
    tlon: {
      autoDiscoverChannels: false,
    },
  },
}

访问控制

私信允许列表(空 = 不允许私信,使用 ownerShip 进行批准流程):

{
  channels: {
    tlon: {
      dmAllowlist: ["~zod", "~nec"],
    },
  },
}

群组授权(默认受限):

{
  channels: {
    tlon: {
      defaultAuthorizedShips: ["~zod"],
      authorization: {
        channelRules: {
          "chat/~host-ship/general": {
            mode: "restricted",
            allowedShips: ["~zod", "~nec"],
          },
          "chat/~host-ship/announcements": {
            mode: "open",
          },
        },
      },
    },
  },
}

所有者和批准系统

设置所有者 ship,以便在未授权用户尝试交互时接收批准请求:

{
  channels: {
    tlon: {
      ownerShip: "~your-main-ship",
    },
  },
}

所有者 ship 会自动在所有位置获得授权——私信邀请会自动接受,
频道消息始终允许。你不需要将所有者添加到 dmAllowlist
defaultAuthorizedShips

设置后,所有者会收到以下私信通知:

  • 来自不在允许列表中的 ships 的私信请求
  • 未授权频道中的提及
  • 群组邀请请求

自动接受设置

自动接受私信邀请(针对 dmAllowlist 中的 ships):

{
  channels: {
    tlon: {
      autoAcceptDmInvites: true,
    },
  },
}

自动接受来自受信任 ships 的群组邀请:

{
  channels: {
    tlon: {
      autoAcceptGroupInvites: true,
      groupInviteAllowlist: ["~zod"],
    },
  },
}

groupInviteAllowlist 为空时,autoAcceptGroupInvites 会默认拒绝。将
允许列表设置为应自动接受其群组邀请的 ships。

递送目标(CLI/cron)

将这些目标与 openclaw message send 或 cron 递送一起使用:

  • 私信:~sampel-palnetdm/~sampel-palnet
  • 群组:chat/~host-ship/channelgroup:~host-ship/channel

内置 Skills

Tlon 插件包含一个内置 Skills(@tloncorp/tlon-skill),
它提供对 Tlon 操作的 CLI 访问:

  • 联系人:获取/更新资料,列出联系人
  • 频道:列出、创建、发布消息、获取历史记录
  • 群组:列出、创建、管理成员
  • 私信:发送消息、对消息作出回应
  • 表情回应:向帖子和私信添加/移除 emoji 表情回应
  • 设置:通过 slash commands 管理插件权限

安装插件后,该 Skills 会自动可用。

能力

功能 Status
直接消息 ✅ 支持
群组/频道 ✅ 支持(默认需要提及)
线程 ✅ 支持(在线程中自动回复)
富文本 ✅ Markdown 转换为 Tlon 格式
图片 ✅ 上传到 Tlon 存储
表情回应 ✅ 通过内置 Skills
投票 ❌ 暂不支持
原生命令 ✅ 支持(默认仅所有者可用)

故障排除

先运行这个排查步骤:

openclaw status
openclaw gateway status
openclaw logs --follow
openclaw doctor

常见故障:

  • 忽略私信:发送者不在 dmAllowlist 中,且未配置用于批准流程的 ownerShip
  • 忽略群组消息:频道未被发现,或发送者未授权。
  • 连接错误:检查 ship URL 是否可访问;对本地 ships 启用 allowPrivateNetwork
  • 认证错误:确认登录码是当前有效的(代码会轮换)。

配置参考

完整配置:配置

提供商选项:

  • channels.tlon.enabled:启用/禁用渠道启动。
  • channels.tlon.ship:机器人的 Urbit ship 名称(例如 ~sampel-palnet)。
  • channels.tlon.url:ship URL(例如 https://sampel-palnet.tlon.network)。
  • channels.tlon.code:ship 登录码。
  • channels.tlon.allowPrivateNetwork:允许 localhost/LAN URL(SSRF 绕过)。
  • channels.tlon.ownerShip:批准系统的所有者 ship(始终授权)。
  • channels.tlon.dmAllowlist:允许发私信的 ships(空 = 无)。
  • channels.tlon.autoAcceptDmInvites:自动接受来自允许列表中 ships 的私信。
  • channels.tlon.autoAcceptGroupInvites:自动接受来自允许列表中 ships 的群组邀请。
  • channels.tlon.groupInviteAllowlist:其群组邀请可以被自动接受的 ships。
  • channels.tlon.autoDiscoverChannels:自动发现群组频道(默认:true)。
  • channels.tlon.groupChannels:手动固定的频道 nests。
  • channels.tlon.defaultAuthorizedShips:对所有频道授权的 ships。
  • channels.tlon.authorization.channelRules:按频道设置的认证规则。
  • channels.tlon.showModelSignature:在消息后附加模型名称。

备注

  • 群组回复需要提及(例如 ~your-bot-ship)才会响应。
  • 线程回复:如果传入消息在线程中,OpenClaw 会在线程内回复。
  • 富文本:Markdown 格式(粗体、斜体、代码、标题、列表)会转换为 Tlon 的原生格式。
  • 图片:URL 会上传到 Tlon 存储,并作为图片块嵌入。

相关内容


📄 Twitch

原文:https://docs.openclaw.ai/zh-CN/channels/twitch

通过 IRC 连接支持 Twitch 聊天。OpenClaw 以 Twitch 用户(机器人账号)身份连接,用于在频道中接收和发送消息。

内置插件


Twitch 在当前 OpenClaw 版本中作为内置插件提供,因此普通打包构建不需要单独安装。

如果你使用的是较旧构建,或是排除了 Twitch 的自定义安装,请直接安装 npm 包:



bash
openclaw plugins install @openclaw/twitch



bash
openclaw plugins install ./path/to/local/twitch-plugin


使用裸包名可跟随当前官方发布标签。仅在需要可复现安装时才固定精确版本。

详情:插件

快速设置(初学者)



当前打包的 OpenClaw 版本已内置该插件。较旧/自定义安装可以使用上面的命令手动添加。


为机器人创建一个专用 Twitch 账号(或使用现有账号)。


使用 Twitch Token Generator

- 选择 **Bot Token**
- 确认已选择 `chat:read``chat:write` 作用域
- 复制 **Client ID** 和 **Access Token**



使用 https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/ 将用户名转换为 Twitch 用户 ID。


- 环境变量:OPENCLAW_TWITCH_ACCESS_TOKEN=...(仅默认账号)
- 或配置:channels.twitch.accessToken

如果两者都已设置,配置优先(环境变量回退仅适用于默认账号)。



使用已配置的渠道启动 Gateway 网关。


添加访问控制(allowFromallowedRoles),防止未经授权的用户触发机器人。requireMention 默认值为 true

最小配置:

{
  channels: {
    twitch: {
      enabled: true,
      username: "openclaw", // Bot's Twitch account
      accessToken: "oauth:abc123...", // OAuth Access Token (or use OPENCLAW_TWITCH_ACCESS_TOKEN env var)
      clientId: "xyz789...", // Client ID from Token Generator
      channel: "vevisk", // Which Twitch channel's chat to join (required)
      allowFrom: ["123456789"], // (recommended) Your Twitch user ID only - get it from https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
    },
  },
}

它是什么

  • 一个由 Gateway 网关拥有的 Twitch 渠道。
  • 确定性路由:回复始终返回 Twitch。
  • 每个账号都会映射到一个隔离的会话键 agent:<agentId>:twitch:<accountName>
  • username 是机器人的账号(用于认证),channel 是要加入的聊天室。

设置(详细)

生成凭证

使用 Twitch Token Generator

  • 选择 Bot Token
  • 确认已选择 chat:readchat:write 作用域
  • 复制 Client IDAccess Token


不需要手动注册应用。令牌会在数小时后过期。

配置机器人



bash
OPENCLAW_TWITCH_ACCESS_TOKEN=oauth:abc123...



json5
{
channels: {
twitch: {
enabled: true,
username: "openclaw",
accessToken: "oauth:abc123...",
clientId: "xyz789...",
channel: "vevisk",
},
},
}


如果环境变量和配置都已设置,配置优先。

访问控制(推荐)

{
  channels: {
    twitch: {
      allowFrom: ["123456789"], // (recommended) Your Twitch user ID only
    },
  },
}

优先使用 allowFrom 作为硬性允许列表。如果你想要基于角色的访问,请改用 allowedRoles

可用角色: "moderator""owner""vip""subscriber""all"


为什么使用用户 ID? 用户名可以更改,可能导致冒充。用户 ID 是永久的。

查找你的 Twitch 用户 ID:https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/(将你的 Twitch 用户名转换为 ID)

令牌刷新(可选)

来自 Twitch Token Generator 的令牌无法自动刷新,请在过期后重新生成。

若要自动刷新令牌,请在 Twitch Developer Console 创建你自己的 Twitch 应用,并添加到配置:

{
  channels: {
    twitch: {
      clientSecret: "your_client_secret",
      refreshToken: "your_refresh_token",
    },
  },
}

机器人会在过期前自动刷新令牌,并记录刷新事件。

多账号支持

使用 channels.twitch.accounts 配置每个账号的令牌。共享模式请参阅配置

示例(一个机器人账号加入两个频道):

{
  channels: {
    twitch: {
      accounts: {
        channel1: {
          username: "openclaw",
          accessToken: "oauth:abc123...",
          clientId: "xyz789...",
          channel: "vevisk",
        },
        channel2: {
          username: "openclaw",
          accessToken: "oauth:def456...",
          clientId: "uvw012...",
          channel: "secondchannel",
        },
      },
    },
  },
}


每个账号都需要自己的令牌(每个频道一个令牌)。

访问控制



json5
{
channels: {
twitch: {
accounts: {
default: {
allowFrom: ["123456789", "987654321"],
},
},
},
},
}



json5
{
channels: {
twitch: {
accounts: {
default: {
allowedRoles: ["moderator", "vip"],
},
},
},
},
}

`allowFrom` 是硬性允许列表。设置后,仅允许这些用户 ID。如果你想要基于角色的访问,请不要设置 `allowFrom`,而是配置 `allowedRoles`



默认情况下,requireMentiontrue。若要禁用并响应所有消息:

```json5
{
  channels: {
    twitch: {
      accounts: {
        default: {
          requireMention: false,
        },
      },
    },
  },
}
```


故障排除

首先运行诊断命令:

openclaw doctor
openclaw channels status --probe



- 检查访问控制: 确保你的用户 ID 位于 allowFrom 中,或临时移除 allowFrom 并设置 allowedRoles: ["all"] 进行测试。
- 检查机器人是否在频道中: 机器人必须加入 channel 中指定的频道。



“连接失败”或认证错误:

- 确认 `accessToken`  OAuth 访问令牌值(通常以 `oauth:` 前缀开头)
- 检查令牌是否具有 `chat:read`  `chat:write` 作用域
- 如果使用令牌刷新,请确认已设置 `clientSecret`  `refreshToken`



检查日志中的刷新事件:

```
Using env token source for mybot
Access token refreshed for user 123456 (expires in 14400s)
```

如果你看到 “token refresh disabled (no refresh token)”:

- 确保已提供 `clientSecret`
- 确保已提供 `refreshToken`


配置

账号配置


机器人用户名。


具有 chat:readchat:write 的 OAuth 访问令牌。


Twitch Client ID(来自 Token Generator 或你的应用)。


要加入的频道。


启用此账号。


可选:用于自动刷新令牌。


可选:用于自动刷新令牌。


令牌有效期(秒)。


令牌获取时间戳。


用户 ID 允许列表。

<ParamField path="allowedRoles" type='Array<"moderator" | "owner" | "vip" | "subscriber" | "all">'>
基于角色的访问控制。


要求 @mention。

提供商选项

  • channels.twitch.enabled - 启用/禁用渠道启动
  • channels.twitch.username - 机器人用户名(简化的单账号配置)
  • channels.twitch.accessToken - OAuth 访问令牌(简化的单账号配置)
  • channels.twitch.clientId - Twitch Client ID(简化的单账号配置)
  • channels.twitch.channel - 要加入的频道(简化的单账号配置)
  • channels.twitch.accounts.<accountName> - 多账号配置(上方所有账号字段)

完整示例:

{
  channels: {
    twitch: {
      enabled: true,
      username: "openclaw",
      accessToken: "oauth:abc123...",
      clientId: "xyz789...",
      channel: "vevisk",
      clientSecret: "secret123...",
      refreshToken: "refresh456...",
      allowFrom: ["123456789"],
      allowedRoles: ["moderator", "vip"],
      accounts: {
        default: {
          username: "mybot",
          accessToken: "oauth:abc123...",
          clientId: "xyz789...",
          channel: "your_channel",
          enabled: true,
          clientSecret: "secret123...",
          refreshToken: "refresh456...",
          expiresIn: 14400,
          obtainmentTimestamp: 1706092800000,
          allowFrom: ["123456789", "987654321"],
          allowedRoles: ["moderator"],
        },
      },
    },
  },
}

工具动作

智能体可以调用 twitch 并使用 action:

  • send - 向频道发送消息

示例:

{
  action: "twitch",
  params: {
    message: "Hello Twitch!",
    to: "#mychannel",
  },
}

安全与运维

  • 像对待密码一样对待令牌 — 切勿将令牌提交到 git。
  • 使用自动令牌刷新 适用于长期运行的机器人。
  • 使用用户 ID 允许列表 而不是用户名来进行访问控制。
  • 监控日志 以查看令牌刷新事件和连接状态。
  • 最小化令牌作用域 — 仅请求 chat:readchat:write
  • 如果卡住:确认没有其他进程占用该会话后,重启 Gateway 网关。

限制

  • 每条消息 500 个字符(会在词边界自动分块)。
  • Markdown 会在分块前被移除。
  • 无速率限制(使用 Twitch 内置速率限制)。

相关


📄 WhatsApp

原文:https://docs.openclaw.ai/zh-CN/channels/whatsapp

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 中,也可以使用。



默认私信策略会为未知发送者进行配对。


跨渠道诊断和修复手册。


完整的渠道配置模式和示例。

快速设置


{
  channels: {
    whatsapp: {
      dmPolicy: "pairing",
      allowFrom: ["+15551234567"],
      groupPolicy: "allowlist",
      groupAllowFrom: ["+15551234567"],
    },
  },
}

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

openclaw gateway

openclaw pairing list whatsapp
openclaw pairing approve whatsapp <CODE>
配对请求会在 1 小时后过期。每个渠道的待处理请求最多为 3 个。



OpenClaw 建议在可行时为 WhatsApp 使用单独号码。(渠道元数据和设置流程已针对该设置优化,但也支持个人号码设置。)

部署模式



这是最清晰的运维模式:

- 为 OpenClaw 使用单独的 WhatsApp 身份
- 更清晰的私信允许列表和路由边界
- 降低自聊混淆的可能性

最小策略模式:

```json5
{
  channels: {
    whatsapp: {
      dmPolicy: "allowlist",
      allowFrom: ["+15551234567"],
    },
  },
}
```


新手引导支持个人号码模式,并会写入适合自聊的基线配置:

- `dmPolicy: "allowlist"`
- `allowFrom` 包含你的个人号码
- `selfChatMode: true`

在运行时,自聊保护会根据已关联的自身号码和 `allowFrom` 生效。


在当前 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 可以用其原生 @newsletter JID 作为显式出站目标。出站 newsletter 发送使用渠道会话元数据(agent:<agentId>:whatsapp:channel:<jid>),而不是私信会话语义。
  • WhatsApp Web 传输会遵循 Gateway 网关主机上的标准代理环境变量(HTTPS_PROXYHTTP_PROXYNO_PROXY / 小写变体)。优先使用主机级代理配置,而不是 WhatsApp 渠道专用代理设置。
  • 启用 messages.removeAckAfterReply 时,OpenClaw 会在可见回复送达后清除 WhatsApp ack 反应。

插件钩子和隐私

WhatsApp 入站消息可能包含个人消息内容、电话号码、群组标识符、发送者姓名和会话关联字段。因此,除非你显式选择加入,否则 WhatsApp 不会向插件广播入站 message_received 钩子载荷:

{
  channels: {
    whatsapp: {
      pluginHooks: {
        messageReceived: true,
      },
    },
  },
}

你可以将选择加入限定到一个账号:

{
  channels: {
    whatsapp: {
      accounts: {
        work: {
          pluginHooks: {
            messageReceived: true,
          },
        },
      },
    },
  },
}

仅对你信任其接收 WhatsApp 入站消息内容和标识符的插件启用此功能。

访问控制和激活



channels.whatsapp.dmPolicy 控制直接聊天访问:

- `pairing`(默认)
- `allowlist`
- `open`(要求 `allowFrom` 包含 `"*"`
- `disabled`

`allowFrom` 接受 E.164 风格号码(内部会规范化)。

`allowFrom` 是私信发送者访问控制列表。它不会限制显式出站发送到 WhatsApp 群组 JID  `@newsletter` 渠道 JID。

多账号覆盖:`channels.whatsapp.accounts.<id>.dmPolicy`(以及 `allowFrom`)会优先于该账号的渠道级默认值。

运行时行为详情:

- 配对会持久化到渠道允许存储,并与配置的 `allowFrom` 合并
- 定时自动化和 Heartbeat 接收者回退会使用显式投递目标或配置的 `allowFrom`;私信配对批准不会隐式成为 cron  Heartbeat 接收者
- 如果未配置允许列表,则默认允许已关联的自身号码
- OpenClaw 永远不会自动配对出站 `fromMe` 私信(你从已关联设备发给自己的消息)


群组访问有两层:

1. **群组成员允许列表**`channels.whatsapp.groups`
   - 如果省略 `groups`,所有群组都符合条件
   - 如果存在 `groups`,它会作为群组允许列表(允许 `"*"`

2. **群组发送者策略**`channels.whatsapp.groupPolicy` + `groupAllowFrom`
   - `open`:绕过发送者允许列表
   - `allowlist`:发送者必须匹配 `groupAllowFrom`(或 `*`
   - `disabled`:阻止所有群组入站

发送者允许列表回退:

- 如果未设置 `groupAllowFrom`,运行时会在可用时回退到 `allowFrom`
- 发送者允许列表会在 mention/回复激活之前评估

注意:如果完全不存在 `channels.whatsapp` 块,即使设置了 `channels.defaults.groupPolicy`,运行时群组策略回退也会是 `allowlist`(并记录警告日志)。


群组回复默认需要 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]

消息规范化和上下文



传入的 WhatsApp 消息会包装在共享入站信封中。

如果存在引用回复,上下文会按此形式追加:

```text
[Replying to <sender> id:<stanzaId>]
<quoted body or media placeholder>
[/Replying]
```

可用时也会填充回复元数据字段(`ReplyToId``ReplyToBody``ReplyToSender`、发送者 JID/E.164)。
当引用回复目标是可下载媒体时,OpenClaw 会通过正常的入站媒体存储保存它,并将其暴露为 `MediaPath`/`MediaType`,以便智能体检查被引用的图片,而不是只能看到 `<media:image>`


仅媒体入站消息会使用如下占位符规范化:

- `<media:image>`
- `<media:video>`
- `<media:audio>`
- `<media:document>`
- `<media:sticker>`

当正文只有 `<media:audio>` 时,已授权群组语音便笺会在 mention 门控前转录,因此在语音便笺中说出 bot mention 可以触发回复。如果转录仍未 mention bot,则转录会保留在待处理群组历史中,而不是保留原始占位符。

位置正文使用简短的坐标文本。位置标签/评论和联系人/vCard 详情会呈现为围栏式不可信元数据,而不是内联提示文本。


对于群组,未处理消息可以被缓冲,并在 bot 最终被触发时作为上下文注入。

- 默认限制:`50`
- 配置:`channels.whatsapp.historyLimit`
- 回退:`messages.groupChat.historyLimit`
- `0` 禁用

注入标记:

- `[Chat messages since your last reply - for context]`
- `[Current message - respond to this]`


对于已接受的 WhatsApp 入站消息,默认启用已读回执。

全局禁用:

```json5
{
  channels: {
    whatsapp: {
      sendReadReceipts: false,
    },
  },
}
```

按账号覆盖:

```json5
{
  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.reactions
  • channels.whatsapp.actions.polls
  • 默认启用渠道发起的配置写入(通过 channels.whatsapp.configWrites=false 禁用)。

故障排除



症状:渠道状态报告未关联。

修复:

```bash
openclaw channels login --channel whatsapp
openclaw channels status
```


症状:已关联账号反复断开连接或尝试重连。

安静账号可以在正常消息超时后保持连接;当 WhatsApp Web 传输活动停止、套接字关闭,或应用级活动在更长的安全窗口内保持静默时,watchdog 会重启。

如果日志显示反复出现 `status=408 Request Time-out Connection was lost`,请调整 `web.whatsapp` 下的 Baileys 套接字时序。先将 `keepAliveIntervalMs` 缩短到低于你的网络空闲超时,并在慢速或有丢包的链路上增加 `connectTimeoutMs`

```json5
{
  web: {
    whatsapp: {
      keepAliveIntervalMs: 15000,
      connectTimeoutMs: 60000,
      defaultQueryTimeoutMs: 60000,
    },
  },
}
```

修复:

```bash
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` 重新关联。


症状: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 送达会单独检查:只有在 Baileys 针对至少一次可见文本或媒体发送返回出站消息 ID 后,OpenClaw 才会将自动回复视为已发送。

确认反应是独立的回复前回执。成功的反应并不能证明后续文本或媒体回复已被 WhatsApp 接受。

检查 Gateway 网关日志中的 `auto-reply delivery failed` 或 `auto-reply was not accepted by WhatsApp provider`。


按以下顺序检查:

- `groupPolicy`
- `groupAllowFrom` / `allowFrom`
- `groups` 允许列表条目
- 提及门控(`requireMention` + 提及模式)
- `openclaw.json` 中的重复键(JSON5):后面的条目会覆盖前面的条目,因此每个作用域只保留一个 `groupPolicy`


WhatsApp Gateway 网关运行时应使用 Node。Bun 被标记为不兼容稳定的 WhatsApp/Telegram Gateway 网关运行。

系统提示

WhatsApp 通过 groupsdirect 映射支持适用于群组和直接聊天的 Telegram 风格系统提示。

群组消息的解析层级:

首先确定有效的 groups 映射:如果账号定义了自己的 groups,它会完全替换根 groups 映射(不进行深度合并)。然后在生成的单一映射上执行提示查找:

  1. 群组专用系统提示groups["<groupId>"].systemPrompt):当映射中存在特定群组条目定义了其 systemPrompt 键时使用。如果 systemPrompt 为空字符串(""),则会抑制通配符,并且不应用系统提示。
  2. 群组通配符系统提示groups["*"].systemPrompt):当特定群组条目完全不存在于映射中,或存在但未定义 systemPrompt 键时使用。

直接消息的解析层级:

首先确定有效的 direct 映射:如果账号定义了自己的 direct,它会完全替换根 direct 映射(不进行深度合并)。然后在生成的单一映射上执行提示查找:

  1. Direct 专属系统提示词direct["<peerId>"].systemPrompt):当映射中存在特定对等方条目systemPrompt 键已定义时使用。如果 systemPrompt 是空字符串(""),则会抑制通配符,并且不会应用系统提示词。
  2. Direct 通配符系统提示词direct["*"].systemPrompt):当映射中完全不存在特定对等方条目,或条目存在但未定义 systemPrompt 键时使用。


dms 仍然是轻量级的按私信历史覆盖存储桶(dms.<id>.historyLimit)。提示词覆盖位于 direct 下。

与 Telegram 多账号行为的区别:在 Telegram 中,多账号设置会有意抑制根级 groups 对所有账号的继承,即使某些账号没有定义自己的 groups,也是如此,以防止机器人接收它不属于的群组中的群组消息。WhatsApp 不应用此防护:对于没有定义账号级覆盖的账号,无论配置了多少个账号,根级 groups 和根级 direct 都始终会被继承。在多账号 WhatsApp 设置中,如果你想要按账号设置群组或 direct 提示词,请在每个账号下显式定义完整映射,而不是依赖根级默认值。

重要行为:

  • channels.whatsapp.groups 既是按群组配置映射,也是聊天级群组允许列表。在根级或账号作用域中,groups["*"] 表示该作用域“允许所有群组”。
  • 只有在你已经希望该作用域允许所有群组时,才添加通配符群组 systemPrompt。如果你仍然只希望一组固定的群组 ID 符合条件,请不要使用 groups["*"] 作为提示词默认值。改为在每个显式允许列表中的群组条目上重复该提示词。
  • 群组准入和发送者授权是两个独立检查。groups["*"] 会扩大可进入群组处理的群组集合,但它本身不会授权这些群组中的每个发送者。发送者访问仍由 channels.whatsapp.groupPolicychannels.whatsapp.groupAllowFrom 单独控制。
  • channels.whatsapp.direct 对私信没有相同的副作用。direct["*"] 只会在私信已通过 dmPolicyallowFrom 或配对存储规则准入后,提供默认 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.
            "120363406415684625@g.us": {
              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 字段:

  • 访问:dmPolicyallowFromgroupPolicygroupAllowFromgroups
  • 投递:textChunkLimitchunkModemediaMaxMbsendReadReceiptsackReactionreactionLevel
  • 多账号:accounts.<id>.enabledaccounts.<id>.authDir、账号级覆盖
  • 运维:configWritesdebounceMsweb.enabledweb.heartbeatSecondsweb.reconnect.*web.whatsapp.*
  • 会话行为:session.dmScopehistoryLimitdmHistoryLimitdms.<id>.historyLimit
  • 提示词:groups.<id>.systemPromptgroups["*"].systemPromptdirect.<id>.systemPromptdirect["*"].systemPrompt

相关内容


📄 Zalo

原文:https://docs.openclaw.ai/zh-CN/channels/zalo

Status:实验性。支持私信。下面的能力章节反映当前 Marketplace 机器人行为。

内置插件

Zalo 在当前 OpenClaw 版本中作为内置插件发布,因此正常的打包构建不需要单独安装。

如果你使用的是旧版构建,或排除了 Zalo 的自定义安装,请直接安装 npm 包:

  • 通过 CLI 安装:openclaw plugins install @openclaw/zalo
  • 固定版本:openclaw plugins install @openclaw/zalo@2026.5.2
  • 或从源码检出安装:openclaw plugins install ./path/to/local/zalo-plugin
  • 详情:插件

快速设置(初学者)

  1. 确保 Zalo 插件可用。
    - 当前打包的 OpenClaw 版本已经内置它。
    - 旧版/自定义安装可以使用上面的命令手动添加它。
  2. 设置令牌:
    - 环境变量:ZALO_BOT_TOKEN=...
    - 或配置:channels.zalo.accounts.default.botToken: "..."
  3. 重启 Gateway 网关(或完成设置)。
  4. 私信访问默认使用配对;首次联系时批准配对码。

最小配置:

{
  channels: {
    zalo: {
      enabled: true,
      accounts: {
        default: {
          botToken: "12345689:abc-xyz",
          dmPolicy: "pairing",
        },
      },
    },
  },
}

它是什么

Zalo 是一款面向越南的消息应用;它的 Bot API 让 Gateway 网关可以运行用于一对一会话的机器人。
当你希望将支持或通知确定性路由回 Zalo 时,它很适合。

本页反映当前 OpenClaw 对 Zalo Bot Creator / Marketplace bots 的行为。
Zalo Official Account (OA) bots 是不同的 Zalo 产品界面,行为可能不同。

  • 由 Gateway 网关拥有的 Zalo Bot API 渠道。
  • 确定性路由:回复会返回 Zalo;模型永远不会选择渠道。
  • 私信共享智能体的主会话。
  • 下面的能力章节展示当前 Marketplace 机器人支持情况。

设置(快速路径)

1. 创建机器人令牌(Zalo Bot Platform)

  1. 前往 https://bot.zaloplatforms.com 并登录。
  2. 创建新机器人并配置其设置。
  3. 复制完整机器人令牌(通常是 numeric_id:secret)。对于 Marketplace 机器人,可用的运行时令牌可能会在创建后的机器人欢迎消息中出现。

2. 配置令牌(环境变量或配置)

示例:

{
  channels: {
    zalo: {
      enabled: true,
      accounts: {
        default: {
          botToken: "12345689:abc-xyz",
          dmPolicy: "pairing",
        },
      },
    },
  },
}

如果你之后迁移到支持群组的 Zalo 机器人界面,可以显式添加群组专用配置,例如 groupPolicygroupAllowFrom。对于当前 Marketplace 机器人行为,请参阅能力

环境变量选项:ZALO_BOT_TOKEN=...(仅适用于默认账号)。

多账号支持:使用 channels.zalo.accounts,并为每个账号配置令牌和可选的 name

  1. 重启 Gateway 网关。当解析到令牌(环境变量或配置)时,Zalo 会启动。
  2. 私信访问默认使用配对。机器人首次被联系时批准代码。

工作方式(行为)

  • 入站消息会被标准化为带有媒体占位符的共享渠道信封。
  • 回复始终路由回同一个 Zalo 聊天。
  • 默认使用长轮询;可通过 channels.zalo.webhookUrl 使用 webhook 模式。

限制

  • 出站文本会按 2000 个字符分块(Zalo API 限制)。
  • 媒体下载/上传受 channels.zalo.mediaMaxMb 限制(默认 5)。
  • 由于 2000 字符限制会降低流式传输的实用性,默认会阻止流式传输。

访问控制(私信)

私信访问

  • 默认:channels.zalo.dmPolicy = "pairing"。未知发送者会收到配对码;在批准前消息会被忽略(代码 1 小时后过期)。
  • 通过以下方式批准:
  • openclaw pairing list zalo
  • openclaw pairing approve zalo <CODE>
  • 配对是默认令牌交换。详情:配对
  • channels.zalo.allowFrom 接受数字用户 ID(无可用的用户名查找)。

访问控制(群组)

对于 Zalo Bot Creator / Marketplace bots,群组支持在实践中不可用,因为机器人根本无法被加入群组。

这意味着下面这些群组相关配置键存在于 schema 中,但无法用于 Marketplace 机器人:

  • channels.zalo.groupPolicy 控制群组入站处理:open | allowlist | disabled
  • channels.zalo.groupAllowFrom 限制哪些发送者 ID 可以在群组中触发机器人。
  • 如果未设置 groupAllowFrom,Zalo 会回退到 allowFrom 进行发送者检查。
  • 运行时注意事项:如果完全缺少 channels.zalo,运行时仍会为安全起见回退到 groupPolicy="allowlist"

群组策略值(当你的机器人界面支持群组访问时)如下:

  • groupPolicy: "disabled" — 阻止所有群组消息。
  • groupPolicy: "open" — 允许任何群组成员(受提及门控限制)。
  • groupPolicy: "allowlist" — 默认失败关闭;仅接受允许的发送者。

如果你使用的是不同的 Zalo 机器人产品界面,并且已经验证群组行为可用,请单独记录该行为,而不是假设它与 Marketplace 机器人流程一致。

长轮询与 webhook

  • 默认:长轮询(不需要公共 URL)。
  • Webhook 模式:设置 channels.zalo.webhookUrlchannels.zalo.webhookSecret
  • Webhook 密钥必须为 8-256 个字符。
  • Webhook URL 必须使用 HTTPS。
  • Zalo 发送事件时会带上 X-Bot-Api-Secret-Token 标头用于验证。
  • Gateway 网关 HTTP 在 channels.zalo.webhookPath 处理 webhook 请求(默认使用 webhook URL 路径)。
  • 请求必须使用 Content-Type: application/json(或 +json 媒体类型)。
  • 重复事件(event_name + message_id)会在较短的重放窗口内被忽略。
  • 突发流量会按路径/来源进行速率限制,并可能返回 HTTP 429。

注意: 根据 Zalo API 文档,getUpdates(轮询)和 webhook 每个 Zalo API 互斥。

支持的消息类型

如需快速支持情况快照,请参阅能力。以下注释会在行为需要额外上下文时补充细节。

  • 文本消息:完全支持,带 2000 字符分块。
  • 文本中的普通 URL:行为与普通文本输入相同。
  • 链接预览/富链接卡片:请参阅能力中的 Marketplace 机器人 Status;它们无法可靠触发回复。
  • 图片消息:请参阅能力中的 Marketplace 机器人 Status;入站图片处理不可靠(显示输入指示器但没有最终回复)。
  • 贴纸:请参阅能力中的 Marketplace 机器人 Status。
  • 语音备注/音频文件/视频/通用文件附件:请参阅能力中的 Marketplace 机器人 Status。
  • 不支持的类型:会被记录(例如,来自受保护用户的消息)。

能力

此表总结 OpenClaw 中当前 Zalo Bot Creator / Marketplace bot 行为。

功能 Status
直接消息 ✅ 支持
群组 ❌ Marketplace 机器人不可用
媒体(入站图片) ⚠️ 受限/请在你的环境中验证
媒体(出站图片) ⚠️ 未针对 Marketplace 机器人重新测试
文本中的普通 URL ✅ 支持
链接预览 ⚠️ Marketplace 机器人不可靠
反应 ❌ 不支持
贴纸 ⚠️ Marketplace 机器人没有智能体回复
语音备注/音频/视频 ⚠️ Marketplace 机器人没有智能体回复
文件附件 ⚠️ Marketplace 机器人没有智能体回复
话题线程 ❌ 不支持
投票 ❌ 不支持
原生命令 ❌ 不支持
流式传输 ⚠️ 已阻止(2000 字符限制)

交付目标(CLI/cron)

  • 使用聊天 ID 作为目标。
  • 示例:openclaw message send --channel zalo --target 123456789 --message "hi"

故障排除

机器人没有响应:

  • 检查令牌是否有效:openclaw channels status --probe
  • 验证发送者已获批准(配对或 allowFrom)
  • 检查 Gateway 网关日志:openclaw logs --follow

Webhook 未接收事件:

  • 确保 webhook URL 使用 HTTPS
  • 验证密钥令牌为 8-256 个字符
  • 确认 Gateway 网关 HTTP 端点可在配置的路径上访问
  • 检查 getUpdates 轮询没有运行(它们互斥)

配置参考(Zalo)

完整配置:配置

扁平顶层键(channels.zalo.botTokenchannels.zalo.dmPolicy 以及类似键)是旧版单账号简写。新配置优先使用 channels.zalo.accounts.<id>.*。这里仍记录两种形式,因为它们存在于 schema 中。

提供商选项:

  • channels.zalo.enabled:启用/禁用渠道启动。
  • channels.zalo.botToken:来自 Zalo Bot Platform 的机器人令牌。
  • channels.zalo.tokenFile:从常规文件路径读取令牌。符号链接会被拒绝。
  • channels.zalo.dmPolicypairing | allowlist | open | disabled(默认:pairing)。
  • channels.zalo.allowFrom:私信允许列表(用户 ID)。open 需要 "*"。向导会要求输入数字 ID。
  • channels.zalo.groupPolicyopen | allowlist | disabled(默认:allowlist)。存在于配置中;关于当前 Marketplace 机器人行为,请参阅能力访问控制(群组)
  • channels.zalo.groupAllowFrom:群组发送者允许列表(用户 ID)。未设置时回退到 allowFrom
  • channels.zalo.mediaMaxMb:入站/出站媒体上限(MB,默认 5)。
  • channels.zalo.webhookUrl:启用 webhook 模式(需要 HTTPS)。
  • channels.zalo.webhookSecret:webhook 密钥(8-256 个字符)。
  • channels.zalo.webhookPath:Gateway 网关 HTTP 服务器上的 webhook 路径。
  • channels.zalo.proxy:API 请求的代理 URL。

多账号选项:

  • channels.zalo.accounts.<id>.botToken:每账号令牌。
  • channels.zalo.accounts.<id>.tokenFile:每账号常规令牌文件。符号链接会被拒绝。
  • channels.zalo.accounts.<id>.name:显示名称。
  • channels.zalo.accounts.<id>.enabled:启用/禁用账号。
  • channels.zalo.accounts.<id>.dmPolicy:每账号私信策略。
  • channels.zalo.accounts.<id>.allowFrom:每账号允许列表。
  • channels.zalo.accounts.<id>.groupPolicy:每账号群组策略。存在于配置中;关于当前 Marketplace 机器人行为,请参阅能力访问控制(群组)
  • channels.zalo.accounts.<id>.groupAllowFrom:每账号群组发送者允许列表。
  • channels.zalo.accounts.<id>.webhookUrl:每账号 webhook URL。
  • channels.zalo.accounts.<id>.webhookSecret:每账号 webhook 密钥。
  • channels.zalo.accounts.<id>.webhookPath:每账号 webhook 路径。
  • channels.zalo.accounts.<id>.proxy:每账号代理 URL。

相关


📄 Zalo 个人版

原文:https://docs.openclaw.ai/zh-CN/channels/zalouser

Status: 实验性。此集成通过 OpenClaw 内部原生 zca-js 自动化一个个人 Zalo 账号


这是一个非官方集成,可能导致账号被暂停或封禁。使用风险由你自行承担。

内置插件

Zalo Personal 在当前 OpenClaw 版本中作为内置插件提供,因此普通
打包构建不需要单独安装。

如果你使用的是较旧构建,或是不包含 Zalo Personal 的自定义安装,
请直接安装 npm 包:

  • 通过 CLI 安装:openclaw plugins install @openclaw/zalouser
  • 固定版本:openclaw plugins install @openclaw/zalouser@2026.5.2
  • 或从源码检出安装:openclaw plugins install ./path/to/local/zalouser-plugin
  • 详情:插件

不需要外部 zca/openzca CLI 二进制文件。

快速设置(初学者)

  1. 确保 Zalo Personal 插件可用。
    - 当前打包的 OpenClaw 版本已内置它。
    - 较旧/自定义安装可以使用上面的命令手动添加。
  2. 登录(QR,在 Gateway 网关机器上):
    - openclaw channels login --channel zalouser
    - 使用 Zalo 移动应用扫描二维码。
  3. 启用渠道:
{
  channels: {
    zalouser: {
      enabled: true,
      dmPolicy: "pairing",
    },
  },
}
  1. 重启 Gateway 网关(或完成设置)。
  2. 私信访问默认使用配对;首次联系时批准配对码。

它是什么

  • 完全通过 zca-js 在进程内运行。
  • 使用原生事件监听器接收入站消息。
  • 直接通过 JS API(文本/媒体/链接)发送回复。
  • 面向无法使用 Zalo Bot API 的“个人账号”使用场景设计。

命名

渠道 ID 是 zalouser,用于明确表示此集成自动化一个个人 Zalo 用户账号(非官方)。我们保留 zalo,供未来可能的官方 Zalo API 集成使用。

查找 ID(目录)

使用目录 CLI 发现对端/群组及其 ID:

openclaw directory self --channel zalouser
openclaw directory peers list --channel zalouser --query "name"
openclaw directory groups list --channel zalouser --query "work"

限制

  • 出站文本会被分块为约 2000 个字符(Zalo 客户端限制)。
  • 默认阻止流式传输。

访问控制(私信)

channels.zalouser.dmPolicy 支持:pairing | allowlist | open | disabled(默认:pairing)。

channels.zalouser.allowFrom 应使用稳定的 Zalo 用户 ID。它也可以引用静态发送者访问组(accessGroup:<name>)。在交互式设置期间,输入的名称可以通过插件的进程内联系人查找解析为 ID。

如果原始名称保留在配置中,启动时只有在启用 channels.zalouser.dangerouslyAllowNameMatching: true 时才会解析它。没有该显式启用项时,运行时发送者检查仅基于 ID,原始名称会被忽略用于授权。

通过以下命令批准:

  • openclaw pairing list zalouser
  • openclaw pairing approve zalouser <code>

群组访问(可选)

  • 默认:channels.zalouser.groupPolicy = "open"(允许群组)。未设置时,使用 channels.defaults.groupPolicy 覆盖默认值。
  • 使用以下配置限制为允许列表:
  • channels.zalouser.groupPolicy = "allowlist"
  • channels.zalouser.groups(键应为稳定的群组 ID;只有在启用 channels.zalouser.dangerouslyAllowNameMatching: true 时,名称才会在启动时解析为 ID)
  • channels.zalouser.groupAllowFrom(控制允许的群组中哪些发送者可以触发机器人;可以用 accessGroup:<name> 引用静态发送者访问组)
  • 阻止所有群组:channels.zalouser.groupPolicy = "disabled"
  • 配置向导可以提示输入群组允许列表。
  • 启动时,只有在启用 channels.zalouser.dangerouslyAllowNameMatching: true 时,OpenClaw 才会将允许列表中的群组/用户名称解析为 ID 并记录映射。
  • 群组允许列表匹配默认仅基于 ID。除非启用 channels.zalouser.dangerouslyAllowNameMatching: true,否则未解析的名称会被忽略用于认证。
  • channels.zalouser.dangerouslyAllowNameMatching: true 是一个应急兼容模式,会重新启用可变的启动时名称解析和运行时群组名称匹配。
  • 如果未设置 groupAllowFrom,运行时会回退到 allowFrom 来执行群组发送者检查。
  • 发送者检查同时适用于普通群组消息和控制命令(例如 /new/reset)。

示例:

{
  channels: {
    zalouser: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["1471383327500481391"],
      groups: {
        "123456789": { allow: true },
        "Work Chat": { allow: true },
      },
    },
  },
}

群组提及门控

  • channels.zalouser.groups.<group>.requireMention 控制群组回复是否需要提及。
  • 解析顺序:精确群组 ID/名称 -> 规范化群组 slug -> * -> 默认值(true)。
  • 这同时适用于允许列表群组和开放群组模式。
  • 引用机器人消息会计为群组激活的隐式提及。
  • 已授权的控制命令(例如 /new)可以绕过提及门控。
  • 当群组消息因需要提及而被跳过时,OpenClaw 会将其存储为待处理群组历史,并在下一条已处理的群组消息中包含它。
  • 群组历史限制默认为 messages.groupChat.historyLimit(回退值 50)。你可以使用 channels.zalouser.historyLimit 为每个账号覆盖。

示例:

{
  channels: {
    zalouser: {
      groupPolicy: "allowlist",
      groups: {
        "*": { allow: true, requireMention: true },
        "Work Chat": { allow: true, requireMention: false },
      },
    },
  },
}

多账号

账号会映射到 OpenClaw 状态中的 zalouser 配置文件。示例:

{
  channels: {
    zalouser: {
      enabled: true,
      defaultAccount: "default",
      accounts: {
        work: { enabled: true, profile: "work" },
      },
    },
  },
}

正在输入、回应和送达确认

  • OpenClaw 会在分发回复前发送正在输入事件(尽力而为)。
  • 渠道操作中,zalouser 支持消息回应操作 react
  • 使用 remove: true 从消息中移除特定回应表情。
  • 回应语义:回应
  • 对于包含事件元数据的入站消息,OpenClaw 会发送已送达 + 已读确认(尽力而为)。

故障排除

登录无法保留:

  • openclaw channels status --probe
  • 重新登录:openclaw channels logout --channel zalouser && openclaw channels login --channel zalouser

允许列表/群组名称未解析:

  • allowFrom/groupAllowFrom 中使用数字 ID,并在 groups 中使用稳定的群组 ID。如果你确实需要精确的好友/群组名称,请启用 channels.zalouser.dangerouslyAllowNameMatching: true

从旧的基于 CLI 的设置升级:

  • 移除任何旧的外部 zca 进程假设。
  • 该渠道现在完全在 OpenClaw 中运行,不需要外部 CLI 二进制文件。

相关

上一篇 [OpenClaw 文档]消息渠道--概览
下一篇 【转载】HCIE R&S 备考笔记 MPLS virtual private network PE-CE之间的路由协议(静态、RIP、IS-IS)