← К схеме

Mattermost

чат-интеграция · знания в мессенджере

Знания компании — в переписке Mattermost

Сотрудник пишет боту в личных сообщениях — отвечает Query Engine под его доступами, со ссылками на источники. Всё, что умеет веб-чат, не выходя из мессенджера.

Mattermost — ещё одно тонкое окно в тот же диалог: думает и ищет Query Engine под личностью сотрудника, Mattermost лишь приносит реплику и уносит ответ. Близнец Slack на self-hosted сервере — беседу режет тред, а транспорт — исходящий WebSocket вместо вебхука. Одна модель диалога на все поверхности →

01

Подключение

Admin заводит бот-аккаунт в System Console Mattermost (Integrations → Bot Accounts) один раз — адрес сервера и bot-токен ложатся в mattermost_settings, как SMTP у Email. Экран — настройки платформы, один тумблер включает бота. Включение проверяет токен вживую (GET /api/v4/users/me) и проставляет bot_user_id — так слушатель отличает собственные посты бота.

В отличие от Telegram требования публичного HTTPS нет и webhook-секрета нет вовсе — слушатель подключается сам, наружу ничего не открыто. Адрес сервера в приватной сети — штатно (SSRF-защиты намеренно нет: у Telegram она защищала обратное направление, облако → нас).

02

Личность

Пишущего сводит к учётке identity_mapping (source='mattermost') — личность входа. Событие posted доверенного email не несёт, поэтому авто-матча нет: только код-привязка — вошедший в веб получает код и возвращает его боту. Учётку заводит только админ (приглашением) — знание бота доступа не даёт.

Непривязанному — нейтральный отказ, подбор кода ограничен. Дальше везде только user_id → роль → ACL.

03

Беседа

В личке Mattermost есть треды, поэтому граница — правило Slack: тред — это беседа. Реплика в треде продолжает её, новое корневое сообщение открывает чистую — ни команды /new, ни указателя активной беседы. Контекст усекается по токенам — общий механизм движка.

Беседа живёт в conversations (ключ channel_id + root_id в meta; вложенных тредов нет — реплика несёт id настоящего корня). Группы · @mention · командные каналы — v2.

04

Доставка

HTTP-push для DM у Mattermost нет, поэтому внутрь никто не стучится: singleton-слушатель WebSocket в процессе планировщика (единственный сервис в одну реплику) сам подключается к {base_url}/api/v4/websocket, аутентифицируется bot-токеном и слушает события posted. Он только транспортирует — фильтрует структурные DM (не собственный пост бота, без системного subtype, не другой бот), обработка уходит в очередь interactive, ответ возвращается по REST постом в тред. Переживает рестарт и ретраи.

Повтор события отсекается по job id = id поста; окно по каналу — под rate-limit fail-closed. Здоровье слушателя — Redis-ключ с TTL, карточка админа показывает его тихой строкой «слушает / не подключён»; канал сигналов настройкам не нужен — слушатель перечитывает их каждые 30 с и разрывает соединение при смене своего конфига.

Как это выглядит

Ответ под доступами Максима: что ему не видно — в ответ не попадёт. Реплика в треде продолжает беседу, новое корневое сообщение открывает чистую.

Первый контакт — привязка аккаунта кодом:

Бот отвечает одинаково всем — известному и встречному, существование инстанса не выдаёт. Код виден только во вошедшем браузере и возвращается в бот — пересланная ссылка угнать учётку не даёт. Экран привязки →

Что в базе

identity_mapping Auth · правка + source='mattermost' → мост личности входа
link_tokens Auth · общая одноразовый код привязки (вошёл в веб → вернул боту)
conversations.meta Query Engine ключ беседы channel_id + root_id
cache:mattermost:listener Cache & Workers здоровье слушателя · TTL (90 с) — истёкший ключ честно читается как «не запущен»
mattermost_settings подключение к боту · общее ядро singleton · CHECK (id = 1)
id BigInteger PKCHECK всегда 1 · singleton
base_url Text NULL адрес сервера (https://mattermost.company.com) · любая установка с API v4, приватная сеть — штатно
bot_token_enc Text NULL токен бот-аккаунта из System Console, шифротекст AES-256-GCM
bot_user_id Text NULL из /users/me · проставляется только успешной проверкой · позволяет слушателю пропускать собственные посты бота
bot_username Text NULL @имя бота для UI · из /users/me
enabled Boolean NOT NULLDEFAULT мастер-выключатель · DEFAULT false · off → бот молчит
last_test_ok Boolean NULL итог последней проверки: токен ответил — здоровье доставки показывает флаг подключённости слушателя, отдельно
last_test_at DateTime(tz) NULL когда проверяли · питает чип статуса на экране
created_at DateTime(tz) DEFAULT server_default=now()
updated_at DateTime(tz) DEFAULT server_default=now() · триггер set_updated_at()

is_available() — свойство модели, не колонка: enabled и заполнены base_url · bot_token_enc · bot_user_id. bot_user_id играет ту же роль «честно подключено», что webhook_secret_enc у Telegram — появляется только после того, как токен действительно ответил.

Честное включение. Включение бота атомарно: Achilles проверяет токен вживую (GET /api/v4/users/me), и переключатель остаётся включённым только если сервер ответил — отказ (отозванный токен, недостижимый адрес) откатывает enabled и возвращает причину, а не зелёный тумблер без бота за ним. Проверка заодно проставляет bot_user_id и bot_username. «Проверить соединение» читает ту же правду — проверка ручается за токен + сервер; за доставку ручается флаг подключённости слушателя.

Проверки: фильтр слушателя (свои посты · боты · системные)резолв личностидедуп по id постатред = беседаACL-проброснепривязанный пользовательлимит подбора кодачестное включение

API

Webhook-эндпоинта нет: единственный входящий путь — исходящий WebSocket, анонимного маршрута не существует вовсе.