From 6b24388faa71c4b0582b52abc812a219213752e5 Mon Sep 17 00:00:00 2001 From: "Andrew K. Choi" Date: Wed, 18 Feb 2026 13:28:29 +0900 Subject: [PATCH] feat: Allow assigned admins to access admin panel via command and buttons - Modified check_admin_access() to check both super admins (.env) and assigned admins (DB) - Updated /admin command handler to support both admin types - Replaced all is_admin() checks with async check_admin_access() in admin panel - Assigned admins can now use /admin command and navigate via buttons - Super admin check (is_super_admin) remains unchanged for admin management - Added proper async/await for database queries in all admin checks --- .gitignore | 1 + main.py | 21 +++- src/handlers/admin_panel.py | 231 +++++++++++++++++++----------------- 3 files changed, 146 insertions(+), 107 deletions(-) diff --git a/.gitignore b/.gitignore index e9bb22d..c0b2162 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ venv.bak/ # Системные файлы .DS_Store Thumbs.db.bot.pid +*.bak diff --git a/main.py b/main.py index 2506ae7..89bf5ad 100644 --- a/main.py +++ b/main.py @@ -192,7 +192,26 @@ async def btn_main_menu(message: Message): async def cmd_admin(message: Message): """Обработчик команды /admin (регистронезависимо) - перенаправляет в admin_panel""" from src.core.config import ADMIN_IDS - if message.from_user.id not in ADMIN_IDS: + from src.core.database import async_session_maker + from src.core.models import User + from sqlalchemy import select + + # Проверяем, является ли пользователь главным администратором из .env + user_id = message.from_user.id + is_super_admin = user_id in ADMIN_IDS + + # Проверяем, является ли пользователь назначенным администратором + is_assigned_admin = False + if not is_super_admin: + async with async_session_maker() as session: + user = await session.execute( + select(User).where(User.telegram_id == user_id) + ) + user = user.scalar_one_or_none() + is_assigned_admin = user and user.is_admin + + # Если не администратор ни того, ни другого типа + if not (is_super_admin or is_assigned_admin): await message.answer("❌ Недостаточно прав для доступа к админ панели") return diff --git a/src/handlers/admin_panel.py b/src/handlers/admin_panel.py index cd3e7e5..84e36f7 100644 --- a/src/handlers/admin_panel.py +++ b/src/handlers/admin_panel.py @@ -109,7 +109,7 @@ admin_router = Router() def is_admin(user_id: int) -> bool: - """Проверка прав администратора""" + """Проверка прав администратора (быстрая проверка только .env)""" return user_id in ADMIN_IDS @@ -118,6 +118,25 @@ def is_super_admin(user_id: int) -> bool: return user_id in ADMIN_IDS +async def check_admin_access(user_id: int) -> bool: + """ + Асинхронная проверка доступа администратора. + Проверяет как главных администраторов (.env), так и назначенных (БД) + """ + # Сначала проверяем главных администраторов + if user_id in ADMIN_IDS: + return True + + # Затем проверяем назначенных администраторов в БД + async with async_session_maker() as session: + from sqlalchemy import select + result = await session.execute( + select(User).where(User.telegram_id == user_id, User.is_admin == True) + ) + user = result.scalar_one_or_none() + return user is not None + + def get_admin_main_keyboard() -> InlineKeyboardMarkup: """Главная админ-панель""" buttons = [ @@ -178,7 +197,7 @@ def get_winner_management_keyboard() -> InlineKeyboardMarkup: @admin_router.callback_query(F.data == "admin_panel") async def show_admin_panel(callback: CallbackQuery): """Показать админ-панель""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -213,7 +232,7 @@ async def show_admin_panel(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_lotteries") async def show_lottery_management(callback: CallbackQuery): """Управление розыгрышами""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -232,7 +251,7 @@ async def start_create_lottery(callback: CallbackQuery, state: FSMContext): # Сразу отвечаем на callback await callback.answer() - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): logging.warning(f"⚠️ Пользователь {callback.from_user.id} не является админом") await callback.message.answer("❌ Недостаточно прав") return @@ -260,7 +279,7 @@ async def start_create_lottery(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.lottery_title)) async def process_lottery_title(message: Message, state: FSMContext): """Обработка названия розыгрыша (создание или редактирование)""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -308,7 +327,7 @@ async def process_lottery_title(message: Message, state: FSMContext): @admin_router.message(StateFilter(AdminStates.lottery_description)) async def process_lottery_description(message: Message, state: FSMContext): """Обработка описания розыгрыша (создание или редактирование)""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -366,7 +385,7 @@ async def process_lottery_description(message: Message, state: FSMContext): @admin_router.message(StateFilter(AdminStates.lottery_prizes)) async def process_lottery_prizes(message: Message, state: FSMContext): """Обработка призов розыгрыша (создание или редактирование)""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -429,7 +448,7 @@ async def process_lottery_prizes(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "confirm_create_lottery", StateFilter(AdminStates.lottery_confirm)) async def confirm_create_lottery(callback: CallbackQuery, state: FSMContext): """Подтверждение создания розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -472,7 +491,7 @@ async def confirm_create_lottery(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_list_all_lotteries") async def list_all_lotteries(callback: CallbackQuery): """Список всех розыгрышей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -522,7 +541,7 @@ async def list_all_lotteries(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_lottery_detail_")) async def show_lottery_detail(callback: CallbackQuery): """Детальная информация о розыгрыше""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -604,7 +623,7 @@ async def show_lottery_detail(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_participants") async def show_participant_management(callback: CallbackQuery): """Управление участниками""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -618,7 +637,7 @@ async def show_participant_management(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_participants_")) async def show_lottery_participants(callback: CallbackQuery): """Показать участников конкретного розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -670,7 +689,7 @@ async def show_lottery_participants(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_bulk_operations") async def show_bulk_operations_menu(callback: CallbackQuery): """Подменю массовых операций""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -697,7 +716,7 @@ async def show_bulk_operations_menu(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_add_participant") async def start_add_participant(callback: CallbackQuery, state: FSMContext): """Начать добавление участника""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -735,7 +754,7 @@ async def start_add_participant(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_add_part_to_")) async def choose_user_to_add(callback: CallbackQuery, state: FSMContext): """Выбор пользователя для добавления""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -763,7 +782,7 @@ async def choose_user_to_add(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.add_participant_user)) async def process_add_participant(message: Message, state: FSMContext): """Обработка добавления участника""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -818,7 +837,7 @@ async def process_add_participant(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_remove_participant") async def remove_participant_start(callback: CallbackQuery): """Начало процесса удаления участника""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -853,7 +872,7 @@ async def remove_participant_start(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_remove_part_from_")) async def remove_participant_select_lottery(callback: CallbackQuery, state: FSMContext): """Выбор розыгрыша для удаления участника""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -884,7 +903,7 @@ async def remove_participant_select_lottery(callback: CallbackQuery, state: FSMC @admin_router.message(StateFilter(AdminStates.remove_participant_user)) async def process_remove_participant(message: Message, state: FSMContext): """Обработка удаления участника""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -949,7 +968,7 @@ async def process_remove_participant(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_list_all_participants") async def list_all_participants(callback: CallbackQuery): """Список всех участников""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -998,7 +1017,7 @@ async def list_all_participants(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_participants_report") async def generate_participants_report(callback: CallbackQuery): """Генерация отчета по участникам""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1083,7 +1102,7 @@ async def generate_participants_report(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_export_participants") async def export_participants_data(callback: CallbackQuery): """Экспорт данных участников""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1136,7 +1155,7 @@ async def export_participants_data(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_search_participants") async def start_search_participants(callback: CallbackQuery, state: FSMContext): """Начать поиск участников""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1159,7 +1178,7 @@ async def start_search_participants(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.participant_search)) async def process_search_participants(message: Message, state: FSMContext): """Обработка поиска участников""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -1210,7 +1229,7 @@ async def process_search_participants(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_bulk_add_participant") async def start_bulk_add_participant(callback: CallbackQuery, state: FSMContext): """Начать массовое добавление участников""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1248,7 +1267,7 @@ async def start_bulk_add_participant(callback: CallbackQuery, state: FSMContext) @admin_router.callback_query(F.data.startswith("admin_bulk_add_to_")) async def choose_users_bulk_add(callback: CallbackQuery, state: FSMContext): """Выбор пользователей для массового добавления""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1277,7 +1296,7 @@ async def choose_users_bulk_add(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.add_participant_bulk)) async def process_bulk_add_participant(message: Message, state: FSMContext): """Обработка массового добавления участников""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -1331,7 +1350,7 @@ async def process_bulk_add_participant(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_bulk_remove_participant") async def start_bulk_remove_participant(callback: CallbackQuery, state: FSMContext): """Начать массовое удаление участников""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1379,7 +1398,7 @@ async def start_bulk_remove_participant(callback: CallbackQuery, state: FSMConte @admin_router.callback_query(F.data.startswith("admin_bulk_remove_from_")) async def choose_users_bulk_remove(callback: CallbackQuery, state: FSMContext): """Выбор пользователей для массового удаления""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1408,7 +1427,7 @@ async def choose_users_bulk_remove(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.remove_participant_bulk)) async def process_bulk_remove_participant(message: Message, state: FSMContext): """Обработка массового удаления участников""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -1466,7 +1485,7 @@ async def process_bulk_remove_participant(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_bulk_add_accounts") async def start_bulk_add_accounts(callback: CallbackQuery, state: FSMContext): """Начать массовое добавление участников по номерам счетов""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1504,7 +1523,7 @@ async def start_bulk_add_accounts(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_bulk_add_accounts_to_")) async def choose_accounts_bulk_add(callback: CallbackQuery, state: FSMContext): """Выбор номеров счетов для массового добавления""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1535,7 +1554,7 @@ async def choose_accounts_bulk_add(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.add_participant_bulk_accounts)) async def process_bulk_add_accounts(message: Message, state: FSMContext): """Обработка массового добавления участников по номерам счетов""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -1592,7 +1611,7 @@ async def process_bulk_add_accounts(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_bulk_remove_accounts") async def start_bulk_remove_accounts(callback: CallbackQuery, state: FSMContext): """Начать массовое удаление участников по номерам счетов""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1630,7 +1649,7 @@ async def start_bulk_remove_accounts(callback: CallbackQuery, state: FSMContext) @admin_router.callback_query(F.data.startswith("admin_bulk_remove_accounts_from_")) async def choose_accounts_bulk_remove(callback: CallbackQuery, state: FSMContext): """Выбор номеров счетов для массового удаления""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1660,7 +1679,7 @@ async def choose_accounts_bulk_remove(callback: CallbackQuery, state: FSMContext @admin_router.message(StateFilter(AdminStates.remove_participant_bulk_accounts)) async def process_bulk_remove_accounts(message: Message, state: FSMContext): """Обработка массового удаления участников по номерам счетов""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -1721,7 +1740,7 @@ async def process_bulk_remove_accounts(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_participants_by_lottery") async def show_participants_by_lottery(callback: CallbackQuery): """Показать участников по розыгрышам""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1765,7 +1784,7 @@ async def show_participants_by_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_participants_report") async def show_participants_report(callback: CallbackQuery): """Отчет по участникам""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1832,7 +1851,7 @@ async def show_participants_report(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_edit_lottery") async def start_edit_lottery(callback: CallbackQuery, state: FSMContext): """Начать редактирование розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1869,7 +1888,7 @@ async def start_edit_lottery(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_edit_field_")) async def handle_edit_field(callback: CallbackQuery, state: FSMContext): """Обработка выбора поля для редактирования""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1910,7 +1929,7 @@ async def handle_edit_field(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_edit_")) async def redirect_to_edit_lottery(callback: CallbackQuery, state: FSMContext): """Редирект на редактирование розыгрыша из детального просмотра""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1931,7 +1950,7 @@ async def redirect_to_edit_lottery(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_edit_lottery_select_")) async def choose_edit_field(callback: CallbackQuery, state: FSMContext): """Выбор поля для редактирования""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1967,7 +1986,7 @@ async def choose_edit_field(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_toggle_active_")) async def toggle_lottery_active(callback: CallbackQuery, state: FSMContext): """Переключить активность розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -1993,7 +2012,7 @@ async def toggle_lottery_active(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_finish_lottery") async def start_finish_lottery(callback: CallbackQuery): """Завершить розыгрыш""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2031,7 +2050,7 @@ async def start_finish_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_confirm_finish_")) async def confirm_finish_lottery(callback: CallbackQuery): """Подтвердить завершение розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2060,7 +2079,7 @@ async def confirm_finish_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_do_finish_")) async def do_finish_lottery(callback: CallbackQuery): """Выполнить завершение розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2091,7 +2110,7 @@ async def do_finish_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_delete_lottery") async def start_delete_lottery(callback: CallbackQuery): """Удаление розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2131,7 +2150,7 @@ async def start_delete_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_confirm_delete_")) async def confirm_delete_lottery(callback: CallbackQuery): """Подтвердить удаление розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2163,7 +2182,7 @@ async def confirm_delete_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_do_delete_")) async def do_delete_lottery(callback: CallbackQuery): """Выполнить удаление розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2200,7 +2219,7 @@ async def do_delete_lottery(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_winners") async def show_winner_management(callback: CallbackQuery): """Управление победителями""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2214,7 +2233,7 @@ async def show_winner_management(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_set_manual_winner") async def start_set_manual_winner(callback: CallbackQuery, state: FSMContext): """Начать установку ручного победителя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2258,7 +2277,7 @@ async def start_set_manual_winner(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_set_winner_")) async def handle_set_winner_from_lottery(callback: CallbackQuery, state: FSMContext): """Обработчик для кнопки 'Установить победителя' из карточки розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2272,7 +2291,7 @@ async def handle_set_winner_from_lottery(callback: CallbackQuery, state: FSMCont @admin_router.callback_query(F.data.startswith("admin_choose_winner_lottery_")) async def choose_winner_place(callback: CallbackQuery, state: FSMContext): """Выбор места для победителя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2309,7 +2328,7 @@ async def choose_winner_place(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.set_winner_place)) async def process_winner_place(message: Message, state: FSMContext): """Обработка места победителя""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -2357,7 +2376,7 @@ async def process_winner_place(message: Message, state: FSMContext): @admin_router.message(StateFilter(AdminStates.set_winner_user)) async def process_winner_user(message: Message, state: FSMContext): """Обработка пользователя-победителя (по ID, username или номеру счета)""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): await message.answer("❌ Недостаточно прав") return @@ -2457,7 +2476,7 @@ async def process_winner_user(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_list_winners") async def list_all_winners(callback: CallbackQuery): """Список всех победителей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2524,7 +2543,7 @@ async def list_all_winners(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_edit_winner") async def edit_winner_start(callback: CallbackQuery): """Начало редактирования победителя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2570,7 +2589,7 @@ async def edit_winner_start(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_edit_winner_lottery_")) async def edit_winner_select_place(callback: CallbackQuery, state: FSMContext): """Выбор места победителя для редактирования""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2604,7 +2623,7 @@ async def edit_winner_select_place(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_edit_winner_id_")) async def edit_winner_details(callback: CallbackQuery): """Показать детали победителя (пока просто информационное сообщение)""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2647,7 +2666,7 @@ async def edit_winner_details(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_remove_winner") async def remove_winner_start(callback: CallbackQuery): """Начало удаления победителя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2693,7 +2712,7 @@ async def remove_winner_start(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_remove_winner_lottery_")) async def remove_winner_select_place(callback: CallbackQuery): """Выбор победителя для удаления""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2727,7 +2746,7 @@ async def remove_winner_select_place(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_confirm_remove_winner_")) async def confirm_remove_winner(callback: CallbackQuery): """Подтверждение удаления победителя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2769,7 +2788,7 @@ async def confirm_remove_winner(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_do_remove_winner_")) async def do_remove_winner(callback: CallbackQuery): """Выполнение удаления победителя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2815,7 +2834,7 @@ async def do_remove_winner(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_conduct_draw") async def choose_lottery_for_draw(callback: CallbackQuery): """Выбор розыгрыша для проведения""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2854,7 +2873,7 @@ async def choose_lottery_for_draw(callback: CallbackQuery): @admin_router.callback_query(F.data.regexp(r"^admin_conduct_\d+$")) async def conduct_lottery_draw_confirm(callback: CallbackQuery): """Запрос подтверждения проведения розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -2912,7 +2931,7 @@ async def conduct_lottery_draw(callback: CallbackQuery): logger.info(f"🎯 conduct_lottery_draw HANDLER TRIGGERED! data={callback.data}, user={callback.from_user.id}") logger.info(f"conduct_lottery_draw вызван: callback.data={callback.data}, user_id={callback.from_user.id}") - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3004,7 +3023,7 @@ async def conduct_lottery_draw(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_stats") async def show_detailed_stats(callback: CallbackQuery): """Подробная статистика""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3080,7 +3099,7 @@ async def show_detailed_stats(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_settings") async def show_admin_settings(callback: CallbackQuery): """Настройки админ-панели""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3112,7 +3131,7 @@ async def show_admin_settings(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_export_data") async def export_data(callback: CallbackQuery): """Экспорт данных из системы""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3165,7 +3184,7 @@ async def export_data(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_cleanup") async def cleanup_old_data(callback: CallbackQuery): """Очистка старых данных""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3186,7 +3205,7 @@ async def cleanup_old_data(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_cleanup_old_lotteries") async def cleanup_old_lotteries(callback: CallbackQuery): """Очистка старых завершённых розыгрышей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3234,7 +3253,7 @@ async def cleanup_old_lotteries(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_cleanup_inactive_users") async def cleanup_inactive_users(callback: CallbackQuery): """Очистка неактивных пользователей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3295,7 +3314,7 @@ async def cleanup_inactive_users(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_cleanup_old_participations") async def cleanup_old_participations(callback: CallbackQuery): """Очистка старых участий""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3344,7 +3363,7 @@ async def cleanup_old_participations(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_system_info") async def show_system_info(callback: CallbackQuery): """Системная информация""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3374,7 +3393,7 @@ async def show_system_info(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_winner_display_settings") async def show_winner_display_settings(callback: CallbackQuery, state: FSMContext): """Настройка отображения победителей для розыгрышей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3416,7 +3435,7 @@ async def show_winner_display_settings(callback: CallbackQuery, state: FSMContex @admin_router.callback_query(F.data.startswith("admin_set_display_")) async def choose_display_type(callback: CallbackQuery, state: FSMContext): """Выбор типа отображения для конкретного розыгрыша""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3455,7 +3474,7 @@ async def apply_display_type(callback: CallbackQuery, state: FSMContext): logger.info(f"🎭 Попытка смены типа отображения. Callback data: {callback.data}") - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): logger.warning(f"🚫 Отказ в доступе пользователю {callback.from_user.id}") await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3519,7 +3538,7 @@ async def apply_display_type(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_messages") async def show_messages_menu(callback: CallbackQuery): """Показать меню управления сообщениями""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3542,7 +3561,7 @@ async def show_messages_menu(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_messages_recent") async def show_recent_messages(callback: CallbackQuery, page: int = 0): """Показать последние сообщения""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3591,7 +3610,7 @@ async def show_recent_messages(callback: CallbackQuery, page: int = 0): @admin_router.callback_query(F.data.startswith("admin_message_view_")) async def view_message(callback: CallbackQuery): """Просмотр конкретного сообщения""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3677,7 +3696,7 @@ async def view_message(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_message_delete_")) async def delete_message(callback: CallbackQuery): """Удалить сообщение пользователя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3743,7 +3762,7 @@ async def delete_message(callback: CallbackQuery): @admin_router.callback_query(F.data.startswith("admin_messages_user_")) async def show_user_messages(callback: CallbackQuery): """Показать все сообщения конкретного пользователя""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Недостаточно прав", show_alert=True) return @@ -3893,7 +3912,7 @@ async def _notify_all_participants_about_results(bot, session: AsyncSession, lot @admin_router.callback_query(F.data == "admin_export_users") async def admin_export_users(callback: CallbackQuery): """Экспорт всех пользователей в XLSX""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -3991,7 +4010,7 @@ async def admin_export_users(callback: CallbackQuery): @admin_router.callback_query(F.data == "admin_import_users") async def admin_import_users_start(callback: CallbackQuery, state: FSMContext): """Начать импорт пользователей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4024,7 +4043,7 @@ async def admin_import_users_start(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.import_users_json), F.document) async def admin_import_users_process(message: Message, state: FSMContext): """Обработка импорта пользователей из XLSX""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): return # Проверяем формат файла @@ -4198,7 +4217,7 @@ async def admin_import_users_process(message: Message, state: FSMContext): @admin_router.callback_query(F.data == "admin_broadcast") async def admin_broadcast_menu(callback: CallbackQuery, state: FSMContext): """Меню массовой рассылки""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4245,7 +4264,7 @@ async def admin_broadcast_menu(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_broadcast_start") async def admin_broadcast_start(callback: CallbackQuery, state: FSMContext): """Выбор типа рассылки""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4274,7 +4293,7 @@ async def admin_broadcast_start(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "broadcast_type_direct") async def broadcast_type_direct(callback: CallbackQuery, state: FSMContext): """Рассылка в ЛС - запрос сообщения""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4308,7 +4327,7 @@ async def broadcast_type_direct(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("broadcast_type_")) async def broadcast_type_channel_or_group(callback: CallbackQuery, state: FSMContext): """Выбор канала или группы для рассылки""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4362,7 +4381,7 @@ async def broadcast_type_channel_or_group(callback: CallbackQuery, state: FSMCon @admin_router.callback_query(F.data.startswith("broadcast_select_channel_")) async def broadcast_select_channel(callback: CallbackQuery, state: FSMContext): """Выбран канал/группа - запрос сообщения""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4407,7 +4426,7 @@ async def broadcast_select_channel(callback: CallbackQuery, state: FSMContext): @admin_router.message(StateFilter(AdminStates.broadcast_message), F.text | F.photo | F.video | F.document) async def admin_broadcast_send(message: Message, state: FSMContext): """Обработка и отправка рассылки""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): return data = await state.get_data() @@ -4550,7 +4569,7 @@ async def _broadcast_channel(message: Message, state: FSMContext, data: dict): @admin_router.callback_query(F.data == "admin_broadcast_channels") async def admin_broadcast_channels_menu(callback: CallbackQuery, state: FSMContext): """Меню управления каналами""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4594,7 +4613,7 @@ async def admin_broadcast_channels_menu(callback: CallbackQuery, state: FSMConte @admin_router.callback_query(F.data == "admin_broadcast_add_channel") async def admin_broadcast_add_channel_start(callback: CallbackQuery, state: FSMContext): """Начать добавление канала""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4625,7 +4644,7 @@ async def admin_broadcast_add_channel_start(callback: CallbackQuery, state: FSMC @admin_router.message(StateFilter(AdminStates.broadcast_add_channel_id), F.text) async def admin_broadcast_add_channel_id(message: Message, state: FSMContext): """Обработка ID канала""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): return try: @@ -4691,7 +4710,7 @@ async def admin_broadcast_add_channel_id(message: Message, state: FSMContext): @admin_router.message(StateFilter(AdminStates.broadcast_add_channel_title), F.text) async def admin_broadcast_add_channel_description(message: Message, state: FSMContext): """Обработка описания канала""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): return data = await state.get_data() @@ -4755,7 +4774,7 @@ async def admin_broadcast_add_channel_description(message: Message, state: FSMCo @admin_router.callback_query(F.data == "admin_broadcast_stats") async def admin_broadcast_stats(callback: CallbackQuery, state: FSMContext): """Статистика рассылок""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4809,7 +4828,7 @@ async def admin_broadcast_stats(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_broadcast_inactive") async def admin_broadcast_inactive(callback: CallbackQuery, state: FSMContext): """Статистика по неактивным пользователям""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4857,7 +4876,7 @@ async def admin_broadcast_inactive(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_check_inactive_now") async def admin_check_inactive_now(callback: CallbackQuery, state: FSMContext): """Запустить проверку неактивных пользователей вручную""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4890,7 +4909,7 @@ async def admin_check_inactive_now(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_users") async def admin_users_menu(callback: CallbackQuery, state: FSMContext): """Меню управления пользователями""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4927,7 +4946,7 @@ async def admin_users_menu(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data == "admin_users_search") async def admin_users_search_prompt(callback: CallbackQuery, state: FSMContext): """Запрос поискового запроса""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -4958,7 +4977,7 @@ async def admin_users_search_prompt(callback: CallbackQuery, state: FSMContext): @admin_router.message(AdminStates.user_management_search) async def admin_users_search_process(message: Message, state: FSMContext): """Обработка поискового запроса""" - if not is_admin(message.from_user.id): + if not await check_admin_access(message.from_user.id): return query = message.text.strip() @@ -5026,7 +5045,7 @@ async def admin_users_search_process(message: Message, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_users_list:")) async def admin_users_list(callback: CallbackQuery, state: FSMContext): """Список всех пользователей с пагинацией""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -5085,7 +5104,7 @@ async def admin_users_list(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_users_banned:")) async def admin_users_banned_list(callback: CallbackQuery, state: FSMContext): """Список заблокированных пользователей""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -5149,7 +5168,7 @@ async def admin_users_banned_list(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_user_view:")) async def admin_user_view(callback: CallbackQuery, state: FSMContext): """Просмотр информации о пользователе""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -5193,7 +5212,7 @@ async def admin_user_view(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_user_ban:")) async def admin_user_ban(callback: CallbackQuery, state: FSMContext): """Заблокировать пользователя в чате""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return @@ -5215,7 +5234,7 @@ async def admin_user_ban(callback: CallbackQuery, state: FSMContext): @admin_router.callback_query(F.data.startswith("admin_user_unban:")) async def admin_user_unban(callback: CallbackQuery, state: FSMContext): """Разблокировать пользователя в чате""" - if not is_admin(callback.from_user.id): + if not await check_admin_access(callback.from_user.id): await callback.answer("❌ Доступ запрещен", show_alert=True) return