--- description: "Паттерны для создания handlers, services и обработки событий aiogram" globs: ["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 ```python from aiogram import Router # Создаем роутер router = Router() # Регистрируем middleware (если нужно) router.message.middleware(SomeMiddleware()) # Экспортируем в __init__.py # from .{module}_handlers import router ``` ## Структура Handler ```python 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 Бизнес-логика выносится в сервисы: ```python 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 (локально) ```python 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] ): ... ``` ### Через фабрики (для сервисов) ```python # В 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) ```python # Определение состояний в 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 и методам сервисов: ```python @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` для автоматической обработки (если есть)