Перейти к содержанию

Интеграция с Telegram

0. Безопасное выкачивание файлов из Telegram (download_user_file)

Функция download_user_file защищает ваш бот от атак класса Path Traversal при скачивании файлов пользователя. Имя файла автоматизировано через chutils.fs.safe_filename и гарантированно не сможет выйти за рамки целевой директории.

from chutils.telegram import download_user_file

# Скачивание через aiogram bot
saved_path = await download_user_file(
    bot=bot,
    file_id=message.document.file_id,
    target_dir="./downloads",
    max_size_bytes=10 * 1024 * 1024,  # 10 MB лимит
)

0.1 Безопасная отправка файлов и папок (send_telegram_file)

Функция send_telegram_file отправляет локальные файлы или директории в Telegram с автоматической архивацией папок в ZIP, проверкой лимита 50 МБ и обрезкой длинных подписей (caption до 1024 символов).

from chutils.telegram import send_telegram_file

# Отправка одиночного файла
await send_telegram_file(
    bot=bot,
    chat_id=12345678,
    file_path="./reports/summary.pdf",
    caption="Ежемесячный отчёт",
)

# Отправка всей директории (автоматически запакуется в ZIP)
await send_telegram_file(
    bot="BOT_TOKEN_12345",
    chat_id=12345678,
    file_path="./logs_folder",
    caption="Архив логов сервера",
)

1. Проверка прав администратора (is_admin)

Функция is_admin проверяет, является ли пользователь администратором по Telegram ID или username.

from chutils.telegram import is_admin

# Проверка по явным спискам
if is_admin(user_id=12345678, admin_ids=[12345678, 87654321]):
    print("Доступ разрешен")

# Проверка по username (регистронезависимо, подгружает значки @)
if is_admin(username="alice", admin_usernames=["@alice", "bob"]):
    print("Привет, Алиса!")

Автоматический Fallback на конфигурацию chutils

Если явные списки admin_ids / admin_usernames не переданы, is_admin автоматически считывает разрешенные ID и юзернеймы из секции [Telegram] вашего конфигурационного файла (pyproject.toml / ai-lint.toml / .env):

[Telegram]
admin_ids = [12345678, 87654321]
admin_usernames = ["@admin", "owner"]

2. Декоратор @admin_only

Декоратор @admin_only поддерживает как синхронные, так и асинхронные функции-хэндлеры.

from chutils.telegram import admin_only
from chutils.exceptions import TelegramAccessDeniedError


# Асинхронный хэндлер с кастомным сообщением об отказе
@admin_only(
    admin_ids=[12345678], refusal_text="⛔ Функция доступна только администраторам"
)
async def secret_command(event):
    await event.answer("Секретные данные")


# Тихий режим (запросы игнорируются без вывода ответа)
@admin_only(admin_usernames=["admin"], silent=True)
async def quiet_handler(event):
    await event.answer("Тихая команда")


# Режим генерации исключения для централизованной обработки в Middleware
@admin_only(raise_on_denied=True)
def sync_handler(user_id: int):
    return "OK"

3. Интеграция с aiogram 3.x (AdminFilter)

AdminFilter наследуется от aiogram.filters.BaseFilter и позволяет использовать проверку прав непосредственно в роутерах и диспетчерах aiogram.

from aiogram import Router, types
from chutils.telegram import AdminFilter

router = Router()


@router.message(AdminFilter(admin_ids=[12345678]))
async def admin_panel(message: types.Message):
    await message.answer("Добро пожаловать в админ-панель!")

4. Защита от спама и флуда (@tg_rate_limit)

Декоратор @tg_rate_limit ограничивает частоту вызова команд с динамическим расчетом оставшегося времени ожидания {wait_sec}.

from chutils.telegram import tg_rate_limit


# Разрешить не более 2 вызовов в 10 секунд
@tg_rate_limit(
    rate=2, per=10.0, warning_text="⏱ Замедлитесь! Подождите {wait_sec} сек."
)
async def heavy_command(event):
    await event.answer("Тяжелый запрос выполнен!")

5. aiogram 3.x Middleware (TelegramThrottlingMiddleware)

Глобальное предотвращение спама на уровне роутеров/диспетчеров aiogram:

from aiogram import Dispatcher
from chutils.telegram import TelegramThrottlingMiddleware

dp = Dispatcher()
# Подключение мидлваря для всех текстовых сообщений
dp.message.middleware(TelegramThrottlingMiddleware(rate=1, per=2.0))

6. Белые и черные списки пользователей (AccessListManager и @allowed_only)

Менеджер AccessListManager управляет списками разрешенных и заблокированных пользователей с поддержкой автосохранения в JSON-файл (atomic_write):

from chutils.telegram import AccessListManager, allowed_only

manager = AccessListManager(storage_path="allowed_users.json")

# Динамическое управление пользователями
manager.allow_user("trusted_user")
manager.block_user(999888)


# Использование в декораторе
@allowed_only(manager=manager, refusal_text="⛔ У вас нет доступа")
async def restricted_feature(event):
    await event.answer("Доступ ограниченной группе предоставлен!")

7. aiogram 3.x SecretUserFilter (SecretUserFilter)

from aiogram import Router
from chutils.telegram import SecretUserFilter

router = Router()
# Фильтрация только разрешенных юзеров по ID
router.message.filter(SecretUserFilter(allowed_ids=[12345678, 87654321]))

8. Трейсинг и логирование апдейтов (trace_telegram_update)

Утилита trace_telegram_update замеряет точное время выполнения хэндлера (execution_time_ms) и автоматически логирует входящие события:

from chutils.telegram import trace_telegram_update


@trace_telegram_update()
async def process_user_request(event):
    # Код обработки запроса
    return "SUCCESS"

9. aiogram 3.x TelegramLoggingMiddleware (TelegramLoggingMiddleware)

Прозрачное сквозное логирование всех входящих событий через Middleware:

from aiogram import Dispatcher
from chutils.telegram import TelegramLoggingMiddleware

dp = Dispatcher()
dp.update.outer_middleware(TelegramLoggingMiddleware())

10. Безопасное экранирование (escape_markdown и escape_html)

Защищает от ошибок парсинга BadRequest: Can't parse entities:

from chutils.telegram import escape_markdown, escape_html

safe_mdv2 = escape_markdown("Цена: 100.00$ [link]", version=2)
safe_html = escape_html("<b>1 < 2 & 3 > 0</b>")

11. Обрезка и разбиение длинных сообщений (smart_truncate и split_message)

from chutils.telegram import smart_truncate, split_message

# Безопасная обрезка с запечатыванием незавершенного кодового блока ```
short_text = smart_truncate(long_code_str, max_length=1000)

# Разбиение текста по различным стратегиям: 'line', 'paragraph', 'word', 'char'
chunks_paragraphs = split_message(article_text, max_length=4096, mode="paragraph")
chunks_lines = split_message(huge_log_report, max_length=4096, mode="line")

for chunk in chunks_paragraphs:
    await message.answer(chunk)

12. Лог-хэндлер алертов в Telegram (TelegramLogHandler)

Автоматическая отправка ошибок уровня ERROR / CRITICAL в Telegram с троттлингом и подавлением транзиентных сбоев (флаппинга):

import logging
from chutils.telegram import TelegramLogHandler

logger = logging.getLogger("my_app")
handler = TelegramLogHandler(
    bot_token="TOKEN",
    chat_id=12345678,
    rate_limit_per_min=10,
    # Подавление ложных алертов (например, при ночных реконнектах LongPoll):
    flapping_patterns=["Unable to make request to BotPolling"],
    flapping_threshold=3,       # Отправит алерт только если сбой повторится 3 раза подряд
    flapping_timeout=60.0,      # или продлится непрерывно более 60 секунд
)
logger.addHandler(handler)

logger.error("Критический сбой базы данных!")

13. Мост алертов диагностики (HealthCheckAlertBridge & send_alert)

from chutils.telegram import send_alert, HealthCheckAlertBridge

# Прямая отправка алерта
send_alert(
    title="High CPU Usage", message="Загрузка процессора превысила 95%", level="WARNING"
)

# Мост алертов диагностики
bridge = HealthCheckAlertBridge()
bridge.on_health_check("PostgreSQL", "UNHEALTHY", {"error": "Connection timeout"})

14. Динамические Inline-клавиатуры (build_inline_keyboard)

Построение сетки кнопок из списков кортежей или словарей:

from chutils.telegram import build_inline_keyboard

buttons = [
    ("Купить", "buy_item_1"),
    ("Подробнее", "info_item_1"),
    {"text": "Сайт", "url": "https://example.com"},
]

# Возвращает структуру {'inline_keyboard': [...]} или aiogram InlineKeyboardMarkup (при as_aiogram=True)
keyboard = build_inline_keyboard(buttons, buttons_per_row=2, as_aiogram=True)

15. Пагинатор списков и каталогов (PaginatorKeyboard)

Автоматическая генерация панели навигации («, 1/5, ») с прикреплением футеров:

from chutils.telegram import PaginatorKeyboard

catalog_items = [f"Товар #{i}" for i in range(1, 50)]
paginator = PaginatorKeyboard(catalog_items, per_page=5, callback_prefix="catalog")

# Построение 2-й страницы с дополнительной кнопкой 'Закрыть'
kb = paginator.build_keyboard(
    page=2, footer_buttons=[("Закрыть", "close_catalog")], as_aiogram=True
)

16. Логирование критических ошибок в Telegram (TelegramLogHandler)

TelegramLogHandler направляет логи (по умолчанию уровня ERROR и выше) напрямую в указанный чат Telegram с поддержкой ограничения частоты (Rate Limiting) и подавления флэппинга:

import logging
from chutils.telegram import TelegramLogHandler

handler = TelegramLogHandler(
    bot_token="BOT_TOKEN",
    chat_id=12345678,
    level=logging.ERROR,
    rate_limit_per_min=10,
    flapping_threshold=3,
    flapping_timeout=60.0,
)

logger = logging.getLogger("my_app")
logger.addHandler(handler)