Developer and self-hosted

Mattermost

Status: завантажуваний Plugin (токен бота + події WebSocket). Підтримуються канали, групи та особисті повідомлення. Mattermost — це самостійно розгортувана платформа командного обміну повідомленнями; подробиці про продукт і завантаження див. на офіційному сайті mattermost.com.

Встановлення

Установіть Mattermost перед налаштуванням каналу:

реєстр npm

openclaw plugins install @openclaw/mattermost

локальна робоча копія

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

Докладніше: Plugins

Швидке налаштування

  • Переконайтеся, що Plugin доступний

    Поточні пакетовані випуски OpenClaw уже включають його. Старіші або власні встановлення можуть додати його вручну за допомогою команд вище.

  • Створіть бота Mattermost

    Створіть обліковий запис бота Mattermost і скопіюйте токен бота.

  • Скопіюйте базову URL-адресу

    Скопіюйте базову URL-адресу Mattermost (наприклад, https://chat.example.com).

  • Налаштуйте OpenClaw і запустіть gateway

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

    {
      channels: {
        mattermost: {
          enabled: true,
          botToken: "mm-token",
          baseUrl: "https://chat.example.com",
          dmPolicy: "pairing",
        },
      },
    }
    
  • Нативні слеш-команди

    Нативні слеш-команди вмикаються окремо. Коли їх увімкнено, OpenClaw реєструє слеш-команди oc_* через API Mattermost і отримує callback POST-запити на HTTP-сервері gateway.

    {
      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",
          },
        },
      },
    }
    
    Примітки щодо поведінки
    • native: "auto" за замовчуванням вимкнено для Mattermost. Установіть native: true, щоб увімкнути.
    • Якщо callbackUrl пропущено, OpenClaw виводить його з хоста/порту gateway + callbackPath.
    • Для налаштувань із кількома обліковими записами commands можна задати на верхньому рівні або в channels.mattermost.accounts.<id>.commands (значення облікового запису перевизначають поля верхнього рівня).
    • Callback-запити команд перевіряються за токенами окремих команд, які повертає Mattermost, коли OpenClaw реєструє команди oc_*.
    • OpenClaw оновлює поточну реєстрацію команд Mattermost перед прийняттям кожного callback-запиту, тому застарілі токени з видалених або повторно згенерованих слеш-команд перестають прийматися без перезапуску gateway.
    • Перевірка callback-запиту відмовляє за замовчуванням, якщо API Mattermost не може підтвердити, що команда досі актуальна; невдалі перевірки коротко кешуються, паралельні запити об’єднуються, а початок свіжих пошуків обмежується за частотою для кожної команди, щоб обмежити тиск повторного відтворення.
    • Слеш-callback-запити відмовляють за замовчуванням, коли реєстрація не вдалася, запуск був частковим або токен callback-запиту не збігається із зареєстрованим токеном визначеної команди (токен, дійсний для однієї команди, не може дійти до upstream-перевірки для іншої команди).
    Вимога доступності

    Кінцева точка callback має бути доступною із сервера Mattermost.

    • Не задавайте callbackUrl як localhost, якщо Mattermost не працює на тому самому хості/у тому самому мережевому просторі імен, що й OpenClaw.
    • Не задавайте callbackUrl як базову URL-адресу Mattermost, якщо ця URL-адреса не reverse-proxy /api/channels/mattermost/command до OpenClaw.
    • Швидка перевірка: curl https://<gateway-host>/api/channels/mattermost/command; GET має повернути 405 Method Not Allowed від OpenClaw, а не 404.
    Allowlist вихідних з’єднань Mattermost

    Якщо ваш callback націлено на приватні/tailnet/внутрішні адреси, задайте Mattermost ServiceSettings.AllowedUntrustedInternalConnections, щоб включити хост/домен callback.

    Використовуйте записи хоста/домену, а не повні 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: на сьогодні така сама поведінка, як first, для Mattermost.
    • Особисті повідомлення ігнорують це налаштування й залишаються без потоків.

    Приклад конфігурації:

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

    Примітки:

    • Сеанси, прив’язані до потоку, використовують id допису-тригера як корінь потоку.
    • first і all наразі еквівалентні, бо щойно 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" (із вимогою згадки).
    • Додавайте відправників до allowlist за допомогою channels.mattermost.groupAllowFrom (рекомендовано ID користувачів).
    • Перевизначення згадки для окремих каналів розміщуються в channels.mattermost.groups.<channelId>.requireMention або channels.mattermost.groups["*"].requireMention для значення за замовчуванням.
    • Зіставлення @username є змінним і вмикається лише коли channels.mattermost.dangerouslyAllowNameMatching: true.
    • Відкриті канали: channels.mattermost.groupPolicy="open" (із вимогою згадки).
    • Примітка щодо runtime: якщо channels.mattermost повністю відсутній, runtime повертається до 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 для особистого повідомлення (визначається через API Mattermost)

    Повторна спроба каналу особистих повідомлень

    Коли OpenClaw надсилає до цілі особистого повідомлення Mattermost і спочатку має визначити прямий канал, він за замовчуванням повторює спроби для тимчасових збоїв створення прямого каналу.

    Використовуйте channels.mattermost.dmChannelRetry, щоб налаштувати цю поведінку глобально для Plugin Mattermost, або channels.mattermost.accounts.<id>.dmChannelRetry для одного облікового запису.

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

    Примітки:

    • Це застосовується лише до створення каналу особистих повідомлень (/api/v4/channels/direct), а не до кожного виклику API Mattermost.
    • Повторні спроби застосовуються до тимчасових збоїв, як-от обмеження частоти, відповіді 5xx і помилки мережі або тайм-ауту.
    • Клієнтські помилки 4xx, окрім 429, вважаються постійними й не повторюються.

    Потоковий preview

    Mattermost передає міркування, активність інструментів і частковий текст відповіді в один чернетковий preview-допис, який фіналізується на місці, коли фінальну відповідь безпечно надіслати. Preview оновлюється в тому самому id допису замість засмічення каналу повідомленнями для кожного фрагмента. Фінали з медіа/помилками скасовують очікувані редагування preview і використовують звичайну доставку замість скидання одноразового preview-допису.

    Увімкніть через channels.mattermost.streaming:

    {
      channels: {
        mattermost: {
          streaming: "partial", // off | partial | block | progress
        },
      },
    }
    
    Режими потокового передавання
    • partial — звичайний вибір: один preview-допис, який редагується в міру зростання відповіді, а потім фіналізується повною відповіддю.
    • block використовує чернеткові фрагменти зі стилем додавання всередині preview-допису.
    • progress показує preview стану під час генерації й публікує фінальну відповідь лише після завершення.
    • off вимикає потоковий preview.
    Примітки щодо поведінки потокового передавання
    • Якщо потік не можна фіналізувати на місці (наприклад, допис було видалено посеред потоку), OpenClaw повертається до надсилання нового фінального допису, щоб відповідь ніколи не загубилася.
    • Payload-и лише з міркуваннями приглушуються в дописах каналу, включно з текстом, що надходить як blockquote > Reasoning:. Установіть /reasoning on, щоб бачити міркування на інших поверхнях; фінальний допис Mattermost зберігає лише відповідь.
    • Див. Streaming для матриці зіставлення каналів.

    Реакції (інструмент повідомлень)

    • Використовуйте message action=react із channel=mattermost.
    • messageId — це id допису Mattermost.
    • 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"],
        },
      },
    }
    

    Використовуйте message action=send з параметром buttons. Кнопки — це 2D-масив (рядки кнопок):

    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 виводить URL-адресу зворотного виклику з gateway.customBindHost + gateway.port, а потім повертається до http://localhost:<port>.
    • Правило доступності: URL-адреса зворотного виклику кнопки має бути доступною із сервера Mattermost. localhost працює лише тоді, коли Mattermost і OpenClaw працюють на одному хості/просторі імен мережі.
    • Якщо ціль зворотного виклику приватна/tailnet/внутрішня, додайте її хост/домен до ServiceSettings.AllowedUntrustedInternalConnections Mattermost.

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

    Зовнішні скрипти та webhooks можуть публікувати кнопки напряму через Mattermost REST API замість проходження через інструмент message агента. За можливості використовуйте buildButtonAttachments() із Plugin; якщо публікуєте необроблений JSON, дотримуйтеся цих правил:

    Структура payload:

    {
      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:

  • Виведіть секрет із токена бота

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

  • Побудуйте об'єкт контексту

    Побудуйте об'єкт контексту з усіма полями, крім _token.

  • Серіалізуйте з відсортованими ключами

    Серіалізуйте з відсортованими ключами та без пробілів (Gateway використовує JSON.stringify з відсортованими ключами, що створює компактний вивід).

  • Підпишіть payload

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

  • Додайте токен

    Додайте отриманий шістнадцятковий digest як _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
    • json.dumps у Python типово додає пробіли ({"key": "val"}). Використовуйте separators=(",", ":"), щоб відповідати компактному виводу JavaScript ({"key":"val"}).
    • Завжди підписуйте всі поля контексту (без _token). Gateway прибирає _token, а потім підписує все, що залишилося. Підписування підмножини призводить до мовчазного збою перевірки.
    • Використовуйте sort_keys=True - Gateway сортує ключі перед підписуванням, а Mattermost може змінити порядок полів контексту під час зберігання payload.
    • Виводьте секрет із токена бота (детерміновано), а не з випадкових байтів. Секрет має бути однаковим у процесі, який створює кнопки, і в Gateway, який їх перевіряє.

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

    Plugin Mattermost містить адаптер каталогу, який розв'язує назви каналів і користувачів через Mattermost API. Це вмикає цілі #channel-name і @username в openclaw message send та доставках cron/webhook.

    Конфігурація не потрібна - адаптер використовує токен бота з конфігурації облікового запису.

    Кілька облікових записів

    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.
    Нативні slash-команди не працюють
    • Unauthorized: invalid command token.: OpenClaw не прийняв токен зворотного виклику. Типові причини:
      • реєстрація slash-команди не вдалася або лише частково завершилася під час запуску
      • зворотний виклик потрапляє не в той Gateway/обліковий запис
      • у Mattermost досі є старі команди, що вказують на попередню ціль зворотного виклику
      • Gateway перезапустився без повторної активації slash-команд
    • Якщо нативні slash-команди перестали працювати, перевірте журнали на mattermost: failed to register slash commands або mattermost: native slash commands enabled but no commands could be registered.
    • Якщо callbackUrl пропущено, а журнали попереджають, що зворотний виклик визначився як http://127.0.0.1:18789/..., ця URL-адреса, ймовірно, доступна лише тоді, коли Mattermost працює на тому самому хості/просторі імен мережі, що й OpenClaw. Натомість установіть явний зовнішньо доступний commands.callbackUrl.
    Проблеми з кнопками
    • Кнопки з'являються як білі блоки: агент може надсилати неправильно сформовані дані кнопок. Перевірте, що кожна кнопка має поля text і callback_data.
    • Кнопки відображаються, але натискання нічого не робить: перевірте, що AllowedUntrustedInternalConnections у конфігурації сервера Mattermost містить 127.0.0.1 localhost, і що EnablePostActionIntegration має значення true у ServiceSettings.
    • Кнопки повертають 404 під час натискання: id кнопки, ймовірно, містить дефіси або підкреслення. Маршрутизатор дій Mattermost ламається на неалфавітно-цифрових ID. Використовуйте лише [a-zA-Z0-9].
    • Журнали Gateway показують invalid _token: невідповідність HMAC. Перевірте, що ви підписуєте всі поля контексту (а не підмножину), використовуєте відсортовані ключі та компактний JSON (без пробілів). Дивіться розділ HMAC вище.
    • Журнали Gateway показують missing _token in context: поля _token немає в контексті кнопки. Переконайтеся, що його включено під час побудови integration payload.
    • Підтвердження показує необроблений ID замість назви кнопки: context.action_id не збігається з id кнопки. Установіть для обох однакове нормалізоване значення.
    • Агент не знає про кнопки: додайте capabilities: ["inlineButtons"] до конфігурації каналу Mattermost.

    Пов'язане