"""Общий на приложение `TelegramClient` (#tg-connection-resilience). Зачем: до этого три HTTP-ручки (`api.v1.support` ×2, `api.v1.glitchtip`) делали `TelegramClient(token)` на КАЖДЫЙ входящий запрос, а клиент внутри пересоздавал `httpx.AsyncClient` на каждую попытку — то есть keep-alive не было ни на каком уровне и каждый запрос начинался с полного TCP+TLS-хендшейка до api.telegram.org. Здесь живёт один экземпляр на процесс: создаётся в lifespan (`app.main`), закрывается на shutdown там же. Воркер бота (`app.tgbot_main`) сюда НЕ ходит — у него свой процесс без ASGI и свой экземпляр на всё время жизни поллинга. """ from __future__ import annotations import logging from app.core.config import settings from app.services.tgbot.client import TelegramClient logger = logging.getLogger(__name__) _client: TelegramClient | None = None def get_telegram_client() -> TelegramClient: """Общий клиент приложения. Ленив: создаётся при первом обращении. Ленивость (а не «только из lifespan») нужна из-за kill-switch: при пустом `TELEGRAM_BOT_TOKEN` в lifespan создавать нечего, а тесты ручек поднимают приложение без прохода через startup. """ global _client if _client is None: _client = TelegramClient(settings.telegram_bot_token) return _client def init_telegram_client() -> TelegramClient: """Явное создание на старте приложения (lifespan).""" return get_telegram_client() async def close_telegram_client() -> None: """Закрывает общий клиент на shutdown. Идемпотентно.""" global _client client, _client = _client, None if client is not None: await client.aclose() logger.info("tg shared client: пул соединений закрыт")