diff --git a/src/handlers/admin_panel.py b/src/handlers/admin_panel.py index 462e30c..d888404 100644 --- a/src/handlers/admin_panel.py +++ b/src/handlers/admin_panel.py @@ -613,6 +613,137 @@ 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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + async with async_session_maker() as session: + lotteries = await LotteryService.get_all_lotteries(session, limit=20) + + if not lotteries: + await callback.message.edit_text( + "❌ Нет розыгрышей в системе", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_participants")] + ]) + ) + return + + buttons = [] + for lottery in lotteries: + buttons.append([ + InlineKeyboardButton( + text=f"{'✅' if lottery.is_active else '🔴'} {lottery.title}", + callback_data=f"admin_remove_part_from_{lottery.id}" + ) + ]) + buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_participants")]) + + await callback.message.edit_text( + "➖ Удалить участника из розыгрыша\n\nВыберите розыгрыш:", + reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons) + ) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + lottery_id = int(callback.data.split("_")[-1]) + + async with async_session_maker() as session: + lottery = await LotteryService.get_lottery(session, lottery_id) + if not lottery: + await callback.answer("❌ Розыгрыш не найден", show_alert=True) + return + + participant_count = await ParticipationService.get_participants_count(session, lottery_id) + + await state.update_data(remove_participant_lottery_id=lottery_id) + await state.set_state(AdminStates.remove_participant_user) + + await callback.message.edit_text( + f"➖ Удалить участника из розыгрыша\n\n" + f"🎯 Розыгрыш: {lottery.title}\n" + f"👥 Участников: {participant_count}\n\n" + f"Отправьте Telegram ID пользователя для удаления:", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="❌ Отмена", callback_data="admin_remove_participant")] + ]) + ) + + +@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): + await message.answer("❌ Недостаточно прав") + return + + data = await state.get_data() + lottery_id = data.get("remove_participant_lottery_id") + + try: + telegram_id = int(message.text.strip()) + except ValueError: + await message.answer( + "❌ Неверный формат. Отправьте числовой Telegram ID.", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="❌ Отмена", callback_data="admin_remove_participant")] + ]) + ) + return + + async with async_session_maker() as session: + user = await UserService.get_user_by_telegram_id(session, telegram_id) + if not user: + await message.answer( + f"❌ Пользователь с ID {telegram_id} не найден в системе", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_participants")] + ]) + ) + await state.clear() + return + + lottery = await LotteryService.get_lottery(session, lottery_id) + if not lottery: + await message.answer("❌ Розыгрыш не найден") + await state.clear() + return + + removed = await ParticipationService.remove_participant(session, lottery_id, user.id) + + await state.clear() + + username = f"@{user.username}" if user.username else "Нет username" + + if removed: + await message.answer( + f"✅ Участник удалён из розыгрыша!\n\n" + f"👤 {user.first_name} {user.last_name or ''}\n" + f"📱 Username: {username}\n" + f"🆔 ID: {user.telegram_id}\n" + f"🎯 Розыгрыш: {lottery.title}", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="👥 К управлению участниками", callback_data="admin_participants")] + ]) + ) + else: + await message.answer( + f"⚠️ Пользователь {user.first_name} не участвует в этом розыгрыше", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="👥 К управлению участниками", callback_data="admin_participants")] + ]) + ) + + @admin_router.callback_query(F.data == "admin_list_all_participants") async def list_all_participants(callback: CallbackQuery): """Список всех участников""" @@ -1541,6 +1672,25 @@ async def start_edit_lottery(callback: CallbackQuery, state: FSMContext): await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)) +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + # Извлекаем lottery_id из callback_data (формат admin_edit_123) + parts = callback.data.split("_") + if len(parts) == 3: # admin_edit_123 + lottery_id = int(parts[2]) + # Подменяем callback_data для обработки существующим хэндлером + callback.data = f"admin_edit_lottery_select_{lottery_id}" + await choose_edit_field(callback, state) + else: + # Если формат другой, то это уже правильный callback + await choose_edit_field(callback, state) + + @admin_router.callback_query(F.data.startswith("admin_edit_lottery_select_")) async def choose_edit_field(callback: CallbackQuery, state: FSMContext): """Выбор поля для редактирования""" @@ -2037,6 +2187,360 @@ 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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + async with async_session_maker() as session: + # Получаем все розыгрыши с победителями + from sqlalchemy import select + result = await session.execute( + select(Winner) + .options(selectinload(Winner.user), selectinload(Winner.lottery)) + .order_by(Winner.created_at.desc()) + .limit(50) + ) + winners = result.scalars().all() + + if not winners: + await callback.message.edit_text( + "📋 Список победителей пуст\n\nПока не было проведено ни одного розыгрыша.", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")] + ]) + ) + return + + text = "👑 Список победителей\n\n" + + # Группируем победителей по розыгрышам + lotteries_dict = {} + for winner in winners: + lottery_id = winner.lottery_id + if lottery_id not in lotteries_dict: + lotteries_dict[lottery_id] = { + 'lottery': winner.lottery, + 'winners': [] + } + lotteries_dict[lottery_id]['winners'].append(winner) + + # Выводим информацию + for lottery_id, data in list(lotteries_dict.items())[:10]: + lottery = data['lottery'] + winners_list = data['winners'] + + text += f"🎯 {lottery.title}\n" + text += f"📅 {lottery.created_at.strftime('%d.%m.%Y')}\n" + + for winner in sorted(winners_list, key=lambda w: w.place): + username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name + manual_mark = "🔧" if winner.is_manual else "🎲" + text += f" {manual_mark} {winner.place} место: {username} - {winner.prize}\n" + + text += "\n" + + if len(lotteries_dict) > 10: + text += f"\n... и ещё {len(lotteries_dict) - 10} розыгрышей" + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔄 Обновить", callback_data="admin_list_winners")], + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")] + ]) + ) + + +@admin_router.callback_query(F.data == "admin_edit_winner") +async def edit_winner_start(callback: CallbackQuery): + """Начало редактирования победителя""" + if not is_admin(callback.from_user.id): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + async with async_session_maker() as session: + from sqlalchemy import select + result = await session.execute( + select(Lottery) + .join(Winner) + .distinct() + .order_by(Lottery.created_at.desc()) + .limit(20) + ) + lotteries_with_winners = result.scalars().all() + + if not lotteries_with_winners: + await callback.message.edit_text( + "❌ Нет розыгрышей с победителями для редактирования", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")] + ]) + ) + return + + text = "📝 Редактировать победителя\n\nВыберите розыгрыш:\n\n" + buttons = [] + + for lottery in lotteries_with_winners: + buttons.append([ + InlineKeyboardButton( + text=f"🎯 {lottery.title}", + callback_data=f"admin_edit_winner_lottery_{lottery.id}" + ) + ]) + + buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")]) + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons) + ) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + lottery_id = int(callback.data.split("_")[-1]) + + async with async_session_maker() as session: + lottery = await LotteryService.get_lottery(session, lottery_id) + winners = await LotteryService.get_winners(session, lottery_id) + + if not winners: + await callback.answer("❌ Нет победителей для редактирования", show_alert=True) + return + + text = f"📝 Редактировать победителя\n\n🎯 {lottery.title}\n\nВыберите место:\n\n" + buttons = [] + + for winner in winners: + username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name + buttons.append([ + InlineKeyboardButton( + text=f"🏆 {winner.place} место: {username} - {winner.prize}", + callback_data=f"admin_edit_winner_id_{winner.id}" + ) + ]) + + buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_edit_winner")]) + + await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + winner_id = int(callback.data.split("_")[-1]) + + async with async_session_maker() as session: + from sqlalchemy import select + result = await session.execute( + select(Winner) + .options(selectinload(Winner.user), selectinload(Winner.lottery)) + .where(Winner.id == winner_id) + ) + winner = result.scalar_one_or_none() + + if not winner: + await callback.answer("❌ Победитель не найден", show_alert=True) + return + + username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name + manual_mark = "🔧 Установлен вручную" if winner.is_manual else "🎲 Выбран случайно" + + text = f"📝 Информация о победителе\n\n" + text += f"🎯 Розыгрыш: {winner.lottery.title}\n" + text += f"🏆 Место: {winner.place}\n" + text += f"💰 Приз: {winner.prize}\n" + text += f"👤 Пользователь: {username}\n" + text += f"🆔 ID: {winner.user.telegram_id}\n" + text += f"📊 Тип: {manual_mark}\n" + text += f"📅 Дата: {winner.created_at.strftime('%d.%m.%Y %H:%M')}\n\n" + text += "ℹ️ Редактирование победителей доступно через удаление и повторное добавление." + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔙 Назад", callback_data=f"admin_edit_winner_lottery_{winner.lottery_id}")] + ]) + ) + + +@admin_router.callback_query(F.data == "admin_remove_winner") +async def remove_winner_start(callback: CallbackQuery): + """Начало удаления победителя""" + if not is_admin(callback.from_user.id): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + async with async_session_maker() as session: + from sqlalchemy import select + result = await session.execute( + select(Lottery) + .join(Winner) + .distinct() + .order_by(Lottery.created_at.desc()) + .limit(20) + ) + lotteries_with_winners = result.scalars().all() + + if not lotteries_with_winners: + await callback.message.edit_text( + "❌ Нет розыгрышей с победителями для удаления", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")] + ]) + ) + return + + text = "❌ Удалить победителя\n\nВыберите розыгрыш:\n\n" + buttons = [] + + for lottery in lotteries_with_winners: + buttons.append([ + InlineKeyboardButton( + text=f"🎯 {lottery.title}", + callback_data=f"admin_remove_winner_lottery_{lottery.id}" + ) + ]) + + buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")]) + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons) + ) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + lottery_id = int(callback.data.split("_")[-1]) + + async with async_session_maker() as session: + lottery = await LotteryService.get_lottery(session, lottery_id) + winners = await LotteryService.get_winners(session, lottery_id) + + if not winners: + await callback.answer("❌ Нет победителей для удаления", show_alert=True) + return + + text = f"❌ Удалить победителя\n\n🎯 {lottery.title}\n\nВыберите победителя для удаления:\n\n" + buttons = [] + + for winner in winners: + username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name + buttons.append([ + InlineKeyboardButton( + text=f"🏆 {winner.place} место: {username} - {winner.prize}", + callback_data=f"admin_confirm_remove_winner_{winner.id}" + ) + ]) + + buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_remove_winner")]) + + await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + winner_id = int(callback.data.split("_")[-1]) + + async with async_session_maker() as session: + from sqlalchemy import select + result = await session.execute( + select(Winner) + .options(selectinload(Winner.user), selectinload(Winner.lottery)) + .where(Winner.id == winner_id) + ) + winner = result.scalar_one_or_none() + + if not winner: + await callback.answer("❌ Победитель не найден", show_alert=True) + return + + username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name + + text = f"⚠️ Подтверждение удаления\n\n" + text += f"Вы действительно хотите удалить победителя?\n\n" + text += f"🎯 Розыгрыш: {winner.lottery.title}\n" + text += f"🏆 Место: {winner.place}\n" + text += f"👤 Пользователь: {username}\n" + text += f"💰 Приз: {winner.prize}\n\n" + text += "⚠️ Это действие необратимо!" + + buttons = [ + [ + InlineKeyboardButton(text="✅ Да, удалить", callback_data=f"admin_do_remove_winner_{winner_id}"), + InlineKeyboardButton(text="❌ Отмена", callback_data=f"admin_remove_winner_lottery_{winner.lottery_id}") + ] + ] + + await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + winner_id = int(callback.data.split("_")[-1]) + + async with async_session_maker() as session: + from sqlalchemy import select, delete + + # Получаем информацию о победителе перед удалением + result = await session.execute( + select(Winner) + .options(selectinload(Winner.user), selectinload(Winner.lottery)) + .where(Winner.id == winner_id) + ) + winner = result.scalar_one_or_none() + + if not winner: + await callback.answer("❌ Победитель не найден", show_alert=True) + return + + lottery_id = winner.lottery_id + username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name + + # Удаляем победителя + await session.execute(delete(Winner).where(Winner.id == winner_id)) + await session.commit() + + await callback.message.edit_text( + f"✅ Победитель удалён!\n\n" + f"👤 {username}\n" + f"🏆 Место: {winner.place}\n" + f"💰 Приз: {winner.prize}", + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="👑 К управлению победителями", callback_data="admin_winners")] + ]) + ) + + # ====================== # ПРОВЕДЕНИЕ РОЗЫГРЫША # ====================== @@ -2236,6 +2740,189 @@ async def show_admin_settings(callback: CallbackQuery): await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)) +@admin_router.callback_query(F.data == "admin_export_data") +async def export_data(callback: CallbackQuery): + """Экспорт данных из системы""" + if not is_admin(callback.from_user.id): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + async with async_session_maker() as session: + from sqlalchemy import func, select + + # Собираем статистику + users_count = await session.scalar(select(func.count(User.id))) + lotteries_count = await session.scalar(select(func.count(Lottery.id))) + participations_count = await session.scalar(select(func.count(Participation.id))) + winners_count = await session.scalar(select(func.count(Winner.id))) + + import json + from datetime import datetime + + # Формируем данные для экспорта + export_info = { + "export_date": datetime.now().isoformat(), + "statistics": { + "users": users_count, + "lotteries": lotteries_count, + "participations": participations_count, + "winners": winners_count + } + } + + # Сохраняем в файл + filename = f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" + with open(filename, 'w', encoding='utf-8') as f: + json.dump(export_info, f, ensure_ascii=False, indent=2) + + text = "💾 Экспорт данных\n\n" + text += f"📊 Статистика:\n" + text += f"👥 Пользователей: {users_count}\n" + text += f"🎯 Розыгрышей: {lotteries_count}\n" + text += f"🎫 Участий: {participations_count}\n" + text += f"🏆 Победителей: {winners_count}\n\n" + text += f"✅ Данные экспортированы в файл:\n{filename}" + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🔄 Экспортировать снова", callback_data="admin_export_data")], + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_settings")] + ]) + ) + + +@admin_router.callback_query(F.data == "admin_cleanup") +async def cleanup_old_data(callback: CallbackQuery): + """Очистка старых данных""" + if not is_admin(callback.from_user.id): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + text = "🧹 Очистка старых данных\n\n" + text += "Выберите тип данных для очистки:\n\n" + text += "⚠️ Внимание! Это действие необратимо!" + + buttons = [ + [InlineKeyboardButton(text="🗑️ Завершённые розыгрыши (>30 дней)", callback_data="admin_cleanup_old_lotteries")], + [InlineKeyboardButton(text="👻 Неактивные пользователи (>90 дней)", callback_data="admin_cleanup_inactive_users")], + [InlineKeyboardButton(text="📋 Старые участия (>60 дней)", callback_data="admin_cleanup_old_participations")], + [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_settings")] + ] + + await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + from datetime import timedelta + + cutoff_date = datetime.now() - timedelta(days=30) + + async with async_session_maker() as session: + from sqlalchemy import select, delete + + # Находим старые завершённые розыгрыши + result = await session.execute( + select(Lottery) + .where( + Lottery.is_completed == True, + Lottery.created_at < cutoff_date + ) + ) + old_lotteries = result.scalars().all() + count = len(old_lotteries) + + if count == 0: + await callback.answer("✅ Нет старых розыгрышей для удаления", show_alert=True) + return + + # Удаляем старые розыгрыши + for lottery in old_lotteries: + await session.delete(lottery) + + await session.commit() + + text = f"✅ Очистка завершена!\n\n" + text += f"🗑️ Удалено розыгрышей: {count}\n" + text += f"📅 Старше: {cutoff_date.strftime('%d.%m.%Y')}" + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🧹 К очистке", callback_data="admin_cleanup")], + [InlineKeyboardButton(text="⚙️ К настройкам", callback_data="admin_settings")] + ]) + ) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + await callback.answer( + "ℹ️ Функция в разработке\n\nУдаление пользователей требует дополнительной логики для сохранения целостности данных.", + show_alert=True + ) + + +@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): + await callback.answer("❌ Недостаточно прав", show_alert=True) + return + + from datetime import timedelta + + cutoff_date = datetime.now() - timedelta(days=60) + + async with async_session_maker() as session: + from sqlalchemy import select, delete + + # Находим старые участия в завершённых розыгрышах + result = await session.execute( + select(Participation) + .join(Lottery) + .where( + Lottery.is_completed == True, + Participation.created_at < cutoff_date + ) + ) + old_participations = result.scalars().all() + count = len(old_participations) + + if count == 0: + await callback.answer("✅ Нет старых участий для удаления", show_alert=True) + return + + # Удаляем старые участия + for participation in old_participations: + await session.delete(participation) + + await session.commit() + + text = f"✅ Очистка завершена!\n\n" + text += f"🗑️ Удалено участий: {count}\n" + text += f"📅 Старше: {cutoff_date.strftime('%d.%m.%Y')}" + + await callback.message.edit_text( + text, + reply_markup=InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🧹 К очистке", callback_data="admin_cleanup")], + [InlineKeyboardButton(text="⚙️ К настройкам", callback_data="admin_settings")] + ]) + ) + + @admin_router.callback_query(F.data == "admin_system_info") async def show_system_info(callback: CallbackQuery): """Системная информация"""