---
read_when:
    - Настройка Mattermost
    - Отладка маршрутизации Mattermost
sidebarTitle: Mattermost
summary: Настройка бота Mattermost и конфигурация OpenClaw
title: Mattermost
x-i18n:
    generated_at: "2026-07-16T16:06:41Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: e7d2233e26c6c0a510a264001a1e0d3e528d8645ffbe2affa3f1672304185ef5
    source_path: channels/mattermost.md
    workflow: 16
---

Статус: загружаемый плагин (токен бота + события 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>

Подробнее: [Плагины](/ru/tools/plugin)

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

<Steps>
  <Step title="Убедитесь, что плагин доступен">
    Установите `@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 с адресом в частной сети/LAN/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` команды навыков также регистрируются как `/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, если этот URL не проксирует `/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 или внутренние адреса, задайте в Mattermost `ServiceSettings.AllowedUntrustedInternalConnections`, включив в него хост или домен обратного вызова.

    Используйте записи хостов или доменов, а не полные 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 рабочей области](/ru/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>`. См. [Группы доступа](/ru/channels/access-groups).

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

- По умолчанию: `channels.mattermost.groupPolicy = "allowlist"` (требуется упоминание).
- Добавьте отправителей в список разрешённых с помощью `channels.mattermost.groupAllowFrom` (рекомендуются идентификаторы пользователей).
- `channels.mattermost.groupAllowFrom` принимает записи `accessGroup:<name>`. См. [Группы доступа](/ru/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/webhooks:

| Цель                                | Место доставки                                                |
| ----------------------------------- | ------------------------------------------------------------- |
| `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" }, // off | partial | block | progress
    },
  },
}
```

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

  </Accordion>
  <Accordion title="Примечания о поведении потоковой передачи">
    - Если поток невозможно финализировать на месте (например, публикация была удалена во время потоковой передачи), OpenClaw отправляет новую финальную публикацию, чтобы ответ не был потерян.
    - Полезная нагрузка, содержащая только рассуждения, не публикуется в канале, включая текст, поступающий как цитата `> Thinking`. Установите `/reasoning on`, чтобы видеть рассуждения в других интерфейсах; финальная публикация Mattermost содержит только ответ.
    - Матрицу сопоставления каналов см. в разделе [Потоковая передача](/ru/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, оставляет кнопки со ссылками видимыми в тексте сообщения и преобразует меню выбора в удобочитаемый текст.

```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 (внешние скрипты)

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

  </Accordion>
</AccordionGroup>

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

Плагин 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` не указан, а в журналах выводится предупреждение, что для обратного вызова определен loopback-URL, например `http://localhost:18789/...`, этот URL, вероятно, доступен только в том случае, если Mattermost работает на том же хосте или в том же сетевом пространстве имен, что и OpenClaw. Вместо него задайте явно доступный извне `commands.callbackUrl`.

  </Accordion>
  <Accordion title="Проблемы с кнопками">
    - Кнопки отображаются как белые прямоугольники или не отображаются вовсе: данные кнопок имеют неверный формат. Для каждой кнопки представления требуются `label` и `value` (кнопки без любого из этих значений отбрасываются).
    - Кнопки отображаются, но нажатия ничего не делают: убедитесь, что Gateway доступен с сервера Mattermost, IP-адрес сервера Mattermost включен в `channels.mattermost.interactions.allowedSourceIps` (без этого принимается только loopback-адрес), а `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>

## Связанные материалы

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