---
read_when:
    - Налаштування Mattermost
    - Налагодження маршрутизації Mattermost
sidebarTitle: Mattermost
summary: Налаштування бота Mattermost і конфігурація OpenClaw
title: Mattermost
x-i18n:
    generated_at: "2026-07-16T17:40:08Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: e7d2233e26c6c0a510a264001a1e0d3e528d8645ffbe2affa3f1672304185ef5
    source_path: channels/mattermost.md
    workflow: 16
---

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

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

<Tabs>
  <Tab title="Реєстр npm">
    ```bash
    openclaw plugins install @openclaw/mattermost
    ```
  </Tab>
  <Tab title="Локальна робоча копія">
    ```bash
    openclaw plugins install ./path/to/local/mattermost-plugin
    ```
  </Tab>
</Tabs>

Докладніше: [Плагіни](/uk/tools/plugin)

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

<Steps>
  <Step title="Переконайтеся, що Plugin доступний">
    Установіть `@openclaw/mattermost` за допомогою наведеної вище команди, а потім перезапустіть Gateway, якщо його вже запущено.
  </Step>
  <Step title="Створіть бота Mattermost">
    Створіть обліковий запис бота Mattermost, скопіюйте **токен бота** та додайте бота до команд і каналів, які він має читати.
  </Step>
  <Step title="Скопіюйте базову URL-адресу">
    Скопіюйте **базову URL-адресу** Mattermost (наприклад, `https://chat.example.com`). Кінцевий `/api/v4` видаляється автоматично.
  </Step>
  <Step title="Налаштуйте OpenClaw і запустіть Gateway">
    Мінімальна конфігурація:

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

    Неінтерактивний варіант:

    ```bash
    openclaw channels add --channel mattermost --bot-token <token> --http-url https://chat.example.com
    ```

  </Step>
</Steps>

<Note>
Самостійно розгорнутий Mattermost за приватною адресою, адресою локальної мережі або tailnet: вихідні запити до API Mattermost проходять через захист від SSRF, який типово блокує приватні та внутрішні IP-адреси. Дозвольте їх за допомогою `channels.mattermost.network.dangerouslyAllowPrivateNetwork: true` (для окремого облікового запису: `channels.mattermost.accounts.<id>.network.dangerouslyAllowPrivateNetwork`).
</Note>

## Власні команди з похилою рискою

Власні команди з похилою рискою вмикаються за бажанням. Коли їх увімкнено, OpenClaw реєструє команди з похилою рискою `oc_*` у кожній команді, учасником якої є бот, і отримує зворотні виклики POST на HTTP-сервері Gateway.

```json5
{
  channels: {
    mattermost: {
      commands: {
        native: true,
        nativeSkills: true,
        callbackPath: "/api/channels/mattermost/command",
        // Використовуйте, коли Mattermost не може безпосередньо звернутися до Gateway (зворотний проксі/загальнодоступна URL-адреса).
        callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
      },
    },
  },
}
```

Зареєстровані команди: `/oc_status`, `/oc_model`, `/oc_models`, `/oc_new`, `/oc_help`, `/oc_think`, `/oc_reasoning`, `/oc_verbose`, `/oc_queue`. З `nativeSkills: true` команди Skills також реєструються як `/oc_<skill>`.

<AccordionGroup>
  <Accordion title="Примітки щодо поведінки">
    - `native` і `nativeSkills` типово мають значення `"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` (значення облікового запису перевизначають поля верхнього рівня).
    - Наявні команди з похилою рискою з таким самим тригером, створені іншими інтеграціями, залишаються без змін (під час реєстрації їх пропускають); команди, створені ботом, оновлюються або створюються повторно, коли змінюється URL-адреса зворотного виклику.
    - Зворотні виклики команд перевіряються за окремими токенами команд, які Mattermost повертає, коли OpenClaw реєструє команди `oc_*`.
    - Перед прийняттям кожного зворотного виклику OpenClaw оновлює поточні дані про реєстрацію команди Mattermost, тому застарілі токени видалених або повторно створених команд з похилою рискою перестають прийматися без перезапуску Gateway.
    - Якщо API Mattermost не може підтвердити актуальність команди, перевірка зворотного виклику завершується відмовою; невдалі перевірки короткочасно кешуються, паралельні пошуки об’єднуються, а частота запуску нових пошуків обмежується для кожної команди, щоб стримувати навантаження від повторного відтворення.
    - Зворотні виклики команд із похилою рискою завершуються відмовою, якщо реєстрація не вдалася, запуск був частковим або токен зворотного виклику не збігається із зареєстрованим токеном визначеної команди (токен, дійсний для однієї команди, не може пройти до подальшої перевірки для іншої команди).
    - Прийняті зворотні виклики підтверджуються тимчасовою відповіддю «Обробка...», а справжня відповідь надходить як звичайне повідомлення.

  </Accordion>
  <Accordion title="Вимога щодо доступності">
    Кінцева точка зворотного виклику має бути доступною із сервера Mattermost.

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

  </Accordion>
  <Accordion title="Список дозволених вихідних адрес Mattermost">
    Якщо зворотні виклики спрямовано на приватні, внутрішні адреси або адреси tailnet, налаштуйте `ServiceSettings.AllowedUntrustedInternalConnections` Mattermost так, щоб він містив хост або домен зворотного виклику.

    Використовуйте записи хостів або доменів, а не повні URL-адреси.

    - Правильно: `gateway.tailnet-name.ts.net`
    - Неправильно: `https://gateway.tailnet-name.ts.net`

  </Accordion>
</AccordionGroup>

## Змінні середовища (типовий обліковий запис)

Якщо надаєте перевагу змінним середовища, установіть на хості Gateway такі значення:

- `MATTERMOST_BOT_TOKEN=...`
- `MATTERMOST_URL=https://chat.example.com`

<Note>
Змінні середовища застосовуються лише до **типового** облікового запису (`default`). Для інших облікових записів потрібно використовувати значення конфігурації.

`MATTERMOST_URL` не можна встановити з `.env` робочого простору; див. [Файли .env робочого простору](/uk/gateway/security).
</Note>

## Режими чату

Mattermost автоматично відповідає на приватні повідомлення. Поведінкою в каналах керує `chatmode`:

<Tabs>
  <Tab title="oncall (типово)">
    Відповідати в каналах лише у разі згадування через @.
  </Tab>
  <Tab title="onmessage">
    Відповідати на кожне повідомлення в каналі.
  </Tab>
  <Tab title="onchar">
    Відповідати, коли повідомлення починається з префікса-тригера.
  </Tab>
</Tabs>

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

```json5
{
  channels: {
    mattermost: {
      chatmode: "onchar",
      oncharPrefixes: [">", "!"], // типово
    },
  },
}
```

Примітки:

- `onchar` усе одно реагує на явні згадування через @.
- `channels.mattermost.requireMention` усе ще враховується, але рекомендовано `chatmode`. Налаштування `groups.<channelId>.requireMention` для окремого каналу мають вищий пріоритет за обидва.
- Після того як бот надішле видиму відповідь у гілці каналу, він відповідатиме на подальші повідомлення в тій самій гілці без нового згадування через @ або префікса `onchar`, тому багатоетапні розмови в гілці триватимуть без переривання. Участь запам’ятовується протягом 7 днів після останньої відповіді бота в цій гілці та зберігається після перезапусків Gateway. Це не стосується гілок, які бот лише спостерігав; щоб знову вимагати явного згадування, почніть нове повідомлення верхнього рівня.

## Гілки та сеанси

Використовуйте `channels.mattermost.replyToMode`, щоб визначити, чи залишатимуться відповіді в каналі та групі в основному каналі, чи створюватимуть гілку під початковим дописом.

- `off` (типово): відповідати в гілці, лише якщо вхідний допис уже належить до неї.
- `first`: для дописів верхнього рівня в каналах або групах створювати гілку під цим дописом і спрямовувати розмову до сеансу, пов’язаного з гілкою.
- `all` і `batched`: наразі в Mattermost поводяться так само, як `first`, оскільки після створення кореня гілки в Mattermost подальші фрагменти та медіафайли надсилаються в ту саму гілку.
- Для приватних повідомлень типовим залишається `off`, навіть якщо встановлено `replyToMode`.

Використовуйте `channels.mattermost.replyToModeByChatType`, щоб перевизначити режим для чатів `direct`, `group` або `channel`. Установіть `direct`, щоб увімкнути гілки для приватних повідомлень:

- `off` (типово): приватні повідомлення залишаються без гілок в одному безперервному сеансі.
- `first`, `all` або `batched`: кожне приватне повідомлення верхнього рівня починає гілку Mattermost, пов’язану з новим незалежним сеансом.

```json5
{
  channels: {
    mattermost: {
      replyToMode: "all",
      replyToModeByChatType: {
        direct: "first",
      },
    },
  },
}
```

Примітки:

- Сеанси, пов’язані з гілкою, використовують ідентифікатор початкового допису як корінь гілки.
- `first` і `all` наразі рівнозначні, оскільки після створення кореня гілки в Mattermost подальші фрагменти та медіафайли надсилаються в ту саму гілку.
- Перевизначення для окремих типів чатів мають вищий пріоритет за `replyToMode`. Без перевизначення `direct` наявні розгортання зберігають плоскі приватні повідомлення без гілок.

## Керування доступом (приватні повідомлення)

- Типово: `channels.mattermost.dmPolicy = "pairing"` (невідомі відправники отримують код сполучення). Інші значення: `allowlist`, `open`, `disabled`.
- Підтвердження:
  - `openclaw pairing list mattermost`
  - `openclaw pairing approve mattermost <CODE>`
- Загальнодоступні приватні повідомлення: `channels.mattermost.dmPolicy="open"` разом із `channels.mattermost.allowFrom=["*"]` (схема конфігурації вимагає підстановний знак).
- `channels.mattermost.allowFrom` приймає ідентифікатори користувачів (рекомендовано) і записи `accessGroup:<name>`. Див. [Групи доступу](/uk/channels/access-groups).

## Канали (групи)

- Типово: `channels.mattermost.groupPolicy = "allowlist"` (потрібне згадування).
- Дозвольте відправників за допомогою `channels.mattermost.groupAllowFrom` (рекомендовано використовувати ідентифікатори користувачів).
- `channels.mattermost.groupAllowFrom` приймає записи `accessGroup:<name>`. Див. [Групи доступу](/uk/channels/access-groups).
- Перевизначення вимоги згадування для окремих каналів розміщуються в `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`), а в журнал одноразово записується попередження.

Приклад:

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

## Цілі вихідного доставлення

Використовуйте ці формати цілей із `openclaw message send` або cron/Webhook:

| Ціль                                | Куди доставляється                                             |
| ----------------------------------- | ------------------------------------------------------------- |
| `channel:<id>`                      | Канал за ідентифікатором                                      |
| `channel:<name>` або `#channel-name` | Канал за назвою; пошук серед команд, до яких належить бот      |
| `user:<id>` або `mattermost:<id>`    | Приватне повідомлення цьому користувачеві                      |
| `@username`                         | Приватне повідомлення (ім’я користувача визначається через API Mattermost) |

Вихідне надсилання підтримує не більше одного вкладення на повідомлення; надсилайте кілька файлів окремими повідомленнями.

<Warning>
Непрефіксовані непрозорі ідентифікатори (як-от `64ifufp...`) у Mattermost **неоднозначні** (ідентифікатор користувача або ідентифікатор каналу).

OpenClaw спочатку перевіряє **користувача**:

- Якщо ідентифікатор належить користувачеві (`GET /api/v4/users/<id>` виконується успішно), OpenClaw надсилає **приватне повідомлення**, визначивши приватний канал через `/api/v4/channels/direct`.
- Інакше ідентифікатор вважається **ідентифікатором каналу**.

Якщо потрібна детермінована поведінка, завжди використовуйте явні префікси (`user:<id>` / `channel:<id>`).
</Warning>

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

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

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

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

Примітки:

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

## Потокове попереднє відображення

Mattermost потоково виводить міркування, активність інструментів і частковий текст відповіді в **чернетку попереднього допису**, яка остаточно оновлюється на місці, коли підсумкову відповідь можна безпечно надіслати. У режимі `partial` попередній перегляд оновлюється в дописі з тим самим ідентифікатором, а не засмічує канал окремими повідомленнями для кожного фрагмента. У режимі `block` попередній перегляд почергово показує блоки завершеного тексту й активності інструментів, тому попередні блоки залишаються видимими як окремі дописи, а не перезаписуються наступними. Остаточні повідомлення з медіафайлами або помилками скасовують відкладені редагування попереднього перегляду й використовують звичайне доставлення замість публікації непотрібного попереднього допису.

Потокове попереднє відображення **увімкнено за замовчуванням** у режимі `partial`. Налаштовуйте через `channels.mattermost.streaming.mode` (застарілі скалярні/булеві значення `streaming` мігруються командою `openclaw doctor --fix`):

```json5
{
  channels: {
    mattermost: {
      streaming: { mode: "partial" }, // вимкнено | частково | блок | перебіг
    },
  },
}
```

<AccordionGroup>
  <Accordion title="Режими потокового виведення">
    - `partial` (за замовчуванням): один попередній допис, який редагується в міру формування відповіді, а потім остаточно оновлюється повною відповіддю.
    - `block` почергово показує в попередньому перегляді блоки завершеного тексту й активності інструментів, тому кожен блок залишається видимим як окремий допис, а не перезаписується на місці. Паралельні та послідовні оновлення інструментів використовують спільний поточний допис активності інструментів.
    - `progress` показує попередній перегляд стану під час генерування й публікує остаточну відповідь лише після завершення.
    - `off` вимикає потокове попереднє відображення. З `streaming.block.enabled: true` завершені блоки асистента все одно доставляються як звичайні блокові відповіді (окремі дописи), а не як один об’єднаний остаточний допис.

  </Accordion>
  <Accordion title="Примітки щодо потокового виведення">
    - Якщо потік неможливо остаточно оновити на місці (наприклад, допис видалили під час потокового виведення), OpenClaw натомість надсилає новий остаточний допис, щоб відповідь не було втрачено.
    - Корисні навантаження, що містять лише міркування, не публікуються в каналі, зокрема текст, який надходить як цитата `> Thinking`. Установіть `/reasoning on`, щоб бачити міркування в інших інтерфейсах; остаточний допис Mattermost міститиме лише відповідь.
    - Матрицю зіставлення каналів див. у розділі [Потокове виведення](/uk/concepts/streaming#preview-streaming-modes).

  </Accordion>
</AccordionGroup>

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

- Використовуйте `message action=react` з `channel=mattermost`.
- `messageId` — це ідентифікатор допису Mattermost.
- `emoji` приймає назви на зразок `thumbsup` або `:+1:` (двокрапки необов’язкові).
- Установіть `remove=true` (булеве значення), щоб видалити реакцію.
- Події додавання та видалення реакцій пересилаються як системні події до спрямованого сеансу агента з урахуванням тих самих перевірок політик приватних і групових чатів, що й для повідомлень.

Приклади:

```text
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`.

## Інтерактивні кнопки (інструмент повідомлень)

Надсилайте повідомлення з кнопками, які можна натискати. Коли користувач натискає кнопку, агент отримує вибраний варіант і може відповісти.

Кнопки надходять із семантичного корисного навантаження `presentation` (у звичайних відповідях агента та в `message action=send`). OpenClaw відображає кнопки зі значеннями як інтерактивні кнопки Mattermost, залишає URL-кнопки видимими в тексті повідомлення та перетворює меню вибору на читабельний текст.

```text
message action=send channel=mattermost target=channel:<channelId> presentation={"blocks":[{"type":"buttons","buttons":[{"label":"Так","value":"yes"},{"label":"Ні","value":"no"}]}]}
```

Поля кнопки представлення:

<ParamField path="label" type="string" required>
  Відображувана мітка (псевдонім: `text`).
</ParamField>
<ParamField path="value" type="string">
  Значення, яке надсилається після натискання та використовується як ідентифікатор дії (псевдоніми: `callback_data`, `callbackData`). Обов’язкове для кнопки, яку можна натиснути, якщо не встановлено `url`.
</ParamField>
<ParamField path="url" type="string">
  Кнопка-посилання; відображається як текст `label: url` у тілі повідомлення замість інтерактивної кнопки.
</ParamField>
<ParamField path="style" type='"primary" | "secondary" | "success" | "danger"'>
  Стиль кнопки. Mattermost застосовує стандартне оформлення до значень, які не підтримує.
</ParamField>

Щоб оголосити про підтримку кнопок у системній підказці агента, додайте `inlineButtons` до можливостей каналу:

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

Коли користувач натискає кнопку:

<Steps>
  <Step title="Перевірка доступу">
    Користувач, який натиснув кнопку, має пройти ті самі перевірки політик приватних і групових чатів, що й відправник повідомлення; у разі несанкціонованого натискання показується тимчасове сповіщення, а дію ігнорують.
  </Step>
  <Step title="Кнопки замінюються підтвердженням">
    Усі кнопки замінюються рядком підтвердження (наприклад, «✓ **Так** вибрано користувачем @user»).
  </Step>
  <Step title="Агент отримує вибраний варіант">
    Агент отримує вибраний варіант як вхідне повідомлення (разом із системною подією) і відповідає.
  </Step>
</Steps>

<AccordionGroup>
  <Accordion title="Примітки щодо реалізації">
    - Зворотні виклики кнопок використовують перевірку HMAC-SHA256 (автоматично, налаштування не потрібне).
    - Після натискання замінюється весь блок вкладення, тому всі кнопки видаляються разом — часткове видалення неможливе.
    - Ідентифікатори дій, що містять дефіси або символи підкреслення, очищуються автоматично (обмеження маршрутизації Mattermost).
    - Натискання, у яких `action_id` не відповідає дії в початковому дописі, відхиляються з `403` («Невідома дія»).

  </Accordion>
  <Accordion title="Конфігурація та доступність">
    - `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` (за замовчуванням 18789), а потім використовує `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 або внутрішньої мережі, додайте її хост або домен до `ServiceSettings.AllowedUntrustedInternalConnections` Mattermost.

  </Accordion>
</AccordionGroup>

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

Зовнішні скрипти й Webhook можуть публікувати кнопки безпосередньо через REST API Mattermost замість використання інструмента агента `message`. Надавайте перевагу інструменту `message` OpenClaw. Для прямих інтеграцій імпортуйте `buildButtonAttachments` з `@openclaw/mattermost/api.js`; якщо публікуєте необроблений JSON, дотримуйтеся таких правил:

**Структура корисного навантаження:**

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

<Warning>
**Критично важливі правила**

1. Вкладення мають міститися в `props.attachments`, а не у верхньорівневому `attachments` (інакше їх мовчки ігнорують).
2. Кожна дія потребує `type: "button"` — без нього натискання мовчки відкидаються.
3. Кожна дія потребує поля `id` — Mattermost ігнорує дії без ідентифікаторів.
4. `id` дії має містити **лише літери та цифри** (`[a-zA-Z0-9]`). Дефіси та символи підкреслення порушують серверну маршрутизацію дій Mattermost (повертається 404). Видаляйте їх перед використанням.
5. `context.action_id` має відповідати `id` кнопки; Gateway відхиляє натискання, якщо `action_id` відсутній у дописі.
6. `context.action_id` є обов’язковим — без нього обробник взаємодій повертає 400.
7. IP-адреса джерела зворотного виклику має бути дозволена (див. `interactions.allowedSourceIps` вище).

</Warning>

**Генерування токена HMAC**

Gateway перевіряє натискання кнопок за допомогою HMAC-SHA256. Зовнішні скрипти мають генерувати токени, що відповідають логіці перевірки Gateway:

<Steps>
  <Step title="Виведіть секрет із токена бота">
    `HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)`, закодований у шістнадцятковому форматі.
  </Step>
  <Step title="Створіть об’єкт контексту">
    Створіть об’єкт контексту з усіма полями, **крім** `_token`.
  </Step>
  <Step title="Серіалізуйте з відсортованими ключами">
    Серіалізуйте з **рекурсивно відсортованими ключами** та **без пробілів** (Gateway також канонізує вкладені об’єкти й створює компактний JSON).
  </Step>
  <Step title="Підпишіть корисне навантаження">
    `HMAC-SHA256(key=secret, data=serializedContext)`
  </Step>
  <Step title="Додайте токен">
    Додайте отриманий шістнадцятковий дайджест як `_token` у контекст.
  </Step>
</Steps>

Приклад на Python:

```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}
```

<AccordionGroup>
  <Accordion title="Поширені помилки HMAC">
    - `json.dumps` у Python за замовчуванням додає пробіли (`{"key": "val"}`). Використовуйте `separators=(",", ":")`, щоб отримати такий самий компактний результат, як у JavaScript (`{"key":"val"}`).
    - Завжди підписуйте **всі** поля контексту (крім `_token`). Gateway видаляє `_token`, а потім підписує все, що залишилося. Підписування лише частини полів спричиняє непомітну помилку перевірки.
    - Використовуйте `sort_keys=True` — Gateway сортує ключі перед підписуванням, а Mattermost може змінити порядок полів контексту під час збереження корисного навантаження.
    - Отримуйте секрет із токена бота (детерміновано), а не з випадкових байтів. Секрет має бути однаковим у процесі, що створює кнопки, і в Gateway, який виконує перевірку.

  </Accordion>
</AccordionGroup>

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

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

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

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

Mattermost підтримує кілька облікових записів у `channels.mattermost.accounts`:

```json5
{
  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" },
      },
    },
  },
}
```

Значення облікового запису перевизначають поля верхнього рівня; `channels.mattermost.defaultAccount` визначає, який обліковий запис використовується, якщо його не вказано.

## Усунення несправностей

<AccordionGroup>
  <Accordion title="У каналах немає відповідей">
    Переконайтеся, що бот перебуває в каналі, і згадайте його (oncall), використайте префікс запуску (onchar) або задайте `chatmode: "onmessage"`.
  </Accordion>
  <Accordion title="Помилки автентифікації або кількох облікових записів">
    - Перевірте токен бота, базову URL-адресу й те, чи ввімкнено обліковий запис.
    - Проблеми з кількома обліковими записами: змінні середовища застосовуються лише до облікового запису `default`.
    - Для приватних Mattermost-хостів або хостів у локальній мережі потрібен `network.dangerouslyAllowPrivateNetwork: true` (захист від SSRF за замовчуванням блокує приватні IP-адреси).

  </Accordion>
  <Accordion title="Вбудовані команди зі скісною рискою не працюють">
    - `Unauthorized: invalid command token.`: OpenClaw не прийняв токен зворотного виклику. Типові причини:
      - реєстрація команди зі скісною рискою не вдалася або під час запуску завершилася лише частково
      - зворотний виклик надходить до неправильного Gateway або облікового запису
      - у Mattermost досі є старі команди, спрямовані на попередню ціль зворотного виклику
      - Gateway перезапустився без повторної активації команд зі скісною рискою
    - Якщо вбудовані команди зі скісною рискою перестали працювати, перевірте журнали на наявність `mattermost: failed to register slash commands` або `mattermost: native slash commands enabled but no commands could be registered`.
    - Якщо `callbackUrl` не вказано, а журнали попереджають, що зворотний виклик визначено як URL-адресу зворотної петлі на кшталт `http://localhost:18789/...`, ця URL-адреса, імовірно, доступна лише тоді, коли Mattermost працює на тому самому хості або в тому самому мережевому просторі імен, що й OpenClaw. Натомість явно задайте зовнішньо доступний `commands.callbackUrl`.

  </Accordion>
  <Accordion title="Проблеми з кнопками">
    - Кнопки відображаються як білі прямокутники або не відображаються взагалі: дані кнопок мають неправильний формат. Кожна кнопка представлення потребує `label` і `value` (кнопки без одного з цих полів відкидаються).
    - Кнопки відображаються, але натискання нічого не роблять: переконайтеся, що Gateway доступний із сервера Mattermost, IP-адресу сервера Mattermost додано до `channels.mattermost.interactions.allowedSourceIps` (без нього приймається лише зворотна петля), а `ServiceSettings.AllowedUntrustedInternalConnections` містить хост зворотного виклику для приватних цілей.
    - Після натискання кнопки повертають 404: `id` кнопки, імовірно, містить дефіси або символи підкреслення. Маршрутизатор дій Mattermost не працює з небуквено-цифровими ідентифікаторами. Використовуйте лише `[a-zA-Z0-9]`.
    - Gateway реєструє `rejected callback source`: натискання надійшло з IP-адреси поза `interactions.allowedSourceIps`. Додайте сервер Mattermost або вашу точку входу до списку дозволених і задайте `gateway.trustedProxies` за зворотним проксі.
    - Gateway реєструє `invalid _token`: невідповідність HMAC. Переконайтеся, що підписуєте всі поля контексту (а не лише частину), використовуєте відсортовані ключі й компактний JSON (без пробілів). Див. розділ про HMAC вище.
    - Gateway реєструє `missing _token in context`: поля `_token` немає в контексті кнопки. Переконайтеся, що його додано під час формування корисного навантаження інтеграції.
    - Gateway відхиляє натискання з `Unknown action`: `context.action_id` не відповідає жодному `id` дії в дописі. Задайте для обох однакове очищене значення.
    - Агент не пропонує кнопки: додайте `capabilities: ["inlineButtons"]` до конфігурації каналу Mattermost.

  </Accordion>
</AccordionGroup>

## Пов’язані матеріали

- [Маршрутизація каналів](/uk/channels/channel-routing) — маршрутизація сеансів для повідомлень
- [Огляд каналів](/uk/channels) — усі підтримувані канали
- [Групи](/uk/channels/groups) — поведінка групових чатів і обмеження за згадуванням
- [Сполучення](/uk/channels/pairing) — автентифікація в особистих повідомленнях і процес сполучення
- [Безпека](/uk/gateway/security) — модель доступу та посилення захисту
