Подготовка к релизу

This commit is contained in:
2025-10-30 18:28:26 +03:00
parent 69c0162c6b
commit 4981db90a0
191 changed files with 45557 additions and 3651 deletions
+451
View File
@@ -0,0 +1,451 @@
"""
Централизованный менеджер контекстных меню с учетом прав доступа.
Отвечает за создание и управление меню в зависимости от роли пользователя.
"""
import logging
from typing import Dict, List, Callable, Any, Optional
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from .permissions import UserRole, permission_manager
from utils.formatters import KeyboardFormatter
class MenuConfig:
"""Конфигурация меню"""
def __init__(self, menu_id: str, builder: Callable, required_role: UserRole,
level: str, description: str = ""):
self.menu_id = menu_id
self.builder = builder
self.required_role = required_role
self.level = level
self.description = description
class ContextMenuManager:
"""
Централизованный менеджер контекстных меню.
Обеспечивает:
- Регистрацию меню с учетом прав доступа
- Динамическое построение меню для пользователей
- Разделение меню по уровням доступа
- Переиспользование компонентов меню
"""
def __init__(self, permission_manager_instance, formatter: KeyboardFormatter):
"""
Инициализация менеджера меню.
Args:
permission_manager_instance: Экземпляр менеджера разрешений
formatter: Форматтер клавиатур
"""
self.permission_manager = permission_manager_instance
self.formatter = formatter
self.logger = logging.getLogger(__name__)
# Регистрация меню по ID
self.menus: Dict[str, MenuConfig] = {}
# Группировка меню по уровням доступа
self.menu_levels: Dict[str, List[str]] = {
'user': [],
'moderator': [],
'admin': [],
'super_admin': []
}
# Кеш построенных меню для оптимизации
self.menu_cache: Dict[str, InlineKeyboardMarkup] = {}
# Инициализация стандартных меню
self._initialize_default_menus()
def _initialize_default_menus(self):
"""Инициализация стандартных меню системы"""
# Меню уровня USER
self.register_menu(
'menu_main', self._build_main_menu, UserRole.USER, 'user',
'Главное меню бота'
)
self.register_menu(
'menu_help', self._build_help_menu, UserRole.USER, 'user',
'Меню помощи'
)
self.register_menu(
'menu_games', self._build_games_menu, UserRole.USER, 'user',
'Меню игр'
)
self.register_menu(
'menu_donate', self._build_donate_menu, UserRole.USER, 'user',
'Меню донатов'
)
self.register_menu(
'menu_leaderboard', self._build_leaderboard_menu, UserRole.USER, 'user',
'Таблица лидеров'
)
# Меню уровня MODERATOR
self.register_menu(
'menu_moderation', self._build_moderation_menu, UserRole.MODERATOR, 'moderator',
'Меню модерации'
)
self.register_menu(
'mod_warnings', self._build_warnings_menu, UserRole.MODERATOR, 'moderator',
'Управление предупреждениями'
)
self.register_menu(
'mod_mutes', self._build_mutes_menu, UserRole.MODERATOR, 'moderator',
'Управление заглушками'
)
self.register_menu(
'mod_stats', self._build_mod_stats_menu, UserRole.MODERATOR, 'moderator',
'Статистика модерации'
)
# Меню уровня ADMIN
self.register_menu(
'menu_admin', self._build_admin_menu, UserRole.ADMIN, 'admin',
'Меню администрирования'
)
self.register_menu(
'menu_triggers', self._build_triggers_menu, UserRole.ADMIN, 'admin',
'Управление триггерами'
)
self.register_menu(
'admin_users', self._build_admin_users_menu, UserRole.ADMIN, 'admin',
'Управление пользователями'
)
self.register_menu(
'admin_stats', self._build_admin_stats_menu, UserRole.ADMIN, 'admin',
'Статистика администрирования'
)
self.register_menu(
'admin_settings', self._build_admin_settings_menu, UserRole.ADMIN, 'admin',
'Настройки системы'
)
self.register_menu(
'admin_errors', self._build_admin_errors_menu, UserRole.ADMIN, 'admin',
'Отчеты об ошибках'
)
# Меню уровня SUPER_ADMIN
self.register_menu(
'admin_system', self._build_system_menu, UserRole.SUPER_ADMIN, 'super_admin',
'Системные настройки'
)
self.register_menu(
'admin_logs', self._build_logs_menu, UserRole.SUPER_ADMIN, 'super_admin',
'Системные логи'
)
self.register_menu(
'admin_restart', self._build_restart_menu, UserRole.SUPER_ADMIN, 'super_admin',
'Управление перезапуском'
)
def register_menu(self, menu_id: str, menu_builder: Callable, required_role: UserRole,
level: str, description: str = ""):
"""
Регистрация нового меню.
Args:
menu_id: Уникальный идентификатор меню
menu_builder: Функция-строитель меню
required_role: Минимальная требуемая роль
level: Уровень доступа меню
description: Описание меню
"""
menu_config = MenuConfig(menu_id, menu_builder, required_role, level, description)
self.menus[menu_id] = menu_config
if level not in self.menu_levels:
self.menu_levels[level] = []
self.menu_levels[level].append(menu_id)
self.logger.debug(f"Registered menu: {menu_id} (level: {level}, role: {required_role.value})")
async def get_menu_for_user(self, menu_id: str, user_role: UserRole,
**context) -> Optional[InlineKeyboardMarkup]:
"""
Получение меню для пользователя с учетом его роли.
Args:
menu_id: Идентификатор меню
user_role: Роль пользователя
**context: Дополнительный контекст для построения меню
Returns:
InlineKeyboardMarkup или None если доступ запрещен
"""
# Проверка доступности меню
if not self.is_menu_available(menu_id, user_role):
self.logger.warning(f"Menu {menu_id} not available for role {user_role.value}")
return None
# Получение конфигурации меню
menu_config = self.menus.get(menu_id)
if not menu_config:
self.logger.error(f"Menu configuration not found: {menu_id}")
return None
try:
# Построение меню
menu = await menu_config.builder(user_role, **context)
# Кеширование результата
cache_key = f"{menu_id}_{user_role.value}_{hash(str(context))}"
self.menu_cache[cache_key] = menu
return menu
except Exception as e:
self.logger.error(f"Error building menu {menu_id}: {e}")
return None
def is_menu_available(self, menu_id: str, user_role: UserRole) -> bool:
"""
Проверка доступности меню для роли пользователя.
Args:
menu_id: Идентификатор меню
user_role: Роль пользователя
Returns:
True если меню доступно
"""
menu_config = self.menus.get(menu_id)
if not menu_config:
return False
# Проверка иерархии ролей (USER < MODERATOR < ADMIN < SUPER_ADMIN)
role_hierarchy = {
UserRole.USER: 1,
UserRole.MODERATOR: 2,
UserRole.ADMIN: 3,
UserRole.SUPER_ADMIN: 4
}
user_level = role_hierarchy.get(user_role, 0)
required_level = role_hierarchy.get(menu_config.required_role, 999)
return user_level >= required_level
def get_available_menus_for_role(self, user_role: UserRole) -> List[str]:
"""
Получение списка доступных меню для роли.
Args:
user_role: Роль пользователя
Returns:
Список идентификаторов доступных меню
"""
available_menus = []
for menu_id, menu_config in self.menus.items():
if self.is_menu_available(menu_id, user_role):
available_menus.append(menu_id)
return sorted(available_menus)
def get_menus_by_level(self, level: str) -> List[str]:
"""
Получение меню определенного уровня.
Args:
level: Уровень доступа
Returns:
Список идентификаторов меню уровня
"""
return self.menu_levels.get(level, [])
def clear_cache(self):
"""Очистка кеша меню"""
self.menu_cache.clear()
self.logger.debug("Menu cache cleared")
# Методы построения стандартных меню
async def _build_main_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение главного меню"""
chat_type = context.get('chat_type', 'private')
return self.formatter.create_main_menu(user_role.value, chat_type)
async def _build_help_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню помощи"""
# Базовые кнопки для всех
keyboard = [
[InlineKeyboardButton("📋 Помощь", callback_data='menu_help')],
[InlineKeyboardButton("🎮 Игры", callback_data='menu_games')],
[InlineKeyboardButton("💰 Донат", callback_data='menu_donate')],
[InlineKeyboardButton("🏆 Лидеры", callback_data='menu_leaderboard')]
]
# Добавление кнопок в зависимости от роли
if user_role in [UserRole.MODERATOR, UserRole.ADMIN, UserRole.SUPER_ADMIN]:
keyboard.insert(1, [InlineKeyboardButton("🛡️ Модерация", callback_data='menu_moderation')])
if user_role in [UserRole.ADMIN, UserRole.SUPER_ADMIN]:
keyboard.insert(2, [InlineKeyboardButton("👑 Админ", callback_data='menu_admin')])
keyboard.append([InlineKeyboardButton("❌ Закрыть", callback_data='close_menu')])
return InlineKeyboardMarkup(keyboard)
async def _build_games_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню игр"""
chat_type = context.get('chat_type', 'private')
return self.formatter.create_games_menu(chat_type)
async def _build_donate_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню донатов"""
return self.formatter.create_donation_menu()
async def _build_leaderboard_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню таблицы лидеров"""
# Здесь будет логика получения и отображения лидеров
keyboard = [
[InlineKeyboardButton("📊 Общая таблица", callback_data='leaderboard_general')],
[InlineKeyboardButton("🎮 По играм", callback_data='leaderboard_games')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_main')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_moderation_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню модерации"""
return self.formatter.create_moderation_menu(user_role.value)
async def _build_warnings_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню предупреждений"""
keyboard = [
[InlineKeyboardButton("⚠️ Активные предупреждения", callback_data='warnings_active')],
[InlineKeyboardButton("📋 История", callback_data='warnings_history')],
[InlineKeyboardButton("🔍 Поиск пользователя", callback_data='warnings_search')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_moderation')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_mutes_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню заглушек"""
keyboard = [
[InlineKeyboardButton("🔇 Активные заглушки", callback_data='mutes_active')],
[InlineKeyboardButton("📋 История", callback_data='mutes_history')],
[InlineKeyboardButton("🔍 Поиск пользователя", callback_data='mutes_search')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_moderation')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_mod_stats_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню статистики модерации"""
keyboard = [
[InlineKeyboardButton("📊 Общая статистика", callback_data='mod_stats_general')],
[InlineKeyboardButton("👥 По пользователям", callback_data='mod_stats_users')],
[InlineKeyboardButton("📅 По периодам", callback_data='mod_stats_periods')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_moderation')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_admin_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню администрирования"""
return self.formatter.create_admin_menu(user_role.value)
async def _build_admin_users_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню управления пользователями"""
keyboard = [
[InlineKeyboardButton("👥 Список пользователей", callback_data='admin_users_list')],
[InlineKeyboardButton("🔍 Поиск пользователя", callback_data='admin_users_search')],
[InlineKeyboardButton("📊 Статистика пользователей", callback_data='admin_users_stats')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_admin_stats_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню статистики администрирования"""
keyboard = [
[InlineKeyboardButton("📊 Общая статистика", callback_data='admin_stats_general')],
[InlineKeyboardButton("🎮 Статистика игр", callback_data='admin_stats_games')],
[InlineKeyboardButton("💰 Статистика донатов", callback_data='admin_stats_donates')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_admin_settings_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню настроек"""
keyboard = [
[InlineKeyboardButton("🤖 Настройки бота", callback_data='admin_settings_bot')],
[InlineKeyboardButton("🛡️ Безопасность", callback_data='admin_settings_security')],
[InlineKeyboardButton("🔧 Технические настройки", callback_data='admin_settings_tech')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_admin_errors_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню отчетов об ошибках"""
keyboard = [
[InlineKeyboardButton("🐛 Последние ошибки", callback_data='admin_errors_recent')],
[InlineKeyboardButton("📊 Статистика ошибок", callback_data='admin_errors_stats')],
[InlineKeyboardButton("🔍 Поиск ошибок", callback_data='admin_errors_search')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_system_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню системных настроек"""
keyboard = [
[InlineKeyboardButton("⚙️ Конфигурация системы", callback_data='admin_system_config')],
[InlineKeyboardButton("💾 Резервное копирование", callback_data='admin_system_backup')],
[InlineKeyboardButton("🔄 Обновления", callback_data='admin_system_updates')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_logs_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню системных логов"""
keyboard = [
[InlineKeyboardButton("📋 Логи приложения", callback_data='admin_logs_app')],
[InlineKeyboardButton("🔗 Логи базы данных", callback_data='admin_logs_db')],
[InlineKeyboardButton("🌐 Логи API", callback_data='admin_logs_api')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_restart_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню управления перезапуском"""
keyboard = [
[InlineKeyboardButton("🔄 Мягкий перезапуск", callback_data='admin_restart_soft')],
[InlineKeyboardButton("💥 Полный перезапуск", callback_data='admin_restart_hard')],
[InlineKeyboardButton("⚠️ Экстренная остановка", callback_data='admin_restart_emergency')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
async def _build_triggers_menu(self, user_role: UserRole, **context) -> InlineKeyboardMarkup:
"""Построение меню управления триггерами"""
keyboard = [
[InlineKeyboardButton("📋 Список триггеров", callback_data='triggers_list')],
[InlineKeyboardButton(" Создать триггер", callback_data='triggers_create')],
[InlineKeyboardButton("📝 Шаблоны триггеров", callback_data='triggers_templates')],
[InlineKeyboardButton("📊 Статистика триггеров", callback_data='triggers_stats')],
[InlineKeyboardButton("⬅️ Назад", callback_data='menu_admin')]
]
return InlineKeyboardMarkup(keyboard)
# Глобальный экземпляр менеджера меню
menu_manager = None
def create_menu_manager(permission_manager_instance, formatter: KeyboardFormatter):
"""
Создание глобального экземпляра менеджера меню.
Args:
permission_manager_instance: Экземпляр менеджера разрешений
formatter: Форматтер клавиатур
Returns:
ContextMenuManager: Настроенный менеджер меню
"""
global menu_manager
menu_manager = ContextMenuManager(permission_manager_instance, formatter)
return menu_manager