Files
telegram-helper-bot/.cursor/rules/handlers-patterns.md
Andrey d2d7c83575 Обновлен Python до версии 3.11.9 и изменены зависимости в Dockerfile и pyproject.toml. Удалены устаревшие файлы RATE_LIMITING_SOLUTION.md и тесты для rate limiting.
Обновлены пути к библиотекам в Dockerfile для соответствия новой версии Python.
Исправлены все тесты, теперь все проходят
2026-01-25 16:07:27 +03:00

6.6 KiB
Raw Blame History

description, globs
description globs
Паттерны для создания handlers, services и обработки событий aiogram
helper_bot/handlers/**/*.py

Паттерны для Handlers

Структура модуля handler

Каждый модуль handler (admin, callback, group, private, voice) должен содержать:

handlers/{module}/
├── __init__.py          # Экспорт router
├── {module}_handlers.py # Основные handlers
├── services.py          # Бизнес-логика
├── exceptions.py        # Кастомные исключения
├── dependencies.py      # Dependency injection (опционально)
├── constants.py         # Константы (FSM states, messages)
└── utils.py             # Вспомогательные функции (опционально)

Создание Router

from aiogram import Router

# Создаем роутер
router = Router()

# Регистрируем middleware (если нужно)
router.message.middleware(SomeMiddleware())

# Экспортируем в __init__.py
# from .{module}_handlers import router

Структура Handler

from aiogram import Router, types
from aiogram.filters import Command, StateFilter
from aiogram.fsm.context import FSMContext
from helper_bot.filters.main import ChatTypeFilter
from logs.custom_logger import logger
from helper_bot.utils.metrics import track_time, track_errors

router = Router()

@router.message(
    ChatTypeFilter(chat_type=["private"]),
    Command('command_name')
)
@track_time("handler_name", "module_name")
@track_errors("module_name", "handler_name")
async def handler_function(
    message: types.Message,
    state: FSMContext,
    bot_db: AsyncBotDB,  # Из DependenciesMiddleware
    settings: dict,       # Из DependenciesMiddleware
    **kwargs
):
    """Описание handler."""
    try:
        # Логирование
        logger.info(f"Обработка команды от пользователя: {message.from_user.id}")
        
        # Получение данных из state (если нужно)
        data = await state.get_data()
        
        # Вызов сервиса для бизнес-логики
        service = SomeService(bot_db, settings)
        result = await service.do_something()
        
        # Ответ пользователю
        await message.answer("Результат")
        
        # Обновление state (если нужно)
        await state.set_state("NEW_STATE")
        
    except SomeCustomException as e:
        # Обработка кастомных исключений
        await message.answer(f"Ошибка: {str(e)}")
        logger.error(f"Ошибка в handler: {e}")
    except Exception as e:
        # Обработка общих ошибок
        await handle_error(message, e, state)
        logger.error(f"Неожиданная ошибка: {e}")

Service Layer

Бизнес-логика выносится в сервисы:

from logs.custom_logger import logger
from helper_bot.utils.metrics import track_time, track_errors

class SomeService:
    """Сервис для работы с ..."""
    
    def __init__(self, bot_db: AsyncBotDB, settings: dict):
        self.bot_db = bot_db
        self.settings = settings
    
    @track_time("method_name", "service_name")
    @track_errors("service_name", "method_name")
    async def do_something(self) -> SomeResult:
        """Описание метода."""
        try:
            # Работа с БД через bot_db
            data = await self.bot_db.some_method()
            
            # Бизнес-логика
            result = self._process_data(data)
            
            return result
        except Exception as e:
            logger.error(f"Ошибка в сервисе: {e}")
            raise

Dependency Injection

Через DependenciesMiddleware (глобально)

  • bot_db: AsyncBotDB - доступен во всех handlers
  • settings: dict - настройки из .env
  • bot: Bot - экземпляр бота
  • dp: Dispatcher - dispatcher

Через MagicData (локально)

from aiogram.filters import MagicData
from typing import Annotated
from helper_bot.handlers.admin.dependencies import BotDB, Settings

@router.message(
    Command('admin'),
    MagicData(bot_db=BotDB, settings=Settings)
)
async def handler(
    message: types.Message,
    bot_db: Annotated[AsyncBotDB, BotDB],
    settings: Annotated[dict, Settings]
):
    ...

Через фабрики (для сервисов)

# В dependencies.py
def get_some_service() -> SomeService:
    """Фабрика для SomeService"""
    bdf = get_global_instance()
    db = bdf.get_db()
    settings = bdf.settings
    return SomeService(db, settings)

# В handlers
@router.message(Command('cmd'))
async def handler(
    message: types.Message,
    service: Annotated[SomeService, get_some_service()]
):
    ...

FSM (Finite State Machine)

# Определение состояний в constants.py
FSM_STATES = {
    "ADMIN": "ADMIN",
    "AWAIT_INPUT": "AWAIT_INPUT",
    ...
}

# Установка состояния
await state.set_state(FSM_STATES["ADMIN"])

# Получение состояния
current_state = await state.get_state()

# Сохранение данных
await state.update_data(key=value)

# Получение данных
data = await state.get_data()
value = data.get("key")

# Очистка состояния
await state.clear()

Фильтры

Используйте кастомные фильтры из helper_bot.filters.main:

  • ChatTypeFilter - фильтр по типу чата (private, group, supergroup)

Декораторы для метрик

Всегда добавляйте декораторы метрик к handlers и методам сервисов:

@track_time("handler_name", "module_name")  # Измерение времени выполнения
@track_errors("module_name", "handler_name")  # Отслеживание ошибок
@db_query_time("method_name", "table_name", "operation")  # Для БД операций

Обработка ошибок

  • Используйте кастомные исключения из exceptions.py
  • Обрабатывайте исключения в handlers
  • Логируйте все ошибки через logger.error()
  • Используйте декоратор @error_handler для автоматической обработки (если есть)