Skip to main content
状态:可下载插件(bot token + WebSocket 事件)。支持频道、私密频道、群组 DM 和 DM。Mattermost 是一个可自托管的团队消息平台(mattermost.com)。

安装

详细信息: 插件

快速设置

1

确保插件可用

使用上面的命令安装 @openclaw/mattermost,然后如果 Gateway 已在运行,请重启它。
2

创建 Mattermost bot

创建一个 Mattermost bot 账号,复制 bot token,并将 bot 添加到它应该读取的团队和频道中。
3

复制 base URL

复制 Mattermost 的 base URL(例如 https://chat.example.com)。末尾的 /api/v4 会自动去除。
4

配置 OpenClaw 并启动网关

最小配置:
非交互式替代方案:
自托管的 Mattermost 使用私有/LAN/tailnet 地址时:对外发出的 Mattermost API 请求会经过 SSRF 防护,默认会阻止私有和内部 IP。可通过 channels.mattermost.network.dangerouslyAllowPrivateNetwork: true 显式启用(按账号则为:channels.mattermost.accounts.<id>.network.dangerouslyAllowPrivateNetwork)。

原生 slash 命令

原生 slash 命令默认不启用。启用后,OpenClaw 会在 bot 所属的每个团队上注册 oc_* slash 命令,并在网关 HTTP 服务器上接收回调 POST 请求。
已注册的命令:/oc_status/oc_model/oc_models/oc_new/oc_help/oc_think/oc_reasoning/oc_verbose/oc_queue。启用 nativeSkills: true 后,技能命令也会注册为 /oc_<skill>
  • nativenativeSkills 的默认值为 "auto",对 Mattermost 会解析为禁用。请显式将其设为 true
  • callbackPath 默认值为 /api/channels/mattermost/command
  • 如果省略 callbackUrl,OpenClaw 会推导为 http://<gateway.customBindHost or localhost>:<gateway.port, default 18789><callbackPath>。通配绑定主机(0.0.0.0::)会回退到 localhost
  • 对于多账号配置,commands 可以设置在顶层,或设置在 channels.mattermost.accounts.<id>.commands 下(账号级配置会覆盖顶层字段)。
  • 由其他集成创建、且触发词相同的现有 slash 命令不会被修改(注册时会跳过它们);bot 创建的命令在回调 URL 发生变化时会更新或重新创建。
  • 命令回调会使用 Mattermost 在 OpenClaw 注册 oc_* 命令时返回的每个命令令牌进行校验。
  • OpenClaw 会在接受每次回调前刷新当前的 Mattermost 命令注册信息,因此来自已删除或已重新生成的 slash 命令的过期令牌将不再被接受,无需重启网关。
  • 如果 Mattermost API 无法确认该命令仍然是当前命令,回调校验将失败并关闭;失败结果会短暂缓存,并发查询会合并,每个命令的新鲜查询会限流,以限制重放压力。
  • 当注册失败、启动未完整完成,或回调令牌与解析出的命令已注册令牌不匹配时,slash 回调会失败并关闭(对一个命令有效的令牌无法通过上游校验用于另一个命令)。
  • 被接受的回调会以一个临时的“正在处理……”回复确认;真正的答案会作为普通消息发送。
回调端点必须能够被 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

环境变量(默认账号)

如果你更喜欢使用环境变量,请在网关主机上设置这些值:
  • MATTERMOST_BOT_TOKEN=...
  • MATTERMOST_URL=https://chat.example.com
环境变量只适用于默认账号(default)。其他账号必须使用配置值。MATTERMOST_URL 不能从工作区 .env 中设置;请参见 工作区 .env 文件

聊天模式

Mattermost 会自动回复私信。频道行为由 chatmode 控制:
仅在频道中被 @ 提及时回复。
配置示例:
说明:
  • onchar 仍会响应显式的 @ 提及。
  • channels.mattermost.requireMention 仍会生效,但优先推荐使用 chatmode。按频道配置的 groups.<channelId>.requireMention 会优先于这两者。
  • 当机器人在频道线程中发送可见回复后,同一线程里的后续消息会在不需要新的 @ 提及或 onchar 前缀的情况下得到回复,因此多轮线程对话可以持续进行。参与状态会在机器人最后一次回复该线程后的 7 天内被记住,并且会在网关重启后继续保留。机器人仅观察过的线程不受影响;要再次要求显式提及,请发起一个新的顶层消息。
  • 设置 channels.mattermost.implicitMentions.threadParticipation: false 可阻止已参与线程的后续消息绕过提及门控。账户覆盖使用 channels.mattermost.accounts.<id>.implicitMentions。Mattermost 目前不会产生 replyToBotquotedBot facts,因此这些标志在这里没有作用。

线程和会话

使用 channels.mattermost.replyToMode 来控制频道和群组回复是保持在主频道中,还是在触发消息下方开启一个线程。
  • off (默认):仅当传入的帖子已经位于线程中时,才在线程中回复。
  • first:对于顶级频道/群组帖子,在该帖子下方发起一个线程,并将对话路由到线程作用域的会话中。
  • allbatched:在当前 Mattermost 中与 first 的行为相同,因为一旦 Mattermost 有了线程根消息,后续的分块和媒体就会继续留在同一个线程中。
  • 即使设置了 replyToMode,直接消息默认仍为 off
使用 channels.mattermost.replyToModeByChatType 来覆盖 directgroupchannel 聊天的模式。将 direct 设置为可让直接消息使用线程:
  • off (默认):直接消息保持为非线程式,处于单个滚动会话中。
  • firstallbatched:每条顶级直接消息都会启动一个 Mattermost 线程,并由一个全新、独立的会话来支持。
注意:
  • 线程作用域的会话使用触发该消息的帖子 id 作为线程根。
  • firstall 目前等价,因为一旦 Mattermost 有了线程根,后续的分块和媒体就会继续留在同一个线程中。
  • 按聊天类型的覆盖设置优先于 replyToMode。如果没有 direct 覆盖,现有部署将继续保持扁平、非线程式的 DM。

访问控制(私信)

  • 默认:channels.mattermost.dmPolicy = "pairing"(未知发送者会获得一个配对码)。其他值:allowlistopendisabled
  • 通过以下方式批准:
    • openclaw pairing list mattermost
    • openclaw pairing approve mattermost <CODE>
  • 公开私信:channels.mattermost.dmPolicy="open" 加上 channels.mattermost.allowFrom=["*"](配置 schema 会强制使用通配符)。
  • channels.mattermost.allowFrom 接受用户 ID(推荐)和 accessGroup:<name> 条目。参见 访问组

频道(群组)

  • 默认:channels.mattermost.groupPolicy = "allowlist"(需要提及才可发送)。
  • 使用 channels.mattermost.groupAllowFrom 允许列表中的发送者(推荐使用用户 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,再 channels.defaults.groupPolicy,最后 "allowlist"
  • 运行时说明:如果 channels.mattermost 部分完全缺失,运行时会对群组检查安全降级为 groupPolicy="allowlist"(即使设置了 channels.defaults.groupPolicy 也是如此),并记录一次性警告。
示例:

外发投递目标

openclaw message send 或 cron/webhook 中使用以下目标格式: 外发发送每条消息最多支持一个附件;请将多个文件拆分为多次发送。
裸露的歧义 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 为单个账号进行调整。默认值:
说明:
  • 这只适用于 DM 频道创建(/api/v4/channels/direct),不适用于每一次 Mattermost API 调用。
  • 重试使用带抖动的指数退避,并适用于限流、5xx 响应以及网络或超时错误等瞬时故障。
  • 429 之外的 4xx 客户端错误都被视为永久性错误,不会重试。

预览流式输出

Mattermost 会将思考过程、工具活动和部分回复文本流式写入一个草稿预览帖子,当最终答案可安全发送时,会在原地完成。 在 partial 模式下,预览会在同一个帖子 ID 上更新,而不是通过每个分块消息刷屏频道。 在 block 模式下,预览会在已完成文本和工具活动块之间轮换,因此较早的块会作为各自的帖子保持可见,而不会被下一个块覆盖。 媒体/错误类最终结果会取消待处理的预览编辑,并使用正常投递,而不是刷新一个一次性的预览帖子。 预览流式输出默认在 partial 模式下开启。 可通过 channels.mattermost.streaming.mode 配置(旧的标量/布尔 streaming 值会由 openclaw doctor --fix 迁移):
  • partial(默认):一个预览帖子会随着回复增长而被编辑,最后以完整答案完成。
  • block 会在已完成文本和工具活动块之间轮换预览,因此每个块都会作为自己的帖子保持可见,而不是就地被覆盖。并行和连续的工具更新会共享当前的工具活动帖子。
  • progress 在生成过程中显示状态预览,并且只在完成时发布最终答案。
  • off 禁用预览流式输出。使用 streaming.block.enabled: true 时,已完成的助手块仍会作为正常的块回复(独立帖子)发送,而不是合并成单个最终帖子。
  • 如果流无法就地完成(例如帖子在流式过程中被删除),OpenClaw 会回退并发送一个新的最终帖子,以确保回复不会丢失。
  • 仅思考内容的负载会被从频道帖子中抑制,包括作为 > Thinking 块引用到达的文本。设置 /reasoning on 可在其他界面中查看思考内容;Mattermost 最终帖子只保留答案。
  • 请参见 流式输出 了解通道映射矩阵。

读取频道历史记录(消息工具)

使用 message action=read 或 CLI 读取已配置的 Mattermost 机器人可以访问的频道中的帖子:
  • 结果遵循 Mattermost 的有序帖子列表,并包含规范化的 timestampMstimestampUtc 字段。
  • limit 默认为 60,且上限为 Mattermost 的最大值 200。使用 before=<postId>after=<postId> 进行分页;不能同时使用这两个游标。
  • 直接操作员调用依赖 Mattermost 的频道成员资格和 read_channel 权限。提供商返回的 403 会作为正常的、可见的工具错误。
  • 允许委托读取当前 Mattermost 对话。跨频道委托读取还需要在 channels.mattermost.groups 下配置目标频道 ID、配置 "*" 频道条目,或将 groupPolicy 设置为 "open"。跨账户和跨频道的私信读取会安全失败。
  • 默认情况下禁用历史记录读取。将 channels.mattermost.actions.messages 设置为 true 以启用。可通过 channels.mattermost.accounts.<id>.actions.messages 针对每个账户覆盖该设置。

反应(消息工具)

  • 使用 message action=react 并设置 channel=mattermost
  • messageId 是 Mattermost 帖子 ID。
  • emoji 接受诸如 thumbsup:+1: 这样的名称(冒号可选)。
  • 设置 remove=true(布尔值)可移除反应。
  • 反应的添加/移除事件会作为系统事件转发到路由后的 agent 会话,并且受与消息相同的 DM/群组策略检查约束。
示例:
配置:
  • channels.mattermost.actions.reactions:启用/禁用反应动作(默认 true)。
  • 按账号覆盖:channels.mattermost.accounts.<id>.actions.reactions

交互式按钮(message 工具)

发送带有可点击按钮的消息。当用户点击按钮时,agent 会收到所选内容并可以响应。 按钮来自语义 presentation 负载(在普通 agent 回复和 message action=send 中)。OpenClaw 将值按钮渲染为 Mattermost 交互式按钮,将 URL 按钮保留在消息文本中,并将选择菜单降级为可读文本。
按钮字段:
string
required
显示标签(别名:text)。
string
点击后返回的值,用作操作 ID(别名:callback_datacallbackData)。除非设置了 url,否则可点击按钮必须提供该字段。
string
链接按钮;在消息正文中渲染为 label: url 文本,而不是交互式按钮。
"primary" | "secondary" | "success" | "danger"
按钮样式。Mattermost 会对其不支持的值应用默认样式。
要在 agent 系统提示中声明按钮支持,请将 inlineButtons 添加到 channel capabilities:
当用户点击按钮时:
1

访问检查

点击者必须通过与消息发送者相同的 DM/群组策略检查;未经授权的点击会收到临时通知并被忽略。
2

按钮替换为确认信息

所有按钮都会被一条确认行替换(例如,“✓ 已由 @user 选择”)。
3

Agent 接收所选内容

agent 会将所选内容作为入站消息(外加一个系统事件)接收并作出响应。
  • 按钮回调使用 HMAC-SHA256 验证(自动完成,无需配置)。
  • 点击时会替换整个附件块,因此所有按钮会一起被移除 - 不支持部分移除。
  • 含有连字符或下划线的操作 ID 会被自动清理(Mattermost 路由限制)。
  • 与原始帖子上的任何操作都不匹配的 action_id 点击会被以 403(“未知操作”)拒绝。
  • channels.mattermost.capabilities:能力字符串数组。添加 "inlineButtons" 以在 agent 系统提示中启用按钮工具描述。
  • channels.mattermost.interactions.callbackBaseUrl:按钮回调的可选外部基础 URL(例如 https://gateway.example.com)。当 Mattermost 无法直接到达其绑定主机上的 gateway 时使用此项。
  • 在多账号设置中,你也可以在 channels.mattermost.accounts.<id>.interactions.callbackBaseUrl 下设置相同字段。
  • 如果省略 interactions.callbackBaseUrl,OpenClaw 会根据 gateway.customBindHost + gateway.port(默认 18789)推导回调 URL,然后回退到 http://localhost:<port>。回调路径为 /mattermost/interactions/<accountId>
  • 可达性规则:按钮回调 URL 必须能从 Mattermost 服务器访问到。localhost 仅在 Mattermost 和 OpenClaw 运行在同一主机/网络命名空间时有效。
  • channels.mattermost.interactions.allowedSourceIps:按钮回调的来源 IP 白名单。若未设置,则仅接受回环地址来源(127.0.0.1::1),因此远程 Mattermost 服务器必须被加入白名单,否则其点击会被以 403 拒绝。若位于反向代理之后,还需设置 gateway.trustedProxies,以便从转发头中推导真实客户端 IP。
  • 如果你的回调目标是私有网络/tailnet/内部地址,请将其 host/domain 添加到 Mattermost 的 ServiceSettings.AllowedUntrustedInternalConnections

直接 API 集成(外部脚本)

外部脚本和 webhook 可以直接通过 Mattermost REST API 发布按钮,而无需经过 agent 的 message 工具。优先使用 OpenClaw 的 message 工具。对于直接集成,请从 @openclaw/mattermost/api.js 导入 buildButtonAttachments;如果直接发送原始 JSON,请遵循以下规则: 负载结构:
关键规则
  1. Attachments 放在 props.attachments 中,而不是顶层 attachments(会被静默忽略)。
  2. 每个 action 都需要 type: "button" - 否则点击会被静默吞掉。
  3. 每个 action 都需要 id 字段 - Mattermost 会忽略没有 ID 的 action。
  4. Action id 必须仅包含字母数字[a-zA-Z0-9])。连字符和下划线会破坏 Mattermost 的服务端 action 路由(返回 404)。在使用前请去掉它们。
  5. context.action_id 必须与按钮的 id 匹配;gateway 会拒绝 action_id 在帖子中不存在的点击。
  6. context.action_id 是必需的 - 交互处理器在没有它时会返回 400。
  7. 必须允许回调来源 IP(见上方 interactions.allowedSourceIps)。
HMAC 令牌生成 gateway 使用 HMAC-SHA256 验证按钮点击。外部脚本必须生成与 gateway 验证逻辑匹配的令牌:
1

从 bot token 派生 secret

HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken),十六进制编码。
2

构建上下文对象

构建包含除 _token 之外所有字段的上下文对象。
3

按排序键序列化

使用递归排序的键并且不带空格进行序列化(gateway 也会对嵌套对象做规范化,并生成紧凑 JSON)。
4

签名负载

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

添加令牌

将得到的十六进制摘要作为 _token 添加到上下文中。
Python 示例:
  • Python 的 json.dumps 默认会添加空格({"key": "val"})。使用 separators=(",", ":") 以匹配 JavaScript 的紧凑输出({"key":"val"})。
  • 始终对所有上下文字段(减去 _token)签名。gateway 会删除 _token 后对剩余所有内容签名。只签名子集会导致静默验证失败。
  • 使用 sort_keys=True - gateway 在签名前会对键排序,而 Mattermost 在存储负载时可能会重新排序上下文字段。
  • 从 bot token 派生密钥(确定性),不要使用随机字节。创建按钮的进程与验证按钮的 gateway 必须使用相同的密钥。

目录适配器

Mattermost 插件包含一个目录适配器,可通过 Mattermost API 解析频道和用户名称。这使得 openclaw message send 以及 cron/webhook 投递可以使用 #channel-name@username 目标。 无需配置 - 该适配器使用账号配置中的 bot token。

多账号

Mattermost 支持在 channels.mattermost.accounts 下配置多个账号:
账号值会覆盖顶层字段;channels.mattermost.defaultAccount 用于在未指定账号时选择所使用的账号。

故障排查

确保 bot 在该频道中,并对其进行 mention(oncall),使用触发前缀(onchar),或设置 chatmode: "onmessage"
  • 检查 bot token、base URL,以及该账户是否已启用。
  • 多账户问题:环境变量只适用于 default 账户。
  • 私有/LAN Mattermost 主机需要 network.dangerouslyAllowPrivateNetwork: true(SSRF 防护默认会阻止私有 IP)。
  • Unauthorized: invalid command token.:OpenClaw 未接受回调 token。典型原因:
    • slash command 注册失败,或仅在启动时部分完成
    • 回调指向了错误的 gateway/account
    • Mattermost 仍然有指向先前回调目标的旧命令
    • gateway 重启后没有重新激活 slash commands
  • 如果原生斜杠命令停止工作,检查日志中是否有 mattermost: failed to register slash commandsmattermost: native slash commands enabled but no commands could be registered
  • 如果省略了 callbackUrl,且日志警告回调解析为类似 http://localhost:18789/... 的回环 URL,那么该 URL 很可能只有在 Mattermost 与 OpenClaw 运行在同一主机/网络命名空间时才可访问。请改为设置一个明确的、可从外部访问的 commands.callbackUrl
  • 按钮显示为白色方框或根本不显示:按钮数据格式错误。每个展示按钮都需要 labelvalue(缺少任一项的按钮会被丢弃)。
  • 按钮能渲染但点击无反应:确认 Mattermost 服务器可以访问 gateway,Mattermost 服务器 IP 已包含在 channels.mattermost.interactions.allowedSourceIps 中(否则只接受 loopback),并且 ServiceSettings.AllowedUntrustedInternalConnections 包含私有目标的回调主机。
  • 按钮点击返回 404:按钮 id 很可能包含连字符或下划线。Mattermost 的 action router 在非字母数字 ID 上会出问题。请仅使用 [a-zA-Z0-9]
  • gateway 日志 rejected callback source:点击来自 interactions.allowedSourceIps 之外的 IP。请将 Mattermost 服务器或你的入口网关加入允许列表,并在反向代理后设置 gateway.trustedProxies
  • gateway 日志 invalid _token:HMAC 不匹配。检查你是否对所有上下文字段进行签名(而不是子集)、使用排序后的键,以及紧凑 JSON(无空格)。参见上面的 HMAC 章节。
  • gateway 日志 missing _token in context:按钮上下文中没有 _token 字段。构建集成负载时请确保包含它。
  • gateway 拒绝点击并报 Unknown actioncontext.action_id 与帖子上的任何动作 id 都不匹配。请将二者设置为相同的已清理值。
  • agent 不提供按钮:在 Mattermost 频道配置中添加 capabilities: ["inlineButtons"]

相关内容