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,335 @@
|
||||
"""
|
||||
Единый маршрутизатор команд для централизованной обработки.
|
||||
Отвечает за маршрутизацию команд к соответствующим обработчикам.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Callable, Awaitable, Any, Optional
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes
|
||||
from .middleware import create_middleware_chain
|
||||
from .permissions import permission_manager, UserRole
|
||||
|
||||
|
||||
class CommandHandler:
|
||||
"""
|
||||
Описание обработчика команды с метаданными.
|
||||
"""
|
||||
|
||||
def __init__(self, handler: Callable, required_role: UserRole = UserRole.USER,
|
||||
description: str = "", category: str = "general"):
|
||||
self.handler = handler
|
||||
self.required_role = required_role
|
||||
self.description = description
|
||||
self.category = category
|
||||
|
||||
|
||||
class CommandRouter:
|
||||
"""
|
||||
Центральный маршрутизатор команд бота.
|
||||
|
||||
Обеспечивает:
|
||||
- Регистрацию обработчиков команд
|
||||
- Автоматическую маршрутизацию
|
||||
- Проверку прав доступа
|
||||
- Обработку через middleware
|
||||
"""
|
||||
|
||||
def __init__(self, config, metrics=None):
|
||||
"""
|
||||
Инициализация маршрутизатора.
|
||||
|
||||
Args:
|
||||
config: Конфигурация приложения
|
||||
metrics: Сборщик метрик
|
||||
"""
|
||||
self.config = config
|
||||
self.metrics = metrics
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
# Создаем цепочку middleware
|
||||
self.middleware_chain = create_middleware_chain(config, metrics)
|
||||
|
||||
# Регистрируемые обработчики
|
||||
self.command_handlers: Dict[str, CommandHandler] = {}
|
||||
self.callback_handlers: Dict[str, CommandHandler] = {}
|
||||
self.message_handlers: Dict[str, CommandHandler] = {}
|
||||
|
||||
# Категории команд
|
||||
self.categories = {
|
||||
'user': 'Пользовательские команды',
|
||||
'moderation': 'Модерация',
|
||||
'admin': 'Администрирование',
|
||||
'system': 'Системные команды',
|
||||
'games': 'Игры'
|
||||
}
|
||||
|
||||
def register_command_handler(self, command: str, handler: Callable,
|
||||
required_role: UserRole = UserRole.USER,
|
||||
description: str = "", category: str = "user"):
|
||||
"""
|
||||
Регистрация обработчика команды.
|
||||
|
||||
Args:
|
||||
command: Название команды (без слеша)
|
||||
handler: Функция обработчик
|
||||
required_role: Минимальная требуемая роль
|
||||
description: Описание команды
|
||||
category: Категория команды
|
||||
"""
|
||||
self.command_handlers[command] = CommandHandler(
|
||||
handler=handler,
|
||||
required_role=required_role,
|
||||
description=description,
|
||||
category=category
|
||||
)
|
||||
self.logger.debug(f"Registered command handler: /{command} -> {handler.__name__}")
|
||||
|
||||
def register_callback_handler(self, callback_data: str, handler: Callable,
|
||||
required_role: UserRole = UserRole.USER,
|
||||
description: str = "", category: str = "user"):
|
||||
"""
|
||||
Регистрация обработчика callback запросов.
|
||||
|
||||
Args:
|
||||
callback_data: Callback data
|
||||
handler: Функция обработчик
|
||||
required_role: Минимальная требуемая роль
|
||||
description: Описание
|
||||
category: Категория
|
||||
"""
|
||||
self.callback_handlers[callback_data] = CommandHandler(
|
||||
handler=handler,
|
||||
required_role=required_role,
|
||||
description=description,
|
||||
category=category
|
||||
)
|
||||
|
||||
def register_message_handler(self, message_type: str, handler: Callable,
|
||||
required_role: UserRole = UserRole.USER,
|
||||
description: str = "", category: str = "user"):
|
||||
"""
|
||||
Регистрация обработчика сообщений.
|
||||
|
||||
Args:
|
||||
message_type: Тип сообщения (text, voice, audio, etc.)
|
||||
handler: Функция обработчик
|
||||
required_role: Минимальная требуемая роль
|
||||
description: Описание
|
||||
category: Категория
|
||||
"""
|
||||
self.message_handlers[message_type] = CommandHandler(
|
||||
handler=handler,
|
||||
required_role=required_role,
|
||||
description=description,
|
||||
category=category
|
||||
)
|
||||
|
||||
async def handle_command(self, update: Update, context: ContextTypes):
|
||||
"""
|
||||
Обработка команды через middleware и маршрутизацию.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
context: Контекст бота
|
||||
"""
|
||||
# Извлекаем команду из сообщения
|
||||
command = self._extract_command(update)
|
||||
if not command:
|
||||
return
|
||||
|
||||
# Находим обработчик
|
||||
handler_info = self.command_handlers.get(command)
|
||||
if not handler_info:
|
||||
await self._send_unknown_command(update, command)
|
||||
return
|
||||
|
||||
# Создаем обертку обработчика с проверкой роли
|
||||
async def wrapped_handler(update: Update, context: ContextTypes):
|
||||
await handler_info.handler(update, context)
|
||||
|
||||
# Обрабатываем через middleware
|
||||
await self.middleware_chain.process_request(update, context, wrapped_handler)
|
||||
|
||||
async def handle_callback(self, update: Update, context: ContextTypes):
|
||||
"""
|
||||
Обработка callback запросов.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
context: Контекст бота
|
||||
"""
|
||||
if not update.callback_query:
|
||||
return
|
||||
|
||||
callback_data = update.callback_query.data
|
||||
if not callback_data:
|
||||
return
|
||||
|
||||
self.logger.debug(f"Processing callback: {callback_data}")
|
||||
|
||||
# Находим обработчик (точное совпадение или префикс)
|
||||
handler_info = None
|
||||
|
||||
# Сначала ищем точное совпадение
|
||||
if callback_data in self.callback_handlers:
|
||||
handler_info = self.callback_handlers[callback_data]
|
||||
self.logger.debug(f"Found exact match for callback: {callback_data}")
|
||||
else:
|
||||
# Ищем по префиксу
|
||||
for prefix, handler in self.callback_handlers.items():
|
||||
if callback_data.startswith(prefix):
|
||||
handler_info = handler
|
||||
self.logger.debug(f"Found prefix match for callback: {callback_data} -> {prefix}")
|
||||
break
|
||||
|
||||
if not handler_info:
|
||||
self.logger.warning(f"No handler found for callback: {callback_data}")
|
||||
await update.callback_query.answer("Неизвестное действие")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Found handler for callback {callback_data}: {handler_info.handler.__name__}")
|
||||
|
||||
# Создаем обертку обработчика
|
||||
async def wrapped_handler(update: Update, context: ContextTypes):
|
||||
await handler_info.handler(update, context)
|
||||
|
||||
# Обрабатываем через middleware
|
||||
await self.middleware_chain.process_request(update, context, wrapped_handler)
|
||||
|
||||
async def handle_message(self, update: Update, context: ContextTypes, message_type: str = "text"):
|
||||
"""
|
||||
Обработка сообщений.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
context: Контекст бота
|
||||
message_type: Тип сообщения
|
||||
"""
|
||||
handler_info = self.message_handlers.get(message_type)
|
||||
if not handler_info:
|
||||
return
|
||||
|
||||
# Создаем обертку обработчика
|
||||
async def wrapped_handler(update: Update, context: ContextTypes):
|
||||
await handler_info.handler(update, context)
|
||||
|
||||
# Обрабатываем через middleware
|
||||
await self.middleware_chain.process_request(update, context, wrapped_handler)
|
||||
|
||||
def _extract_command(self, update: Update) -> Optional[str]:
|
||||
"""
|
||||
Извлечение команды из обновления.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
|
||||
Returns:
|
||||
Название команды или None
|
||||
"""
|
||||
if not update.message or not update.message.text:
|
||||
return None
|
||||
|
||||
text = update.message.text.strip()
|
||||
if not text.startswith('/'):
|
||||
return None
|
||||
|
||||
# Извлекаем команду (убираем слеш и username бота)
|
||||
command = text.split()[0][1:].split('@')[0]
|
||||
return command
|
||||
|
||||
async def _send_unknown_command(self, update: Update, command: str):
|
||||
"""
|
||||
Отправка сообщения о неизвестной команде.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
command: Название неизвестной команды
|
||||
"""
|
||||
try:
|
||||
message = f"❌ Неизвестная команда: /{command}\n\nИспользуйте /help для списка доступных команд."
|
||||
if update.message:
|
||||
await update.message.reply_text(message)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to send unknown command message: {e}")
|
||||
|
||||
def get_registered_commands(self) -> Dict[str, Dict[str, Any]]:
|
||||
"""
|
||||
Получение списка зарегистрированных команд сгруппированных по категориям.
|
||||
|
||||
Returns:
|
||||
Словарь категорий с командами
|
||||
"""
|
||||
result = {}
|
||||
|
||||
for category, category_name in self.categories.items():
|
||||
result[category] = {
|
||||
'name': category_name,
|
||||
'commands': {}
|
||||
}
|
||||
|
||||
for command, handler_info in self.command_handlers.items():
|
||||
category = handler_info.category
|
||||
if category not in result:
|
||||
result[category] = {'name': category, 'commands': {}}
|
||||
|
||||
result[category]['commands'][command] = {
|
||||
'description': handler_info.description,
|
||||
'required_role': handler_info.required_role.value
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def get_available_commands_for_role(self, user_role: UserRole) -> List[str]:
|
||||
"""
|
||||
Получение списка команд доступных для роли.
|
||||
|
||||
Args:
|
||||
user_role: Роль пользователя
|
||||
|
||||
Returns:
|
||||
Список доступных команд
|
||||
"""
|
||||
available_commands = []
|
||||
|
||||
for command, handler_info in self.command_handlers.items():
|
||||
if permission_manager.can_execute_command(user_role, command):
|
||||
available_commands.append(command)
|
||||
|
||||
return sorted(available_commands)
|
||||
|
||||
def get_command_info(self, command: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Получение информации о команде.
|
||||
|
||||
Args:
|
||||
command: Название команды
|
||||
|
||||
Returns:
|
||||
Информация о команде или None
|
||||
"""
|
||||
handler_info = self.command_handlers.get(command)
|
||||
if not handler_info:
|
||||
return None
|
||||
|
||||
return {
|
||||
'command': command,
|
||||
'description': handler_info.description,
|
||||
'required_role': handler_info.required_role.value,
|
||||
'category': handler_info.category
|
||||
}
|
||||
|
||||
|
||||
# Функция для создания настроенного маршрутизатора
|
||||
def create_command_router(config, metrics=None):
|
||||
"""
|
||||
Создание и настройка маршрутизатора команд.
|
||||
|
||||
Args:
|
||||
config: Конфигурация приложения
|
||||
metrics: Сборщик метрик
|
||||
|
||||
Returns:
|
||||
CommandRouter: Настроенный маршрутизатор
|
||||
"""
|
||||
return CommandRouter(config, metrics)
|
||||
Reference in New Issue
Block a user