t2sTelegramBot/ARCHITECTURE.md

149 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Архитектура сервиса 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
```