- Клиент бота собирается один раз и достаётся отправителю и транспорту; разрез прошёл по «ответил ли Telegram»: ответ «такого бота нет» роняет старт, недоступность даёт подъём без Telegram (ADR-2026-08-13). Ожидание при сборке ограничено сроком — иначе молчащий Telegram вешал подъём. - Недоставленный ответ не роняет шаг: пишется с job_id и считается метрикой, уровень по причине — WARN для неподнятого входа, ERROR для неназванного адресата. Заведены transcriber_intake_up и transcriber_undelivered_reply_count. - Закрыта утечка токена в журнал: отказ разбора адреса рождается раньше обращения к клиенту, то есть мимо чистки на его границе.
68 lines
3.8 KiB
Go
68 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
|
|
"git.vakhrushev.me/av/transcriber/internal/adapter/telegram"
|
|
"git.vakhrushev.me/av/transcriber/internal/contract"
|
|
"git.vakhrushev.me/av/transcriber/internal/metrics"
|
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
|
)
|
|
|
|
// buildTelegram заводит вход Telegram при старте. Клиент собирается **один
|
|
// раз** и достаётся обоим — отправителю ответов и транспорту бота. Пока его
|
|
// строили порознь, два пути одного старта разошлись: один ронял процесс на
|
|
// негодном токене, другой терпел, и согласовывать их приходилось руками.
|
|
func buildTelegram(botToken string, logger *slog.Logger) (*tgbotapi.BotAPI, contract.TelegramMessageSender, error) {
|
|
bot, err := telegram.NewBot(botToken, logger)
|
|
return telegramFromBot(bot, err, logger)
|
|
}
|
|
|
|
// telegramFromBot решает, чем обернулась сборка клиента, и это решение —
|
|
// единственное содержательное здесь. Оно отделено от самого обращения к
|
|
// Telegram намеренно: обращение ходит в сеть и в проверке недоступно, а
|
|
// разрез проверять надо, иначе его молча вернут к прежнему виду.
|
|
//
|
|
// Разрез проходит по тому, **ответил ли Telegram**, и это решение владельца
|
|
// от 2026-08-13: недоступность Telegram на старт сервиса не влияет.
|
|
//
|
|
// - токен не задан — законный отказ от входа: сервис работает оставшимся;
|
|
// - Telegram ответил отказом — ошибка настройки: бота по этому токену не
|
|
// существует, ждать нечего, и старт роняется. Молча потерянный бот
|
|
// перестаёт отвечать отправителям, а узнать об этом было бы неоткуда;
|
|
// - до Telegram не дошли — недоступность: сеть, DNS, авария Bot API,
|
|
// истёкший срок ожидания. Сервис поднимается без Telegram, потому что
|
|
// основной вход у него другой, и класть его из-за чужой аварии нельзя.
|
|
//
|
|
// Ядро во всех трёх случаях получает непустого отправителя: необязательная
|
|
// зависимость, доехавшая до него нулём, уронила бы первую же задачу из
|
|
// Telegram.
|
|
func telegramFromBot(
|
|
bot *tgbotapi.BotAPI,
|
|
err error,
|
|
logger *slog.Logger,
|
|
) (*tgbotapi.BotAPI, contract.TelegramMessageSender, error) {
|
|
var apiErr *tgbotapi.Error
|
|
|
|
switch {
|
|
case errors.Is(err, telegram.ErrEmptyToken):
|
|
metrics.IntakeUpGauge.WithLabelValues("telegram").Set(0)
|
|
logger.Warn("Telegram bot is not started", "reason", "bot token is not set in configuration")
|
|
return nil, telegram.NewAbsentMessageSender(), nil
|
|
|
|
case errors.As(err, &apiErr):
|
|
// Отказ токена не несёт: его чистит единая точка `telegram.NewBot`.
|
|
return nil, nil, err
|
|
|
|
case err != nil:
|
|
metrics.IntakeUpGauge.WithLabelValues("telegram").Set(0)
|
|
logger.Warn("Telegram bot is not started", "reason", "telegram is unreachable", "error", err)
|
|
return nil, telegram.NewAbsentMessageSender(), nil
|
|
|
|
default:
|
|
metrics.IntakeUpGauge.WithLabelValues("telegram").Set(1)
|
|
return bot, telegram.NewTelegramMessageSender(bot, logger), nil
|
|
}
|
|
}
|