This commit is contained in:
Your Name
2026-07-19 21:24:04 +00:00
commit 4edb1f4b87
13 changed files with 463 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
# Telegram Call Service
REST-сервис: `POST /call` → делает настоящий **1-на-1 звонок** (p2p call,
такой же, как обычный звонок в приложении Telegram — с гудком и ожиданием
ответа) указанному пользователю под вашим аккаунтом и озвучивает переданный
текст через TTS сразу после того, как вызываемый принял звонок.
## Как это устроено
Telegram Bot API **не умеет** звонить — это принципиальное ограничение платформы,
никакой код это не обойдёт. Настоящий голосовой звонок доступен только через
MTProto-аккаунт обычного пользователя (Pyrogram-форк `pyrofork` + `py-tgcalls`,
низкоуровневый биндинг `ntgcalls`).
Библиотека `py-tgcalls` делает это через `phone.requestCall` / `phone.acceptCall`
с DH key exchange — ровно тот же путь, которым идёт обычный звонок из
приложения. `play()` блокирует выполнение, пока собеседник не ответит (или не
истечёт `CALL_RING_TIMEOUT`), бросает `TimedOutAnswer` / `CallDeclined` /
`CallBusy`, если не дозвонились.
## ⚠️ Важно понимать риски
- Это автоматизация **личного аккаунта**, а не бота. Telegram может ограничить
или заблокировать аккаунт за автоматизированные действия, особенно при
частом/массовом использовании. Используйте выделенный номер, не основной.
- API_ID/API_HASH и session-файл дают полный доступ к аккаунту — храните
`.env` и `sessions/` так же бережно, как пароль.
- Не используйте это для звонков посторонним людям без их согласия.
- Звонок реально дозвонится только если у вызываемого аккаунта в
**Settings → Privacy and Security → Calls** разрешены звонки от вашего
аккаунта (например, "Everybody", или вы у него в контактах). Иначе Telegram
тихо отклонит попытку на уровне privacy — это ограничение платформы, не бага
сервиса.
## Настройка
### 1. Получить API_ID / API_HASH
https://my.telegram.org → API development tools → создать приложение.
### 2. Настроить .env
```bash
cp .env.example .env
# заполнить API_ID, API_HASH, PHONE_NUMBER, CALL_TARGET, API_TOKEN
```
`CALL_TARGET` — кому звонить по умолчанию: `@username` вызываемого аккаунта
(или его numeric user id). Убедитесь, что у вызывающего аккаунта этот
пользователь виден (например, есть в контактах) — иначе `resolve_peer` не
сможет найти адресата по username при первом обращении.
### 3. Один раз залогиниться (интерактивно, вне обычного запуска)
```bash
docker compose build
docker compose run --rm callsvc python scripts/login.py
```
Введите код из Telegram (и пароль 2FA, если включён). Session-файл сохранится
в `./sessions/` на хосте и будет переиспользоваться при обычном запуске.
### 4. Запуск сервиса
```bash
docker compose up -d
```
## API
### `GET /health`
Проверка живости.
### `POST /call`
```bash
curl -X POST http://localhost:8000/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-d '{"text": "Внимание! Сработал алерт на проде."}'
```
Поля тела запроса:
| поле | обязательное | описание |
|--------|--------------|---------------------------------------------------------------------------|
| text | да | текст, который будет озвучен (TTS, до 2000 символов) |
| target | нет | `@username` или numeric user id вызываемого. Если не задан — берётся `CALL_TARGET` из `.env` |
Если `API_TOKEN` в `.env` не задан — заголовок `Authorization` не требуется.
Ответ (после того, как собеседник принял звонок и TTS проигрался):
```json
{"status": "called", "target": "@monster1025", "duration": 4.2}
```
Ошибки:
- `409` — не дозвонились: не ответили за `CALL_RING_TIMEOUT` секунд, отклонили,
заняты, звонок сброшен, либо звонок на эту цель уже идёт, либо `target`
резолвится в группу/канал (сервис звонит только пользователям).
- `401` — неверный/отсутствующий Bearer-токен (если `API_TOKEN` задан).
- `500` — прочие ошибки (см. логи `docker compose logs -f`).
## Ограничения текущей версии
- Один одновременный звонок на цель (защищено локом), параллельные запросы
на разные `target` обрабатываются независимо.
- Озвучка через gTTS требует исходящего доступа в интернет из контейнера.
- `POST /call` синхронно ждёт ответа на звонок (до `CALL_RING_TIMEOUT` сек) —
учитывайте это в таймауте клиента, который дёргает ручку.