消息平台

Mattermost

Status: 可下载插件(机器人令牌 + WebSocket 事件)。支持渠道、群组和私信。Mattermost 是可自托管的团队消息平台;产品详情和下载请参阅官方站点 mattermost.com

安装

配置渠道前先安装 Mattermost:

npm 注册表

openclaw plugins install @openclaw/mattermost

本地检出

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

详情:插件

快速设置

  • 确保插件可用

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

  • 创建 Mattermost 机器人

    创建一个 Mattermost 机器人账号,并复制机器人令牌

  • 复制基础 URL

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

  • 配置 OpenClaw 并启动 Gateway 网关

    最小配置:

    {
      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 网关主机/端口 + callbackPath 推导一个值。
    • 对多账号设置,可以在顶层设置 commands,也可以在 channels.mattermost.accounts.<id>.commands 下设置(账号值会覆盖顶层字段)。
    • 命令回调会使用 OpenClaw 注册 oc_* 命令时 Mattermost 返回的每个命令令牌进行校验。
    • OpenClaw 会在接受每个回调前刷新当前 Mattermost 命令注册,因此已删除或重新生成的斜杠命令所产生的陈旧令牌会在不重启 Gateway 网关的情况下停止被接受。
    • 如果 Mattermost API 无法确认该命令仍是当前命令,回调校验会按失败关闭处理;失败的校验会被短暂缓存,并发查询会被合并,新的查询启动会按命令限速,以限制重放压力。
    • 当注册失败、启动不完整,或回调令牌与已解析命令的已注册令牌不匹配时,斜杠回调会按失败关闭处理(对某个命令有效的令牌不能触发另一个命令的上游校验)。
    可达性要求

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

    • 不要把 callbackUrl 设置为 localhost,除非 Mattermost 与 OpenClaw 运行在同一主机/网络命名空间中。
    • 不要把 callbackUrl 设置为你的 Mattermost 基础 URL,除非该 URL 会将 /api/channels/mattermost/command 反向代理到 OpenClaw。
    • 一个快速检查是 curl https://<gateway-host>/api/channels/mattermost/command;GET 应返回 OpenClaw 的 405 Method Not Allowed,而不是 404
    Mattermost 出站允许列表

    如果你的回调指向私有、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

    聊天模式

    Mattermost 会自动回复私信。渠道行为由 chatmode 控制:

    oncall(默认)

    仅在渠道中被 @ 提及时回复。

    onmessage

    回复每条渠道消息。

    onchar

    当消息以触发前缀开头时回复。

    配置示例:

    {
      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 &lt;CODE&gt;
    • 公开私信:channels.mattermost.dmPolicy="open"channels.mattermost.allowFrom=["*"]

    渠道(群组)

    • 默认:channels.mattermost.groupPolicy = "allowlist"(受提及限制)。
    • 使用 channels.mattermost.groupAllowFrom 允许发送者(建议使用用户 ID)。
    • 每个渠道的提及覆盖位于 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/webhook:

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

    私信渠道重试

    当 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: 引用块形式到达的文本。设置 /reasoning on 可在其他界面看到思考;Mattermost 最终帖文只保留答案。
    • 请参阅流式传输了解渠道映射矩阵。

    表情回应(消息工具)

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

    示例:

    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

    交互式按钮(消息工具)

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

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

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

    使用带有 buttons 参数的 message action=send。按钮是二维数组(按钮行):

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

    按钮字段:

    textstringrequired

    显示标签。

    callback_datastringrequired

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

    style"default" | "primary" | "danger"

    按钮样式。

    当用户点击按钮时:

  • 按钮替换为确认信息

    所有按钮都会替换为一行确认信息(例如,“✓ 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
                  },
                },
              },
            ],
          },
        ],
      },
    }
    

    HMAC 令牌生成

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

  • 从 bot 令牌派生密钥

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

  • 构建上下文对象

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

  • 使用排序后的键序列化

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

  • 签名载荷

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

  • 添加令牌

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

  • Python 示例:

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

    目录适配器

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

    无需配置:适配器使用账号配置中的 bot 令牌。

    多账号

    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" },
          },
        },
      },
    }
    

    故障排除

    渠道中没有回复

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

    认证或多账号错误
    • 检查 bot 令牌、基础 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 中的 EnablePostActionIntegrationtrue
    • 点击按钮返回 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 渠道配置中。

    相关内容