> ## Documentation Index
> Fetch the complete documentation index at: https://docs2.openclaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Mattermost

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

## Установка

> [tab] Реестр npm
>
> ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
> openclaw plugins install @openclaw/mattermost
> ```

> [tab] Локальная рабочая копия
>
> ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
> openclaw plugins install ./path/to/local/mattermost-plugin
> ```

Подробнее: [Плагины](https://docs2.openclaw.ai/ru/tools/plugin.md)

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

**1. Убедитесь, что плагин доступен**

Установите `@openclaw/mattermost` с помощью приведённой выше команды, затем перезапустите Gateway, если он уже запущен.

**2. Создайте бота Mattermost**

Создайте учётную запись бота Mattermost, скопируйте **токен бота** и добавьте бота в команды и каналы, которые он должен читать.

**3. Скопируйте базовый URL**

Скопируйте **базовый URL** Mattermost (например, `https://chat.example.com`). Завершающий `/api/v4` удаляется автоматически.

**4. Настройте OpenClaw и запустите Gateway**

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

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

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

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw channels add --channel mattermost --bot-token <token> --http-url https://chat.example.com
```

> [!NOTE]
>
> Для самостоятельно размещённого Mattermost с адресом в частной сети/LAN/tailnet: исходящие запросы к API Mattermost проходят через защиту от SSRF, которая по умолчанию блокирует частные и внутренние IP-адреса. Разрешите их с помощью `channels.mattermost.network.dangerouslyAllowPrivateNetwork: true` (для отдельной учётной записи: `channels.mattermost.accounts.<id>.network.dangerouslyAllowPrivateNetwork`).

## Нативные команды со слешем

Нативные команды со слешем включаются явно. Когда они включены, OpenClaw регистрирует команды со слешем `oc_*` в каждой команде, участником которой является бот, и получает обратные POST-запросы на HTTP-сервере Gateway.

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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>`.

> [accordion] Примечания о поведении
>
> * `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] Требование доступности
>
> Конечная точка обратного вызова должна быть доступна с сервера 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] Список разрешённых исходящих адресов Mattermost
>
> Если обратный вызов направлен на частные адреса, адреса tailnet или внутренние адреса, задайте в Mattermost `ServiceSettings.AllowedUntrustedInternalConnections`, включив в него хост или домен обратного вызова.
>
> Используйте записи хостов или доменов, а не полные URL.
>
> * Правильно: `gateway.tailnet-name.ts.net`
> * Неправильно: `https://gateway.tailnet-name.ts.net`

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

Если предпочитаете переменные окружения, задайте их на хосте Gateway:

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

> [!NOTE]
>
> Переменные окружения применяются только к учётной записи **по умолчанию** (`default`). Для остальных учётных записей необходимо использовать значения конфигурации.
>
> `MATTERMOST_URL` нельзя задать из файла `.env` рабочей области; см. [Файлы .env рабочей области](https://docs2.openclaw.ai/ru/gateway/security.md).

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

Mattermost автоматически отвечает на личные сообщения. Поведение в каналах управляется параметром `chatmode`:

> [tab] oncall (по умолчанию)
>
> Отвечать в каналах только при @упоминании.

> [tab] onmessage
>
> Отвечать на каждое сообщение в канале.

> [tab] onchar
>
> Отвечать, когда сообщение начинается с префикса-триггера.

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

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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>`. См. [Группы доступа](https://docs2.openclaw.ai/ru/channels/access-groups.md).

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

* По умолчанию: `channels.mattermost.groupPolicy = "allowlist"` (требуется упоминание).
* Добавьте отправителей в список разрешённых с помощью `channels.mattermost.groupAllowFrom` (рекомендуются идентификаторы пользователей).
* `channels.mattermost.groupAllowFrom` принимает записи `accessGroup:<name>`. См. [Группы доступа](https://docs2.openclaw.ai/ru/channels/access-groups.md).
* Переопределения требования упоминания для отдельных каналов задаются в `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 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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>`).

## Повторная попытка для канала личных сообщений

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

Используйте `channels.mattermost.dmChannelRetry`, чтобы настроить это поведение глобально для плагина Mattermost, или `channels.mattermost.accounts.<id>.dmChannelRetry` для отдельной учётной записи. Значения по умолчанию:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  channels: {
    mattermost: {
      streaming: { mode: "partial" }, // off | partial | block | progress
    },
  },
}
```

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

> [accordion] Примечания о поведении потоковой передачи
>
> * Если поток невозможно финализировать на месте (например, публикация была удалена во время потоковой передачи), OpenClaw отправляет новую финальную публикацию, чтобы ответ не был потерян.
> * Полезная нагрузка, содержащая только рассуждения, не публикуется в канале, включая текст, поступающий как цитата `> Thinking`. Установите `/reasoning on`, чтобы видеть рассуждения в других интерфейсах; финальная публикация Mattermost содержит только ответ.
> * Матрицу сопоставления каналов см. в разделе [Потоковая передача](https://docs2.openclaw.ai/ru/concepts/streaming.md#preview-streaming-modes).

## Реакции (инструмент сообщений)

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

Примеры:

```text theme={"theme":{"light":"min-light","dark":"min-dark"}}
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 theme={"theme":{"light":"min-light","dark":"min-dark"}}
message action=send channel=mattermost target=channel:<channelId> presentation={"blocks":[{"type":"buttons","buttons":[{"label":"Да","value":"yes"},{"label":"Нет","value":"no"}]}]}
```

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

**label** `string` *required*

Отображаемая подпись (псевдоним: `text`).

**value** `string`

Значение, возвращаемое при нажатии и используемое как идентификатор действия (псевдонимы: `callback_data`, `callbackData`). Обязательно для нажимаемой кнопки, если не задано `url`.

**url** `string`

Кнопка-ссылка; отображается как текст `label: url` в теле сообщения, а не как интерактивная кнопка.

**style** `"primary" | "secondary" | "success" | "danger"`

Стиль кнопки. Для неподдерживаемых значений Mattermost применяет оформление по умолчанию.

Чтобы указать поддержку кнопок в системном промпте агента, добавьте `inlineButtons` в возможности канала:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  channels: {
    mattermost: {
      capabilities: ["inlineButtons"],
    },
  },
}
```

Когда пользователь нажимает кнопку:

**1. Проверка доступа**

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

**2. Замена кнопок подтверждением**

Все кнопки заменяются строкой подтверждения (например, «✓ **Да** — выбор пользователя @user»).

**3. Агент получает выбранное значение**

Агент получает выбранное значение как входящее сообщение (а также системное событие) и отвечает.

> [accordion] Примечания о реализации
>
> * Обратные вызовы кнопок проверяются с помощью HMAC-SHA256 (автоматически, настройка не требуется).
> * При нажатии заменяется весь блок вложения, поэтому все кнопки удаляются одновременно — частичное удаление невозможно.
> * Идентификаторы действий, содержащие дефисы или символы подчёркивания, автоматически очищаются (ограничение маршрутизации Mattermost).
> * Нажатия, у которых `action_id` не соответствует действию в исходной публикации, отклоняются с ошибкой `403` («Неизвестное действие»).

> [accordion] Конфигурация и доступность
>
> * `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.

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

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

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

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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` выше).

**Генерация токена HMAC**

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

**1. Получение секрета из токена бота**

`HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)`, в шестнадцатеричном представлении.

**2. Создание объекта контекста**

Создайте объект контекста со всеми полями, **кроме** `_token`.

**3. Сериализация с отсортированными ключами**

Выполните сериализацию с **рекурсивно отсортированными ключами** и **без пробелов** (Gateway также канонизирует вложенные объекты и создаёт компактный JSON).

**4. Подписание полезной нагрузки**

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

**5. Добавление токена**

Добавьте полученный шестнадцатеричный дайджест в контекст как `_token`.

Пример на Python:

```python theme={"theme":{"light":"min-light","dark":"min-dark"}}
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}
```

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

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

Плагин Mattermost включает адаптер каталога, который разрешает имена каналов и пользователей через API Mattermost. Это позволяет использовать цели `#channel-name` и `@username` в `openclaw message send`, а также при доставке через Cron и Webhook.

Настройка не требуется: адаптер использует токен бота из конфигурации учетной записи.

## Несколько учетных записей

Mattermost поддерживает несколько учетных записей в `channels.mattermost.accounts`:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  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` определяет, какая учетная запись используется, если она не указана.

## Устранение неполадок

> [accordion] Нет ответов в каналах
>
> Убедитесь, что бот добавлен в канал, и упомяните его (oncall), используйте префикс-триггер (onchar) либо задайте `chatmode: "onmessage"`.

> [accordion] Ошибки аутентификации или нескольких учетных записей
>
> * Проверьте токен бота, базовый URL и то, включена ли учетная запись.
> * Проблемы с несколькими учетными записями: переменные среды применяются только к учетной записи `default`.
> * Для частных или локальных хостов Mattermost требуется `network.dangerouslyAllowPrivateNetwork: true` (защита от SSRF по умолчанию блокирует частные IP-адреса).

> [accordion] Встроенные команды с косой чертой не работают
>
> * `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] Проблемы с кнопками
>
> * Кнопки отображаются как белые прямоугольники или не отображаются вовсе: данные кнопок имеют неверный формат. Для каждой кнопки представления требуются `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.

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

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