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