mirror of
https://github.com/FerraSoft/bottohelp.git
synced 2026-08-06 21:55:03 +00:00
Подготовка к релизу
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
"""
|
||||
Объединенный маршрутизатор для всех типов обновлений.
|
||||
Интегрирует CommandRouter, MessageTypeRouter и ContextMenuManager.
|
||||
|
||||
Архитектура:
|
||||
- CommandRouter: обработка команд и callback'ов через middleware и проверки прав
|
||||
- MessageTypeRouter: маршрутизация сообщений по типам контента
|
||||
- ContextMenuManager: управление контекстными меню и проверка доступности
|
||||
|
||||
Алгоритм обработки callback'ов:
|
||||
1. Проверка роли пользователя и доступности меню
|
||||
2. Попытка обработки через CommandRouter (новая система)
|
||||
3. Fallback к MessageTypeRouter при неудаче
|
||||
|
||||
Преимущества:
|
||||
- Централизованная маршрутизация всех типов обновлений
|
||||
- Гибкая система middleware для команд
|
||||
- Поддержка ролевой модели доступа
|
||||
- Легкая расширяемость новыми типами обработчиков
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes
|
||||
from .command_router import CommandRouter
|
||||
from .message_router import MessageTypeRouter
|
||||
from .menu_manager import ContextMenuManager
|
||||
from .permissions import UserRole
|
||||
|
||||
|
||||
class UnifiedMessageRouter:
|
||||
"""
|
||||
Объединенный маршрутизатор для всех типов обновлений.
|
||||
|
||||
Обеспечивает:
|
||||
- Единый интерфейс для обработки всех типов сообщений
|
||||
- Интеграцию с существующими компонентами
|
||||
- Централизованное управление маршрутизацией
|
||||
- Логирование и мониторинг
|
||||
- Правильную обработку callback'ов через CommandRouter с fallback'ом
|
||||
"""
|
||||
|
||||
def __init__(self, command_router: CommandRouter,
|
||||
message_router: MessageTypeRouter,
|
||||
menu_manager: ContextMenuManager):
|
||||
"""
|
||||
Инициализация объединенного маршрутизатора.
|
||||
|
||||
Args:
|
||||
command_router: Маршрутизатор команд
|
||||
message_router: Маршрутизатор типов сообщений
|
||||
menu_manager: Менеджер контекстных меню
|
||||
"""
|
||||
self.command_router = command_router
|
||||
self.message_router = message_router
|
||||
self.menu_manager = menu_manager
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
async def handle_update(self, update: Update, context: ContextTypes) -> bool:
|
||||
"""
|
||||
Единая точка входа для обработки всех типов обновлений.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
context: Контекст бота
|
||||
|
||||
Returns:
|
||||
True если обновление было успешно обработано
|
||||
"""
|
||||
try:
|
||||
# Определение типа обновления и маршрутизация
|
||||
if update.message:
|
||||
return await self._handle_message_update(update, context)
|
||||
elif update.callback_query:
|
||||
return await self._handle_callback_update(update, context)
|
||||
elif update.inline_query:
|
||||
return await self._handle_inline_query(update, context)
|
||||
elif update.chosen_inline_result:
|
||||
return await self._handle_chosen_inline_result(update, context)
|
||||
elif update.chat_member:
|
||||
return await self._handle_chat_member_update(update, context)
|
||||
else:
|
||||
self.logger.debug("Unhandled update type")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error in unified router: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
async def _handle_message_update(self, update: Update, context: ContextTypes) -> bool:
|
||||
"""
|
||||
Обработка обновлений типа message.
|
||||
|
||||
Args:
|
||||
update: Обновление с сообщением
|
||||
context: Контекст бота
|
||||
|
||||
Returns:
|
||||
True если сообщение обработано
|
||||
"""
|
||||
message = update.message
|
||||
|
||||
# Проверка типа сообщения
|
||||
if message.text and message.text.startswith('/'):
|
||||
# Команды обрабатываются через CommandRouter
|
||||
self.logger.debug("Routing to command router")
|
||||
await self.command_router.handle_command(update, context)
|
||||
return True
|
||||
|
||||
elif message.text:
|
||||
# Текстовые сообщения через MessageTypeRouter
|
||||
self.logger.debug("Routing text message")
|
||||
return await self.message_router.route_text_message(update, context)
|
||||
|
||||
elif message.voice:
|
||||
# Голосовые сообщения
|
||||
self.logger.debug("Routing voice message")
|
||||
return await self.message_router.route_voice_message(update, context)
|
||||
|
||||
elif message.photo or message.video or message.audio or message.document:
|
||||
# Медиа-сообщения
|
||||
self.logger.debug("Routing media message")
|
||||
return await self.message_router.route_media_message(update, context)
|
||||
|
||||
else:
|
||||
self.logger.debug("Unhandled message type")
|
||||
return False
|
||||
|
||||
async def _handle_callback_update(self, update: Update, context: ContextTypes) -> bool:
|
||||
"""
|
||||
Обработка callback запросов с учетом контекста меню.
|
||||
|
||||
Алгоритм обработки:
|
||||
1. Получение роли пользователя для проверки прав доступа
|
||||
2. Проверка доступности меню (для запросов menu_*)
|
||||
3. Попытка обработки через CommandRouter (новая система маршрутизации)
|
||||
4. Fallback к MessageTypeRouter при неудаче
|
||||
|
||||
Args:
|
||||
update: Обновление с callback
|
||||
context: Контекст бота
|
||||
|
||||
Returns:
|
||||
True если callback обработан
|
||||
"""
|
||||
callback_data = update.callback_query.data
|
||||
self.logger.debug(f"Processing callback: {callback_data}")
|
||||
|
||||
# Получение роли пользователя
|
||||
user_role = await self._get_user_role(update)
|
||||
|
||||
# Проверка доступности меню (если это запрос меню)
|
||||
if callback_data.startswith('menu_'):
|
||||
if not self.menu_manager.is_menu_available(callback_data, user_role):
|
||||
await update.callback_query.answer("Недостаточно прав доступа")
|
||||
return True
|
||||
|
||||
# Сначала пытаемся маршрутизировать через CommandRouter (новая система)
|
||||
try:
|
||||
result = await self.command_router.handle_callback(update, context)
|
||||
if result:
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error in CommandRouter callback handler: {e}")
|
||||
|
||||
# Fallback к MessageTypeRouter
|
||||
return await self.message_router.route_callback(update, context)
|
||||
|
||||
async def _handle_inline_query(self, update: Update, context: ContextTypes) -> bool:
|
||||
"""
|
||||
Обработка инлайновых запросов.
|
||||
|
||||
Args:
|
||||
update: Инлайновый запрос
|
||||
context: Контекст бота
|
||||
|
||||
Returns:
|
||||
True если запрос обработан
|
||||
"""
|
||||
self.logger.debug("Processing inline query")
|
||||
return await self.message_router.route_inline_query(update, context)
|
||||
|
||||
async def _handle_chosen_inline_result(self, update: Update, context: ContextTypes) -> bool:
|
||||
"""
|
||||
Обработка выбора результата инлайнового запроса.
|
||||
|
||||
Args:
|
||||
update: Выбранный результат
|
||||
context: Контекст бота
|
||||
|
||||
Returns:
|
||||
True если результат обработан
|
||||
"""
|
||||
self.logger.debug("Processing chosen inline result")
|
||||
# Здесь может быть дополнительная логика обработки выбора
|
||||
return True
|
||||
|
||||
async def _handle_chat_member_update(self, update: Update, context: ContextTypes) -> bool:
|
||||
"""
|
||||
Обработка обновлений участников чата.
|
||||
|
||||
Args:
|
||||
update: Обновление участников чата
|
||||
context: Контекст бота
|
||||
|
||||
Returns:
|
||||
True если обновление обработано
|
||||
"""
|
||||
self.logger.debug("Processing chat member update")
|
||||
# Здесь может быть логика обработки вступления/выхода участников
|
||||
return True
|
||||
|
||||
async def _get_user_role(self, update: Update) -> UserRole:
|
||||
"""
|
||||
Получение роли пользователя для данного обновления.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
|
||||
Returns:
|
||||
Роль пользователя
|
||||
"""
|
||||
try:
|
||||
user = update.effective_user
|
||||
if user:
|
||||
# Использование существующей логики определения роли
|
||||
from .permissions import permission_manager
|
||||
# Попытка получить config из Application
|
||||
try:
|
||||
from .application import app_instance
|
||||
if app_instance and hasattr(app_instance, 'config'):
|
||||
config = app_instance.config
|
||||
else:
|
||||
# Создаем минимальный конфиг
|
||||
class MinimalConfig:
|
||||
def __init__(self):
|
||||
self.bot_config = MinimalBotConfig()
|
||||
|
||||
class MinimalBotConfig:
|
||||
def __init__(self):
|
||||
self.super_admin_ids = []
|
||||
self.admin_ids = []
|
||||
self.moderator_ids = []
|
||||
|
||||
config = MinimalConfig()
|
||||
except ImportError:
|
||||
# Создаем минимальный конфиг если Application недоступен
|
||||
class MinimalConfig:
|
||||
def __init__(self):
|
||||
self.bot_config = MinimalBotConfig()
|
||||
|
||||
class MinimalBotConfig:
|
||||
def __init__(self):
|
||||
self.super_admin_ids = []
|
||||
self.admin_ids = []
|
||||
self.moderator_ids = []
|
||||
|
||||
config = MinimalConfig()
|
||||
|
||||
return await permission_manager.get_effective_role(
|
||||
update, user.id, config
|
||||
)
|
||||
return UserRole.USER
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting user role: {e}")
|
||||
return UserRole.USER
|
||||
|
||||
async def get_menu_for_user(self, menu_id: str, user_role: UserRole,
|
||||
**context) -> Optional[object]:
|
||||
"""
|
||||
Получение меню для пользователя через ContextMenuManager.
|
||||
|
||||
Args:
|
||||
menu_id: Идентификатор меню
|
||||
user_role: Роль пользователя
|
||||
**context: Дополнительный контекст
|
||||
|
||||
Returns:
|
||||
Объект меню или None
|
||||
"""
|
||||
# Определение типа чата для контекстного построения меню
|
||||
chat_type = context.get('chat_type', 'private')
|
||||
context['chat_type'] = chat_type
|
||||
|
||||
return await self.menu_manager.get_menu_for_user(menu_id, user_role, **context)
|
||||
|
||||
def get_registered_handlers_count(self) -> dict:
|
||||
"""
|
||||
Получение статистики зарегистрированных обработчиков.
|
||||
|
||||
Returns:
|
||||
Словарь со статистикой
|
||||
"""
|
||||
command_handlers = len(self.command_router.command_handlers)
|
||||
callback_handlers = len(self.command_router.callback_handlers)
|
||||
message_handlers = self.message_router.get_registered_handlers_count()
|
||||
|
||||
return {
|
||||
'command_handlers': command_handlers,
|
||||
'callback_handlers': callback_handlers,
|
||||
'message_handlers': message_handlers,
|
||||
'total': command_handlers + callback_handlers + sum(message_handlers.values())
|
||||
}
|
||||
|
||||
def clear_menu_cache(self):
|
||||
"""Очистка кеша меню"""
|
||||
self.menu_manager.clear_cache()
|
||||
|
||||
|
||||
def create_unified_router(command_router: CommandRouter,
|
||||
message_router: MessageTypeRouter,
|
||||
menu_manager: ContextMenuManager) -> UnifiedMessageRouter:
|
||||
"""
|
||||
Создание объединенного маршрутизатора.
|
||||
|
||||
Args:
|
||||
command_router: Маршрутизатор команд
|
||||
message_router: Маршрутизатор типов сообщений
|
||||
menu_manager: Менеджер меню
|
||||
|
||||
Returns:
|
||||
UnifiedMessageRouter: Настроенный объединенный маршрутизатор
|
||||
"""
|
||||
return UnifiedMessageRouter(command_router, message_router, menu_manager)
|
||||
Reference in New Issue
Block a user