Mattermost (плагин)

Статус: поддерживается через плагин (токен бота + события WebSocket). Каналы, группы и ЛС поддерживаются. Mattermost — самохостируемая платформа командного обмена сообщениями; подробнее на mattermost.com.

Требуется плагин

Mattermost поставляется как плагин и не входит в базовую установку.

Установка через CLI (npm-реестр):

openclaw plugins install @openclaw/mattermost

Из локальной копии (при работе из git-репозитория):

openclaw plugins install ./extensions/mattermost

Если вы выбираете Mattermost при настройке/онбординге и обнаружена git-копия, OpenClaw автоматически предложит локальную установку.

Подробнее: Плагины

Быстрая настройка

  1. Установите плагин Mattermost.
  2. Создайте аккаунт бота Mattermost и скопируйте токен бота.
  3. Скопируйте базовый URL Mattermost (например, https://chat.example.com).
  4. Настройте OpenClaw и запустите шлюз.

Минимальная конфигурация:

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

Нативные slash-команды

Нативные slash-команды включаются по желанию. При включении OpenClaw регистрирует slash-команды oc_* через Mattermost API и получает callback POST на HTTP-сервер шлюза.

{
  channels: {
    mattermost: {
      commands: {
        native: true,
        nativeSkills: true,
        callbackPath: "/api/channels/mattermost/command",
        // Используйте, когда Mattermost не может напрямую связаться со шлюзом (reverse proxy/публичный URL).
        callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
      },
    },
  },
}

Примечания:

  • native: "auto" по умолчанию отключён для Mattermost. Задайте native: true для включения.
  • Если callbackUrl не задан, OpenClaw определяет его из host/port шлюза + callbackPath.
  • Для мульти-аккаунтных конфигураций commands можно задать на верхнем уровне или под channels.mattermost.accounts.<id>.commands (значения аккаунта переопределяют верхнеуровневые).
  • Callback’и команд проверяются по токенам для каждой команды и отклоняются при несовпадении.
  • Требование доступности: эндпоинт callback’а должен быть доступен с сервера Mattermost.
    • Не задавайте callbackUrl как localhost, если Mattermost не работает на том же хосте/сетевом пространстве имён, что и OpenClaw.
    • Не задавайте callbackUrl как URL Mattermost, если этот URL не проксирует /api/channels/mattermost/command на OpenClaw.
    • Быстрая проверка: curl https://<gateway-host>/api/channels/mattermost/command; GET должен вернуть 405 Method Not Allowed от OpenClaw, а не 404.
  • Требование белого списка исходящих соединений Mattermost:
    • Если callback указывает на приватные/tailnet/внутренние адреса, задайте ServiceSettings.AllowedUntrustedInternalConnections в Mattermost, включив хост/домен callback’а.
    • Используйте записи хост/домен, не полные URL.
      • Правильно: gateway.tailnet-name.ts.net
      • Неправильно: https://gateway.tailnet-name.ts.net

Переменные окружения (аккаунт по умолчанию)

Задайте на хосте шлюза, если предпочитаете env:

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

Env применяются только к аккаунту по умолчанию (default). Другие аккаунты должны использовать конфигурацию.

Режимы чата

Mattermost отвечает на ЛС автоматически. Поведение в каналах управляется через chatmode:

  • oncall (по умолчанию): отвечать только при @упоминании в каналах.
  • onmessage: отвечать на каждое сообщение в канале.
  • onchar: отвечать, когда сообщение начинается с триггерного префикса.

Пример конфигурации:

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

Примечания:

  • onchar всё равно отвечает на явные @упоминания.
  • channels.mattermost.requireMention учитывается для устаревших конфигураций, но chatmode предпочтительнее.

Потоки и сессии

Используйте channels.mattermost.replyToMode для управления тем, остаются ли ответы в каналах и группах в основном канале или создают поток под триггерным постом.

  • off (по умолчанию): отвечать в потоке, только если входящий пост уже в нём.
  • first: для верхнеуровневых постов канала/группы начать поток под этим постом и направить беседу в сессию с областью видимости потока.
  • all: аналогично first для Mattermost на данный момент.
  • Прямые сообщения игнорируют эту настройку и остаются без потоков.

Пример конфигурации:

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

Примечания:

  • Сессии с областью видимости потока используют ID триггерного поста как корень потока.
  • first и all в настоящее время эквивалентны, поскольку после создания корня потока Mattermost последующие чанки и медиа продолжают в том же потоке.

Контроль доступа (ЛС)

  • По умолчанию: channels.mattermost.dmPolicy = "pairing" (неизвестные отправители получают код спаривания).
  • Подтверждение:
    • openclaw pairing list mattermost
    • openclaw pairing approve mattermost <CODE>
  • Публичные ЛС: channels.mattermost.dmPolicy="open" плюс channels.mattermost.allowFrom=["*"].

Каналы (группы)

  • По умолчанию: channels.mattermost.groupPolicy = "allowlist" (с гейтингом по упоминанию).
  • Список доступа отправителей через channels.mattermost.groupAllowFrom (рекомендуются user ID).
  • Сопоставление @username изменяемо и включено только при channels.mattermost.dangerouslyAllowNameMatching: true.
  • Открытые каналы: channels.mattermost.groupPolicy="open" (с гейтингом по упоминанию).
  • Примечание: если channels.mattermost полностью отсутствует, runtime использует fallback к groupPolicy="allowlist" для групповых проверок (даже если задан channels.defaults.groupPolicy).

Цели исходящей доставки

Используйте эти форматы целей с openclaw message send или cron/вебхуками:

  • channel:<id> для канала
  • user:<id> для ЛС
  • @username для ЛС (разрешается через Mattermost API)

Голые непрозрачные ID (вроде 64ifufp...) неоднозначны в Mattermost (user ID vs channel ID).

OpenClaw разрешает их сначала как пользователя:

  • Если ID существует как пользователь (GET /api/v4/users/<id> успешен), OpenClaw отправляет ЛС, разрешая прямой канал через /api/v4/channels/direct.
  • Иначе ID считается ID канала.

Для детерминированного поведения всегда используйте явные префиксы (user:<id> / channel:<id>).

Реакции (инструмент message)

  • Используйте message action=react с channel=mattermost.
  • messageId — ID поста Mattermost.
  • emoji принимает имена вроде thumbsup или :+1: (двоеточия необязательны).
  • Задайте remove=true (boolean) для удаления реакции.
  • События добавления/удаления реакций пересылаются как системные события в маршрутизированную сессию агента.

Примеры:

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.

Интерактивные кнопки (инструмент message)

Отправка сообщений с кликабельными кнопками. Когда пользователь нажимает кнопку, агент получает выбор и может ответить.

Включите кнопки, добавив inlineButtons в возможности канала:

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

Используйте message action=send с параметром buttons. Кнопки — двумерный массив (ряды кнопок):

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

Поля кнопок:

  • text (обязательно): отображаемая метка.
  • callback_data (обязательно): значение, возвращаемое при клике (используется как ID действия).
  • style (необязательно): "default", "primary" или "danger".

При клике:

  1. Все кнопки заменяются строкой подтверждения (например, «Yes selected by @user»).
  2. Агент получает выбор как входящее сообщение и отвечает.

Примечания:

  • Callback’и кнопок используют HMAC-SHA256 верификацию (автоматически, без конфигурации).
  • Mattermost удаляет callback data из ответов API (функция безопасности), поэтому все кнопки удаляются при клике — частичное удаление невозможно.
  • ID действий, содержащие дефисы или подчёркивания, автоматически санитизируются (ограничение маршрутизации Mattermost).

Конфигурация:

  • channels.mattermost.capabilities: массив строк возможностей. Добавьте "inlineButtons" для включения описания инструмента кнопок в системном промпте агента.
  • channels.mattermost.interactions.callbackBaseUrl: необязательный внешний базовый URL для callback’ов кнопок (например https://gateway.example.com). Используйте, когда Mattermost не может напрямую связаться со шлюзом по его bind host.
  • В мульти-аккаунтных конфигурациях то же поле можно задать под channels.mattermost.accounts.<id>.interactions.callbackBaseUrl.
  • Если interactions.callbackBaseUrl не задан, OpenClaw определяет callback URL из gateway.customBindHost + gateway.port, затем fallback к http://localhost:<port>.
  • Правило доступности: callback URL кнопок должен быть доступен с сервера Mattermost. localhost работает, только когда Mattermost и OpenClaw на одном хосте/сетевом пространстве имён.
  • Если callback указывает на приватный/tailnet/внутренний адрес, добавьте его хост/домен в ServiceSettings.AllowedUntrustedInternalConnections Mattermost.

Прямая интеграция API (внешние скрипты)

Внешние скрипты и вебхуки могут отправлять кнопки напрямую через Mattermost REST API вместо инструмента message агента. Используйте buildButtonAttachments() из расширения при возможности; при отправке сырого JSON следуйте этим правилам:

Структура полезной нагрузки:

{
  channel_id: "<channelId>",
  message: "Choose an option:",
  props: {
    attachments: [
      {
        actions: [
          {
            id: "mybutton01", // только алфавитно-цифровые — см. ниже
            type: "button", // обязательно, иначе клики молча игнорируются
            name: "Approve", // отображаемая метка
            style: "primary", // необязательно: "default", "primary", "danger"
            integration: {
              url: "https://gateway.example.com/mattermost/interactions/default",
              context: {
                action_id: "mybutton01", // должен совпадать с id кнопки (для поиска имени)
                action: "approve",
                // ... любые пользовательские поля ...
                _token: "<hmac>", // см. раздел HMAC ниже
              },
            },
          },
        ],
      },
    ],
  },
}

Критические правила:

  1. Attachments размещаются в 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-токена:

Шлюз проверяет клики кнопок через HMAC-SHA256. Внешние скрипты должны генерировать токены, соответствующие логике верификации шлюза:

  1. Получите секрет из токена бота: HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)
  2. Постройте объект context со всеми полями кроме _token.
  3. Сериализуйте с отсортированными ключами и без пробелов (шлюз использует JSON.stringify с отсортированными ключами, что даёт компактный вывод).
  4. Подпишите: HMAC-SHA256(key=secret, data=serializedContext)
  5. Добавьте полученный hex-дайджест как _token в context.

Пример на 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}

Частые ошибки HMAC:

  • json.dumps Python добавляет пробелы по умолчанию ({"key": "val"}). Используйте separators=(",", ":") для соответствия компактному выводу JavaScript ({"key":"val"}).
  • Всегда подписывайте все поля context (минус _token). Шлюз удаляет _token, затем подписывает всё остальное. Подпись подмножества приводит к молчаливому провалу верификации.
  • Используйте sort_keys=True — шлюз сортирует ключи перед подписью, а Mattermost может переупорядочить поля context при хранении.
  • Получайте секрет из токена бота (детерминированно), а не случайных байтов. Секрет должен быть одинаковым в процессе, создающем кнопки, и шлюзе, верифицирующем их.

Адаптер каталога

Плагин Mattermost включает адаптер каталога, разрешающий имена каналов и пользователей через Mattermost API. Это позволяет использовать цели #channel-name и @username в openclaw message send и доставках cron/вебхуков.

Конфигурация не требуется — адаптер использует токен бота из конфигурации аккаунта.

Мульти-аккаунт

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 и включён ли аккаунт.
  • Проблемы мульти-аккаунта: env применяются только к аккаунту default.
  • Кнопки отображаются как белые блоки: агент может отправлять некорректные данные кнопок. Проверьте, что каждая кнопка имеет поля text и callback_data.
  • Кнопки рендерятся, но клики ничего не делают: проверьте AllowedUntrustedInternalConnections в конфигурации сервера Mattermost (включает 127.0.0.1 localhost) и EnablePostActionIntegration равно true в ServiceSettings.
  • Кнопки возвращают 404 при клике: id кнопки вероятно содержит дефисы или подчёркивания. Маршрутизатор действий Mattermost ломается на неалфавитно-цифровых ID. Используйте только [a-zA-Z0-9].
  • Логи шлюза invalid _token: несовпадение HMAC. Проверьте, что подписываете все поля context (не подмножество), используете отсортированные ключи и компактный JSON (без пробелов). См. раздел HMAC выше.
  • Логи шлюза missing _token in context: поле _token отсутствует в context кнопки. Убедитесь, что оно включено при формировании полезной нагрузки интеграции.
  • Подтверждение показывает сырой ID вместо имени кнопки: context.action_id не совпадает с id кнопки. Задайте оба одинаковым санитизированным значением.
  • Агент не знает о кнопках: добавьте capabilities: ["inlineButtons"] в конфигурацию канала Mattermost.