telegram: сервис поднимается без бота и работает одним входом

- Клиент бота собирается один раз и достаётся отправителю и транспорту;
  разрез прошёл по «ответил ли Telegram»: ответ «такого бота нет» роняет
  старт, недоступность даёт подъём без Telegram (ADR-2026-08-13). Ожидание
  при сборке ограничено сроком — иначе молчащий Telegram вешал подъём.
- Недоставленный ответ не роняет шаг: пишется с job_id и считается метрикой,
  уровень по причине — WARN для неподнятого входа, ERROR для неназванного
  адресата. Заведены transcriber_intake_up и transcriber_undelivered_reply_count.
- Закрыта утечка токена в журнал: отказ разбора адреса рождается раньше
  обращения к клиенту, то есть мимо чистки на его границе.
This commit is contained in:
av
2026-08-13 19:10:08 +03:00
parent 863ba3b42e
commit b733a84d6a
33 changed files with 1579 additions and 91 deletions
+8 -4
View File
@@ -116,10 +116,14 @@ Ansible из `pet-project-server`). Приложение просто читае
- ключи внешних сервисов не пусты.
*Расхождение:* `LoadConfig` проверяет только существование файла и разбирает
TOML. Пустой токен бота ловится в `NewTelegramController` уже после старта, и
приложение продолжает работу без бота; пустые ключи Yandex ловятся в
конструкторе распознавателя, и вот там процесс уже выходит с кодом 1. Единого
места проверки нет.
TOML. Пустые ключи Yandex ловятся в конструкторе распознавателя, и там процесс
выходит с кодом 1. Единого места проверки нет.
Токен бота под это расхождение больше не подпадает: он судится при сборке
клиента, до подъёма сервера, и разрез у него объявленный — пустое значение
означает отказ от входа и даёт подъём без Telegram, непустое негодное роняет
старт как ошибка настройки. Нормирует это `openspec/specs/intake`, «Недоступный
или незаданный вход Telegram не мешает подъёму».
Секция `[auth]` — первая, у которой проверка своя и стоит на старте:
`AuthConfig.Validate()` зовётся из `main.go` сразу после загрузки и роняет
+8 -3
View File
@@ -65,9 +65,14 @@ transcriber — **приложение, а не библиотека**: внеш
вызывающему нужны **данные** ошибки. Достаём `errors.As`. Не плодим типы там,
где хватает sentinel.
Сегодня в проекте три типизированные ошибки, и данные несёт только одна:
`contract.JobNotFoundError` (состояние и сообщение), `contract.NoopJobError`
(состояние), `tg.EmptyBotTokenError` (без полей — уместнее sentinel).
Сегодня в проекте две типизированные ошибки, и обе несут данные:
`contract.JobNotFoundError` (состояние и сообщение) и `contract.NoopJobError`
(состояние). Третья, `tg.EmptyBotTokenError`, была ровно тем случаем, против
которого написано правило — тип без полей, — и снята задачей
`local-run-without-telegram-token` 2026-08-13; её место занял sentinel
`telegram.ErrEmptyToken`. Рядом с ним живёт `contract.ErrDeliveryChannelDown`
тоже sentinel и по той же причине: заглушка отправителя не знает ни задачи, ни
чата, и нести ей нечего.
## Граница и трансляция: приватный и публичный канал