Документація tg_client

Цей документ описує всі модулі пакету tg_client, окрім директорії graphQL/. У центрі уваги — робота з TDLib, керування сесіями userbot-ів, авторизація, взаємодія з Redis/WebSocket та сервіси для діалогів і медіа.

Примітка: документація не охоплює GraphQL-шару. Якщо потрібна інтеграція з API, використовуйте наведені нижче сервіси як джерело даних та подій. Інформацію по GraphQL-шару можна знайти ТУТ.

1. Огляд архітектури

Пакет складається з кількох горизонтальних шарів:

Основні залежності

  • TDLib (через python-telegram)
  • Redis для шини подій/команд
  • Django ORM + Channels
  • S3/MinIO для медіафайлів

Ключові сервіси

  • TDLibHelper — фасад для API
  • SessionManager — контроль сесій
  • TDLibDialogService — парсер історії
  • WS-консюмери для auth та списку чатів

2. Структура директорій

Каталог Призначення
tg_client/models.py Моделі UserBot та CustomEmoji.
tg_client/_tg_utils/ Конфіг TDLib (config.py) і SessionManager для блокувань та життєвого циклу клієнтів.
tg_client/auth/ Channels-консюмер TDLibAuthConsumer для проходження login/code/password.
tg_client/dialogs/services/ Бізнес-логіка: TDLibHelper, парсер повідомлень, збереження медіа, хелпер UTF-16.
tg_client/dialogs/handlers/ Обробка updateNewMessage з TDLib та пуш у Redis.
tg_client/dialogs/listeners/ Головний лістенер, що стартує userbot-клієнти, слухає Redis-команди й шле відповіді.
tg_client/dialogs/ws/ WebSocket API для фронтенду (наприклад, list_chats/consumers.py).

3. Моделі Django

UserBot

CustomEmoji

4. _tg_utils

config.py

session_manager.py

5. WebSocket-авторизація (tg_client/auth)

TDLibAuthConsumer керує повним циклом логіну userbot-а:

  1. start — ініціалізує SessionManager, створює TDLib-клієнт, запускає login(blocking=False).
  2. send_code — передає код підтвердження (AuthorizationState.WAIT_CODE).
  3. send_password — вводить 2FA (AuthorizationState.WAIT_PASSWORD).
  4. Після READY витягує профіль через get_me(), оновлює UserBot, закриває сесію.

Комунікація завжди ведеться через JSON-повідомлення Channels, Redis тут не залучений.

Ендпойнт WebSocket: wss://<host>/ws/tg-auth/ (див. tg_client/routing.py).

Приклади клієнтських запитів

1. Старт авторизації:

{
  "action": "start",
  "userbot_id": 12,
  "phone": "+380955870336"
}

2. Передача одноразового коду (стає доступною після події code_required):

{
  "action": "send_code",
  "code": "12345"
}

3. Введення 2FA-пароля (коли бекенд надіслав password_required):

{
  "action": "send_password",
  "password": "my-secret"
}

Приклади відповідей сервера

{"type": "connected", "message": "WebSocket connected"}
{"type": "status", "message": "Connecting to Telegram..."}
{"type": "code_required", "message": "Confirmation code sent"}
{"type": "authorized", "message": "Authorization completed", "username": "akva_bot", ...}

Після закриття з'єднання сесія примусово завершується, тому повторні спроби починаються з нового start.

6. Сервіси діалогів

TDLibHelper (dialogs/services/tg_requests.py)

TDLibDialogService

save_to_cloud.py

utf_index_to_py_index.py

Конвертує UTF-16 індекси (які повертає TDLib) у Python-індекси, щоб коректно працювати з емодзі/сурогатами.

7. Лістенери та обробники

main_listener.py

handle_new_update.py

8. WebSocket API для діалогів

TDLibListChatsConsumer

Action (WS) Очікувані поля Результат із Redis
get_chats userbot_id, опц. limit, offset_order, offset_chat_id, chat_list Послідовність {type: "get_chats", payload: chat_item}
open_dialog chat_id, from_message_id, опц. message_thread_id, опц. include_pinned, опц. pinned_limit Потік повідомлень (message) + pinned_message + dialog_end + media_ready; для форумів без message_thread_id повертає топіки
send_message chat_id, text send_message або send_message_error
get_prem_path emoji_id Посилання на лотті/відео кастомного емодзі
forward_message to_chat_ids, from_chat_id, message_ids forward_message або forward_message_error
delete_messages chat_id, from_chat_id, message_ids, revoke delete_messages або delete_messages_error
reply_message chat_id, reply_to_message_id, text reply_message або reply_message_error
edit_message chat_id, message_id, text edit_message або edit_message_error
pin_message chat_id, message_id, опц. disable_notification, only_for_self pin_message або pin_message_error
unpin_message chat_id, message_id unpin_message або unpin_message_error
download_file file_id, опц. source, chat_id, msg_id, message_thread_id, extra download_file або download_file_error + media_ready
archive_chat chat_id archive_chat або archive_chat_error
unarchive_chat chat_id unarchive_chat або unarchive_chat_error
delete_chat chat_id, опц. remove_from_chat_list, опц. revoke delete_chat або delete_chat_error
leave_chat chat_id leave_chat або leave_chat_error
chat_in_folder chat_id, chat_folder_id, опц. added, removed, pin, unpin (дефолт added=true) chat_in_folder або chat_in_folder_error
pin_chat chat_id, опц. is_pinned, опц. chat_list pin_chat або pin_chat_error
import_contacts userbot_id, phone import_contacts або import_contacts_error

Події з Redis

Приклади payload-ів

/ws/chats/
{"action": "open_client", "userbot_ids": [1, 2], "user_id": 1}
{"action": "get_chats", "userbot_id": 1, "user_id": 1}
{"action": "get_chats", "userbot_id": 1, "chat_list": "chatListArchive"}
{"action": "open_dialog", "userbot_id": 1, "from_message_id": "0", "chat_id": "123"}
{"action": "open_dialog", "userbot_id": 1, "from_message_id": "0", "chat_id": "123", "message_thread_id": "456"}

// Відправити повідомлення
{"action": "send_message", "userbot_id": 1, "chat_id": "123", "text": "Привіт"}

// Переслати повідомлення
{"action": "forward_message", "userbot_id": 1, "to_chat_ids": ["488386685", "456456685"], "from_chat_id": "2123445553", "message_ids": ["984071798784", "1231231231"]}

// Видалити повідомлення в чаті
{"action": "delete_messages", "userbot_id": 1, "chat_id": "456456685", "message_ids": ["984071798784"], "revoke": false}

// Reply на конкретне повідомлення
{"action": "reply_message", "userbot_id": 1, "chat_id": "2123445553", "reply_to_message_id": "984121081856", "text": "Відповідаю"}

// Редагувати повідомлення
{"action": "edit_message", "userbot_id": 1, "chat_id": "2123445553", "message_id": "984121081856", "text": "Оновлений текст"}

// Прикріпити повідомлення + only_for_self: true (повідомлення буде прикріплене лише для вас)
{"action": "pin_message", "userbot_id": 1, "chat_id": "2123445553", "message_id": "984121081856"}

// Відкріпити повідомлення
{"action": "unpin_message", "userbot_id": 1, "chat_id": "2123445553", "message_id": "984121081856"}

// Завантажити файл за file_id (наприклад, високу якість фото)
{"action": "download_file", "userbot_id": 1, "file_id": 123456, "chat_id": "2123445553", "msg_id": "984121081856"}

// Архівувати чат
{"action": "archive_chat", "userbot_id": 1, "chat_id": "123"}

// Повернути чат з архіву
{"action": "unarchive_chat", "userbot_id": 1, "chat_id": "123"}

// Видалити чат (прибрати зі списку) revoke = true (видалити історію для всіх), якщо remove_from_chat_list false, то просто очистить історію, але залишить чат у списку
{"action": "delete_chat", "userbot_id": 1, "chat_id": "123", "remove_from_chat_list": true, "revoke": true}

// Вийти з каналу/чату
{"action": "leave_chat", "userbot_id": 1, "chat_id": "123"}

// Додати чат у папку (added дефолтно true)
{"action": "chat_in_folder", "userbot_id": 1, "chat_id": "123", "chat_folder_id": 35}

// Видалити чат з папки (added вказувати false, removed=true)
{"action": "chat_in_folder", "userbot_id": 1, "chat_id": "123", "chat_folder_id": 35, "removed": true}

// Пін чату в папці (added можна не вказувати)
{"action": "chat_in_folder", "userbot_id": 1, "chat_id": "123", "chat_folder_id": 35, "pin": true}

// Анпін чату в папці (added можна не вказувати)
{"action": "chat_in_folder", "userbot_id": 1, "chat_id": "123", "chat_folder_id": 35, "unpin": true}

// Пін/анпін чату в списку (дефолтно main)
{"action": "pin_chat", "userbot_id": 1, "chat_id": "123", "is_pinned": true}

9. Потік даних

Команди від фронтенду

  1. React/SPA шле action у WebSocket (dialogs/ws).
  2. WS-консюмер публікує JSON у Redis-канал tg_commands.
  3. listen_for_commands() читає повідомлення, викликає TDLibHelper.
  4. Результат (або помилка) повертається через send_ws() у канал chat_list_updates.
  5. WS-консюмер відправляє payload назад клієнту.

Push-оновлення

  1. TDLib кидає updateNewMessage.
  2. handle_new_update парсить повідомлення через TDLibDialogService та формує payload із тими ж полями, що повертає open_dialog.
  3. Подія з userbot_id, unread_count та деталями месиджу публікується у chat_list_updates й летить на фронт.
  4. Якщо у повідомленні є файл/кастомне емодзі/іконка топіка, окремо приходить media_ready з метаданими (поступова доставка медіа).

10. Типові сценарії

Пагінація чатів

Беріть order і chat_id з останнього елемента відповіді. Передавайте їх як offset_order/offset_chat_id у наступному get_chats, аби TDLib видав наступний блок.

Завантаження медіа

TDLibHelper.download_file() викликає downloadFile+getFile, чекає завершення, зберігає у S3/MinIO та повертає presigned URL. Встановіть return_meta=True, якщо потрібно локальний шлях.

Парсинг історії

TDLibDialogService.get_dialog() приймає chat_id та from_message_id. Якщо історія порожня, поверне is_empty=True + дані чату.

Робота з кастомними емодзі

При першому використанні емодзі helper завантажує asset, зберігає у CustomEmoji, повертає JSON/WebM. Повторні звернення беруться з БД.

11. Рекомендації з розширення