# Архитектура сервиса T2S Telegram Bot ## Описание Сервис реализует двухбота — один для пользователей (озвучка текста в голос), второй для администратора (логи и копии озвучек). Оба бота запускаются в одном Docker-контейнере в рамках одного asyncio-процесса. ## Компоненты ``` ┌─────────────────────────────────────────────────────┐ │ Docker Container │ │ ┌──────────────────────────────────────────────┐ │ │ │ Python 3.12 процесс │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ User Bot App │ │ Admin Bot App │ │ │ │ │ │ (Application) │ │ (Application) │ │ │ │ │ │ │ │ │ │ │ │ │ │ Token: │ │ Token: │ │ │ │ │ │ USER_BOT_TOKEN │ │ ADMIN_BOT_TOKEN│ │ │ │ │ └───────┬─────────┘ └────────┬────────┘ │ │ │ │ │ │ │ │ │ │ │ Admin Bot │ │ │ │ │ │ Instance │ │ │ │ │ └──────┬───────────────┘ │ │ │ │ │ (Bot token=ADMIN_BOT_TOKEN) │ │ │ │ ▼ │ │ │ │ ┌────────────────────────────┐ │ │ │ │ │ edge-tts (TTS) │ │ │ │ │ │ Microsoft TTS Service │ │ │ │ │ │ via websocket/HTTP │ │ │ │ │ │ Voice: ru-RU-DmitryNeural │ │ │ │ │ └────────────────────────────┘ │ │ │ └──────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘ ``` ## Потоки данных ### Пользовательский поток ``` Пользователь ──текст──▶ User Bot App ──TTS──▶ edge-tts │ User Bot App ◀──audio.ogg────────────┘ │ голосовое сообщение │ ▼ Пользователь ``` ### Администраторский поток ``` User Bot App ──текст + user info──▶ Admin Bot (прямой Bot-клиент) │ голосовое + подпись │ ▼ Администратор ``` ## Детали реализации ### `bot.py` — точка входа ```python async def main(): admin_app = Application.builder().token(ADMIN_TOKEN).build() user_app = Application.builder().token(USER_TOKEN).build() # Оба запускаются внутри вложенных async with ``` Ключевые решения: 1. **Один процесс** — два `Application` внутри вложенных `async with`. Это позволяет разделять память (переменная `admin_chat_id`) и не усложнять инфраструктуру. 2. **Отдельный Bot-клиент для админа** — `admin_bot = Bot(token=ADMIN_TOKEN)` создаётся как экземпляр `telegram.Bot`, а не `Application`. Это лёгкий HTTP-клиент для отправки сообщений без полного цикла поллинга. 3. **Временные файлы** — TTS генерируется в `NamedTemporaryFile(suffix='.ogg')`, файл открывается для чтения, отправляется, затем удаляется в `finally`. Никакого накопления мусора. ### Озвучка (edge-tts) - Библиотека `edge-tts` эмулирует запрос к Microsoft Edge TTS API - Не требует API-ключа, бесплатно - Голос кешируется при первом вызове (скачивается в `~/.cache/edge-tts/`) - Формат вывода — Ogg Opus (нативно поддерживается Telegram как голосовое сообщение) - Асинхронный вызов: `await edge_tts.Communicate(text, voice).save(path)` ### Telegram Bot API - Используется `python-telegram-bot` v21+ (синтаксис `Application`, `async with`) - Long-polling (без webhook — не требует публичного HTTPS-адреса) - `send_action(action='record_voice')` показывает индикатор "запись голоса" ## Обработка ошибок | Сценарий | Реакция | |---|---| | edge-tts недоступен | Исключение в `communicate.save()` → ошибка в лог | | Админ не запустил `/start` | `admin_chat_id is None` → лог `warning` + без уведомления | | Ошибка отправки админу | `try/except` → лог `error`, пользователь всё равно получает ответ | | Временный файл не удалился | `try/unlink` в `finally` — silent ignore | ## Сетевая модель ``` Container ──443/tcp──▶ api.telegram.org (боты) Container ──443/tcp──▶ speech.microsoft.com (edge-tts) ``` Исходящие HTTPS-соединения. Входящих нет — только long-polling к Telegram API. ## Зависимости - `python:3.12-slim` — base image (~130 MB) - `python-telegram-bot >=21, <22` — Telegram API - `edge-tts >=6, <7` — Microsoft TTS - `httpx` — HTTP-клиент PTB - `aiohttp` — HTTP-клиент edge-tts ## Конфигурация Вся конфигурация через переменные окружения (файл `.env`): ```env USER_BOT_TOKEN=*** # Токен пользовательского бота (Telegram BotFather) ADMIN_BOT_TOKEN=*** # Токен админского бота TTS_VOICE=ru-RU-DmitryNeural # Голос edge-tts (опционально) ``` ## Docker ```yaml # docker-compose.yml services: t2s: build: ./app container_name: t2s-telegram-bot restart: always env_file: .env ``` Сборка при заблокированном Docker Hub: ```bash DOCKER_BUILDKIT=0 docker build --network=host -t t2s-telegram-bot ./app ```