golang

Три плагина для Mattermost за день: как мы закрыли боли мессенджера с помощью Cursor

  • понедельник, 24 августа 2026 г. в 00:00:13
https://habr.com/ru/articles/1073404/

Очередная статья про то, как AI упрощает жизнь разработчикам, и параллельно отбирает у него куски работы, которые ещё недавно казались «только руками и с опытом». Долго можно было отмахиваться: «ну нет, это игрушка». Сейчас уже сложнее: случилось то, что отрицали. Описал задачу, получил рабочий код, осталось проверить на стенде и не накосячить в проде.

Cursor использую постоянно, но история не про «AI всё сделал за меня» (хотя в целом сделал но не с первого раза). Задачи от бизнеса не было: три мелочи в Mattermost нас задолбали, я решил допилить сам. Заодно опенсорс в копилку и немного кармы: порадовать коллег, с которыми каждый день в одних чатах. Раньше на это ушла бы неделя: docs, примеры, trial and error. Ушёл день. Вот что из этого вышло.

Кратко: self-hosted Mattermost, обычная лицензия. Три раздражителя в ежедневной работе:

  • ответ на сообщение в ленте

  • статус «прочитал или нет» (ну такое, но тоже полезно)

  • быстрый голосовой созвон.

Закрыл тремя плагинами, собрал за ~день в Cursor, выложил на GitHub. Ниже: зачем, что получилось и где не ждите чуда.

Почему бы вообще не переехать

Когда в Mattermost не хватает мелочи, мысль всплывает сама: «Может, на другое?»

Переезда не было, но думали. Решений на СНГ рынке много, как стартапов так и решений из Big Tech. Каждый раз упирались в одно: у вендора, за чей продукт начнёшь платить, что-то пойдёт не так. Падения, деньги кончатся, закроются, заблокируют. А ты уже мигрировал, уже заплатил. И снова искать, куда бежать.

Mattermost не идеальный из коробки, зато платформа с плагинами. Не хватает фичи: допилить. Не хочешь допиливать: Enterprise. Я пошёл третьим путём, своими силами, без тикета в бэклог.

Три боли, три плагина

1. Channel Reply  

Ответ на конкретное сообщение в ленте канала, не только в треде.

2. Message Status

Галочки «доставлено / прочитано» на своих сообщениях.

3. Voice Channels 

Постоянные голосовые комнаты в сайдбаре, без отдельного сервиса.

Почему 3 отдельных а не один

Вспомним один из принципов SRP (Single Responsibility Principle).
Я решил что каждый плагин должен закрывать свою проблему и есть риск одним плагином заблокировать работу других, а так можно отключить конкретный и продолжать работать с остальными.

Channel Reply: «ответь на это сообщение»

В Mattermost есть треды. Нажал Reply, ответ ушёл в боковую панель. Для длинных веток норм.

Но часто нужно другое: ответ в потоке канала с цитатой, чтобы все видели контекст, а не копировали текст руками и не писали «+1 к сообщению @ivan выше».

Что сделал:

  • кнопка Reply на сообщении;

  • превью цитаты над полем ввода;

  • ответ отдельным постом с кликабельной цитатой (переход к оригиналу);

  • встроенную кнопку MM переименовал в Thread, чтобы не путать;

Мобилка: UI плагина там не работает. Но в текст поста зашита markdown-цитата, прочитать ответ с контекстом можно. Красивый блок с кликом только в вебе.

Message Status: «он видел или я в пустоту пишу?»

Классическая рабочая ситуация: написал коллеге, ждёшь. Тишина. Он занят? Не заходил? Прочитал и забыл ответить? В Mattermost на Team Edition ты этого не знаешь. В Enterprise read receipts есть, но мы не на Enterprise.

Зачем галочки:

- ✓ серая: сообщение дошло до сервера, доставлено;

- ✓✓ зелёные: собеседник увидел сообщение в клиенте (прокрутил в зону видимости);

- при наведении: кто именно прочитал.

Плагин рисует галочки только на своих сообщениях. Обновления в реальном времени.

Хитрость, которую стоит знать: «прочитано» фиксируется, когда человек смотрит сообщение в вебе или desktop (там крутится webapp плагина). Открыл чат в нативном приложении, галочки у отправителя не зеленеют. Можно читать «тихо» :)

Галочки сами по себе тоже видны только в web/desktop, в телефоне их UI нет.

Voice Channels: «созвонимся на пять минут»

Нужен был быстрый голос без ссылки на Jitsi, который мы обычно использовали (там была отдельная авторизация, что ещё больше раздражало, но отдадим должное, работал стабильно). Комнаты в сайдбаре: зашёл, поговорил, вышел. Всё на своём сервере.

Что сделал: WebRTC + Pion на Go, аудио живёт прямо в server-плагине, без отдельного медиа-хоста. Комнаты под Threads, список участников, mute, плавающий виджет. Настройки STUN/TURN и NAT для прода за Docker.

Почему не LiveKit, MediaSoup и прочие SFU «на отдельной машине»: нужно было быстрое базовое решение, а не второй сервис с деплоем, мониторингом и ещё одним местом, где что-то упадёт. Pion внутри плагина проще для нашего масштаба.

Важно: нужно пробросить UDP-порты (20000-20010) и указать публичный IP. Без этого плагин самый капризный из трёх. Зато завёлся, удобно.

Еще момент, что вариант с медиасервером а не P2P выбран для дальнейшего внедрения записей встреч и отправкой ссылки на неё ботом в канал, но это как руки дойдут.

Как уложился в ~день с Cursor

Я не Mattermost-разработчик, зато работаю с медиа (WebRTC, стриминг, аудио/видео). С Mattermost plugin API до этого особо не пересекался: CRT, registerPostActionComponent, всё новое.

AI не в вакууме. Для Cursor заранее заложил правила: что использовать, как собирать, как отлаживать, особенно для плагина со звонками. Явно прописал: медиа через Pion в Go-плагине, без разворачивания LiveKit/MediaSoup на отдельном хосте. Без этого Cursor любезно предложил бы «поднимите SFU-кластер», красиво, но не за день.

Но в целом можно сделать так, чтобы медиасервер не лежал прямо внутри а был отдельно развернут указав адрес где он живет, полезно чтобы не уронить звонками весь мессенджер, но нас не очень много в компании, поэтому должно справиться.

Работало так:

1. Описал боль и ограничения: «reply как в Slack», «галочки на своих сообщениях», «голосовые комнаты, Pion, один бинарник плагина.

2. AI набросал каркас: plugin.json, Makefile, webapp, server hooks.

3. Итерации по скриншотам и логам: «кнопка не там», «renegotiation ломается», «NAT без 1:1 IP», правка, make dist, стенд.

4. Документацию MM читал точечно; по WebRTC/Pion опирался на свой бэкграунд и подсказывал AI, где он уходит не туда.

~один рабочий день до рабочих прототипов. Поставил на стенд, коллеги начали пользоваться, без презентаций и «официального релиза». Для меня это и был успех: перестало бесить, плюс три репозитория на GitHub.

AI хорош для каркаса и незнакомого API, если ты задаёшь рамки. Без опыта в медиа и без правил для Cursor он бы утонул в over-engineering. Staging, NAT, mobile, всё равно на вас.

Главный подводный камень: плагины = web + desktop

Запомнить одно:

Web, Desktop | ✅ всё работает

приложения iOS, Android | ❌ UI плагинов нет

Нативные приложения не грузят ваш main.js. Server-side (Go) работает везде. Кнопки, галочки, комнаты только там, где webapp.

На практике:

- Reply: создавать с телефона нельзя, читать (как цитату) можно.

- Галочки: видны и обновляются в web/desktop; в приложении, «тихое чтение».

- Голос: только web/desktop. (но вполне можно вытащить в отдельный веб чтобы и с телефона заходить на крайний случай)

Как поставить

Можно собрать самому.

make dist

Либо из релиза взять готовую сборку и загрузить её.

.tar.gz в System Console → Plugins → Upload. Mattermost 9.0+, после установки Ctrl+F5.

Риски: major-апдейт MM может потребовать правок; support на вас; перед продом, staging.

Итог

Mattermost на обычной лицензии не тупик, если готовы допилить узкие боли плагинами. Три штуки за день с AI: личная инициатива, не проект от бизнеса. Trade-off один: мобилка урезана, у нас большинство с ПК и ноутбука, терпимо.

Ссылки:
Channel Reply - https://github.com/Azario16/mattermost-plugin-channel-reply
Message Status- https://github.com/Azario16/mattermost-plugin-message-status
Voice Channels - https://github.com/Azario16/mattermost-plugin-voice-channel

MIT. Берите, форкайте, критикуйте. Я сначала починил своё, потом выложил наружу.

ИИ-ииии помните :)

Пишите почаще руками.
Всем удачи!