# Боты и уведомления

# Приём заявок: bug_hunter

*Для разработчиков. Состояние на 24.09.2026. Как пользоваться ботом сотрудникам — статья «Как сообщить об ошибке». Подробности и запуск — README `Buharestskie/bug_hunter_bot`.*

## Коротко

bug\_hunter — приём заявок в разработку: сотрудник заводит ошибку или задачу в мини-приложении Telegram, заявка уходит ответственному за сервис, встаёт в общую очередь и связывается с задачей в YouGile. Разработчик работает в админке, в мини-приложении, в YouGile или через MCP-сервер из Claude Code — статус и переписка синхронизируются.

## Где живёт

<table id="bkmrk-%D0%A1%D0%B5%D1%80%D0%B2%D0%B5%D1%80ulrica-%28%D0%90%D0%BB%D0%BC%D0%B0%D1%82%D1%8B"><tbody><tr><td>Сервер</td><td>`ulrica` (Алматы — оттуда доступен Telegram); каталог и контейнеры — в README</td></tr><tr><td>Адрес</td><td>`https://bug-hunter.donula26.ru` (nginx на сервере)</td></tr><tr><td>Бот</td><td>`@donula_bug_hunter_bot`, вебхук Telegram</td></tr><tr><td>Админка</td><td>`/admin` — вход через Telegram, только ответственные; запасной вход — `/start login` в боте</td></tr><tr><td>Мини-приложение</td><td>`/app` — всем, кроме забаненных</td></tr></tbody></table>

Один процесс FastAPI: вебхук Telegram, API, `/mcp`, `/yougile/webhook` и статика. Данные — Postgres, черновики ввода в чате — Redis, вложения — диск `/data/media`. Фронт собирается на машине разработчика и заливается готовым: Node на сервере нет. Миграции накатываются на старте контейнера.

## Заявка: путь и статусы

<table id="bkmrk-%D0%A1%D1%82%D0%B0%D1%82%D1%83%D1%81%D0%A1%D0%BC%D1%8B%D1%81%D0%BB-%D0%9D%D0%BE%D0%B2%D0%B0%D1%8F%D0%B6%D0%B4%D1%91"><thead><tr><th>Статус</th><th>Смысл</th></tr></thead><tbody><tr><td>Новая</td><td>ждёт разбора ответственным</td></tr><tr><td>На согласовании</td><td>задача ждёт согласующего; в очереди не стоит</td></tr><tr><td>В очереди</td><td>принята, ждёт исполнителя</td></tr><tr><td>В работе</td><td>кто-то взял</td></tr><tr><td>Нужны детали</td><td>задан вопрос автору; ответ автора возвращает прежний статус</td></tr><tr><td>Готово</td><td>сделано; автор задачи может вернуть в работу один раз за 14 дней</td></tr><tr><td>Отклонена / Не баг / Дубль / Отклонена при согласовании / Отозвана</td><td>закрыта без выполнения</td></tr></tbody></table>

**Куда встаёт новая задача:** автор с правом согласования — сразу в очередь; «незнакомый» автор (ни одной разобранной заявки) — в «Новая», решает ответственный; остальные — на согласование, если есть согласующий уровнем выше, иначе в «Новая».

**Очередь:** срочность (Вне очереди → Срочно → В ближайшее время → Не срочно), затем уровень автора (только у задач), затем дата. У ошибки срочности нет, пока её не назначит ответственный. Объединённые заявки занимают одно место.

**Кому уходит новая заявка:** ответственным за сервис; если их нет — всем ответственным с пометкой «Ответственный за сервис не назначен». Задачи на согласовании не уведомляют никого — у согласующих только счётчик в приложении.

**Переписка двигает статус:** не внутреннее сообщение разработчика переводит заявку в «Нужны детали» (даже из «В работе»), ответ автора возвращает её обратно. Внутренние заметки статус не трогают и автору не видны.

**Объединение:** присоединённые заявки получают статус и задачу YouGile главной, смена статуса и срочности идёт на всю группу, автору — одно уведомление на все его заявки группы.

## Каталог сервисов

Каталог — репозитории GitHub организации `Buharestskie` (сверка раз в час) плюс сервисы, заведённые руками. Новый репозиторий появляется **скрытым**: админам приходит «В GitHub появились новые репозитории… Задайте им названия, иначе они не будут видны в боте». Пока в админке «Сервисы» ему не дали человеческое имя и не включили видимость, сотрудники его не выберут. Архивные репозитории из бота пропадают сами. Там же назначаются ответственные за сервис — от этого зависит, кому уходят заявки.

## YouGile

- **Когда заводится задача:** у ошибки — кнопкой «Создать задачу» в карточке, через MCP или переносом карточки на нашей доске в связанную колонку; у задачи — автоматически после согласования или «Принять в очередь».
- **Содержимое:** заголовок «\[Сервис\] кратко», описание — поля заявки, ссылки на вложения и на карточку; стикеры «Репозиторий» и «Срочность». Исполнитель — кто ведёт заявку, иначе ответственные за сервис, иначе нажавший кнопку. «Создал» в YouGile — всегда владелец API-ключа.
- **От нас в YouGile:** «Готово» — в завершающую колонку с отметкой выполнения; отказы — туда же без отметки и с комментарием причины; «В работе» — в рабочую колонку.
- **Из YouGile к нам:** вебхук (`task-moved`, `task-updated`, `task-deleted`, `chat_message-created`) и сверка каждые 10 минут. Статус берётся из колонки: у каждой колонки доски в админке задано, какой статус она ставит. Удалили задачу в YouGile — открытые заявки становятся «Отклонена».
- **Комментарии из YouGile** попадают в заявку, только если у автора комментария в «Людях» указан сотрудник YouGile, и только через вебхук — сверка их не догоняет.
- Доска на проде — «Новая доска» проекта «Разработка-2»: Заблокировано · Бэклог · Ожидает информации · В работе · на проверке · Готово.

## MCP-сервер

`POST https://bug-hunter.donula26.ru/mcp`, JSON-RPC, заголовок `Authorization: Bearer <MCP_TOKEN>`. Действует от имени первичного админа с наименьшим Telegram ID из `ADMIN_TELEGRAM_IDS`.

<table id="bkmrk-%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%A7%D1%82%D0%BE-%D0%B4%D0%B5%D0%BB%D0%B0%D0%B5%D1%82"><thead><tr><th>Инструмент</th><th>Что делает</th></tr></thead><tbody><tr><td>`list_bugs`, `get_bug`, `get_queue`</td><td>список, заявка целиком с перепиской и историей, очередь с местами</td></tr><tr><td>`set_status`</td><td>статус группы: уведомления автору, перенос в YouGile</td></tr><tr><td>`set_urgency`</td><td>срочность группы, стикер в YouGile</td></tr><tr><td>`route_task`</td><td>задача «незнакомца»: принять или отправить на согласование</td></tr><tr><td>`merge_bugs`, `unmerge_bug`</td><td>объединить дубли / отсоединить</td></tr><tr><td>`add_note`</td><td>сообщение в заявку, по умолчанию внутреннее</td></tr><tr><td>`create_yougile_task`</td><td>завести задачу в YouGile</td></tr><tr><td>`list_services`</td><td>каталог сервисов</td></tr></tbody></table>

MCP ведёт себя не во всём как API: `add_note` с `internal=false` не ставит «Нужны детали» и не уходит в YouGile; `set_status` «В работе» для уже взятой заявки молча ничего не делает (API ответил бы 409).

## На что наступают

- **Прод отстаёт от `main`:** на сервере `7f7966a`, не выкачен `837f6c3` (сверка с YouGile перестала откатывать статусы). До выкатки сверка может перезаписать «Нужны детали» и отказы статусом колонки.
- **Ошибки самого бота видны только в логах контейнера приложения.** GlitchTip подключён в коде, но `SENTRY_DSN` на проде не задан. Заявка, которую некому отправить, тоже только в логе.
- **«В работе», пришедшее из YouGile, автору не сообщается** — только «готово», «отклонено» и сообщения.
- **Автор дубля не узнаёт номер исходной заявки** из уведомления — только в карточке.
- **Сверка каждые 10 минут читает чат каждой карточки доски** — сотни запросов к YouGile при общем на компанию лимите частоты.
- **Анализатора нет.** Блок «Гипотеза (ИИ)» в карточке и переменные `ANALYZER_*` — заготовки. Мёртвые также `YOUGILE_BOARD_NAME`, `YOUGILE_COLUMN_NAME`, `YOUGILE_REPO_STICKER_NAME`: доска и стикеры берутся из настроек в базе.
- **Enum в базе хранится именами** (`DONE`, а не `done`) — помнить в миграциях.
- **Локальный стек и прод делят один домен.** Оба ставят вебхук Telegram на `PUBLIC_BASE_URL` при старте — кто стартовал последним, тот и получает обновления. Перед работой с прод-ботом локальный `docker compose` остановить.
- **Локальная копия боевой базы с включённой связью заводит в YouGile подписки на прод-домен** с чужим секретом. Перед локальным запуском выключить связь в настройках или убрать `YOUGILE_API_KEY`.
- **Бэкапов нет.**
- Выкатка — с окном обслуживания: сервис на статус-странице как «Сбор багов и задач» (проба `/healthz`).

# Релей GlitchTip в Telegram

*Для разработчиков. Состояние на 24.09.2026. Подробности — README `Buharestskie/gitchtip_tg_bot`. Отчёт по оплатам для группы ОС описан отдельно — статья «Отчёт по оплатам группы ОС».*

## Коротко

gitchtip\_tg\_bot — релей в Telegram. Принимает HTTP-вебхуки и пересылает их в чаты: алерты GlitchTip, обращения «Сообщить о проблеме» из дашбордов и виджетов, отчёт смены менеджера МОС. Сам следит, жив ли GlitchTip, и раз в день собирает отчёт по оплатам группы ОС.

<figure id="bkmrk-%D0%90%D0%BB%D0%B5%D1%80%D1%82-glitchtip%2C-%D0%BE%D0%B1%D1%80" style="margin:16px 0; text-align:center">[![Алерт GlitchTip, обращение пользователя и монитор](https://wiki.donula26.ru/uploads/images/gallery/2026-09/bots-glitchtip-alerts.png)](https://wiki.donula26.ru/uploads/images/gallery/2026-09/bots-glitchtip-alerts.png)<figcaption style="font-size:13px; color:#6b7280; margin-top:8px; text-align:center">Алерт GlitchTip, обращение пользователя и монитор</figcaption></figure>## Где живёт

<table id="bkmrk-%D0%A1%D0%B5%D1%80%D0%B2%D0%B5%D1%80ulrica-%E2%80%94-%D1%81-%D1%80%D0%BE%D1%81"><tbody><tr><td>Сервер</td><td>`ulrica` — с российских площадок `api.telegram.org` недоступен; каталог, контейнер и адрес — в README</td></tr><tr><td>Адрес</td><td>HTTP без TLS и без reverse proxy, на отдельном порту ulrica</td></tr><tr><td>Код</td><td>доставляется git-бандлом: у ulrica нет доступа к GitHub</td></tr></tbody></table>

## Что куда уходит

Все пути защищены одним токеном в адресе: `/hook/<WEBHOOK_TOKEN>`. Чужой токен — `401`.

<table id="bkmrk-%D0%9A%D1%82%D0%BE-%D1%88%D0%BB%D1%91%D1%82%D0%9F%D1%83%D1%82%D1%8C%D0%9A%D0%B0%D0%BA-%D0%B1%D0%BE%D1%82-"><thead><tr><th>Кто шлёт</th><th>Путь</th><th>Как бот понимает, что это</th><th>Чат</th></tr></thead><tbody><tr><td>GlitchTip (все проекты)</td><td>`/hook/…`</td><td>всё, что не подошло ниже</td><td>`TELEGRAM_CHAT_ID`</td></tr><tr><td>«Сообщить о проблеме»: МОС, МПП1, МПП2, виджет Яндекс.Диска, return\_amo\_hook\_service</td><td>`/hook/…` (у отправителя — `FEEDBACK_WEBHOOK_URL`)</td><td>в теле есть непустой `alias`</td><td>`TELEGRAM_CHAT_ID`</td></tr><tr><td>Отчёт смены из виджета МОС</td><td>`/mos-workday-report/…` (у отправителя — `WORKDAY_REPORT_WEBHOOK_URL`)</td><td>в теле есть объект `report`</td><td>`WORKDAY_REPORT_CHAT_ID`</td></tr><tr><td>Ручной запуск отчёта по оплатам</td><td>`POST /payments-report/…`</td><td>—</td><td>`PAYMENTS_REPORT_CHAT_ID`</td></tr></tbody></table>

Тип определяет **тело**, а не путь: отчёт смены, присланный на `/hook/`, всё равно уйдёт в чат отчётов.

Из пейлоада GlitchTip читаются только `text` и `attachments[].title / title_link / text`. Уровень события не читается — у любого алерта шапка «🔴 GlitchTip».

## Подключить новый проект GlitchTip

1. В GlitchTip, в настройках проекта, — правило алерта с интеграцией **generic webhook**.
2. Адрес — `…/hook/<WEBHOOK_TOKEN>` релея: проще всего скопировать из правила любого уже подключённого проекта; адрес — в README, токен — в `.env` релея на сервере.
3. Как часто алертить, решает правило в GlitchTip: сам релей ничего не фильтрует и не склеивает — один POST, одно сообщение.

## Монитор GlitchTip

Раз в 5 минут релей запрашивает `GLITCHTIP_HEALTH_URL` (таймаут 10 секунд). Пишет только при смене состояния: «🔴 GlitchTip недоступен» с причиной и «🟢 GlitchTip снова доступен». Одного неудачного опроса хватает для алерта; падение и подъём между двумя опросами не заметит.

## На что наступают

- **Нет очереди и повторов.** Если Telegram отказал, отправитель получает `502` и должен повторить сам — иначе сообщение потеряно.
- **Сообщение длиннее 4096 символов** Telegram отклонит — тоже `502`. Режется на части только отчёт по оплатам.
- **Сменили `WEBHOOK_TOKEN`** — поправить адрес в правилах GlitchTip и `FEEDBACK_WEBHOOK_URL` / `WORKDAY_REPORT_WEBHOOK_URL` у всех отправителей.
- **Токен в пути** попадает в access-лог.
- **Поля `…Seconds` в отчёте смены МОС на самом деле минуты** — дашборд уже переводит их, а имена полей не меняет.
- **`aiohttp` не указан в `requirements.txt`** — работает транзитной зависимостью.
- **Отчёт по оплатам падает с `KeyError` молча** — если зарплатный дашборд поменял формат ответа, в чат не придёт ни отчёт, ни сообщение о сбое.
- Внешней пробы у релея нет — окно обслуживания при выкатке открыть не на что.