sno-quiz/bot/admin_handlers.py
2025-09-17 22:22:14 +03:00

233 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Admin handlers for QR code management
"""
import logging
from typing import Optional
from aiogram import F, types
from aiogram.filters import Command
from aiogram.types import (
InlineKeyboardButton,
InlineKeyboardMarkup,
Message,
CallbackQuery,
)
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import config
from qr_service import QRService
logger = logging.getLogger(__name__)
def create_admin_keyboard() -> InlineKeyboardMarkup:
"""Create admin keyboard"""
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(text="🎫 Генерировать QR-коды", callback_data="admin_generate_qr")
)
builder.row(
InlineKeyboardButton(text="📊 Статистика", callback_data="admin_stats")
)
builder.row(
InlineKeyboardButton(text="🔧 Настройки", callback_data="admin_settings")
)
return builder.as_markup()
def create_qr_type_keyboard() -> InlineKeyboardMarkup:
"""Create QR type selection keyboard"""
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(text="💰 Награда", callback_data="qr_type_reward")
)
builder.row(
InlineKeyboardButton(text="🧠 Викторина", callback_data="qr_type_quiz")
)
builder.row(
InlineKeyboardButton(text="🛒 Магазин", callback_data="qr_type_shop")
)
builder.row(
InlineKeyboardButton(text="❌ Отмена", callback_data="admin_cancel")
)
return builder.as_markup()
def create_main_keyboard() -> InlineKeyboardMarkup:
"""Create main keyboard with mini app button"""
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(
text="🎯 Открыть Викторины",
web_app=types.WebAppInfo(url=config.frontend_url)
)
)
return builder.as_markup()
async def handle_admin_command(message: Message) -> None:
"""Handle /admin command"""
if not config.has_admin_privileges(message.from_user.id):
await message.answer(
"У вас нет прав для доступа к панели администратора."
)
return
user_role = "Администратор" if config.is_admin(message.from_user.id) else "Оператор"
await message.answer(
f"🛡️ Панель администратора\n\n"
f"👤 Ваша роль: {user_role}\n"
f"🆔 ID: {message.from_user.id}\n\n"
f"Выберите действие:",
reply_markup=create_admin_keyboard()
)
async def handle_generate_qr_callback(callback: CallbackQuery) -> None:
"""Handle generate QR callback"""
if not config.has_admin_privileges(callback.from_user.id):
await callback.answer("❌ Доступ запрещен", show_alert=True)
return
await callback.message.edit_text(
"🎫 Выберите тип QR-кода для генерации:",
reply_markup=create_qr_type_keyboard()
)
await callback.answer()
async def handle_qr_type_callback(callback: CallbackQuery) -> None:
"""Handle QR type selection callback"""
if not config.has_admin_privileges(callback.from_user.id):
await callback.answer("❌ Доступ запрещен", show_alert=True)
return
qr_type = callback.data.split("_")[-1]
type_descriptions = {
"reward": "💰 Награда (звёзды)",
"quiz": "🧠 Викторина",
"shop": "🛒 Магазин"
}
# Store the selected type in user state
# For simplicity, we'll use a simple approach. In production, use a proper state machine
instructions = {
"reward": "Введите сумму награды в звёздах (1-1000):",
"quiz": "Введите ID викторины:",
"shop": "Введите действие для магазина (например: discount_10):"
}
await callback.message.edit_text(
f"🎫 {type_descriptions.get(qr_type, qr_type)}\n\n"
f"{instructions.get(qr_type)}",
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[types.InlineKeyboardButton(text="❌ Отмена", callback_data="admin_cancel")]
]
)
)
await callback.answer()
async def handle_qr_generation_text(message: Message) -> None:
"""Handle QR generation text input"""
if not config.has_admin_privileges(message.from_user.id):
await message.answer("❌ Доступ запрещен")
return
# This is a simplified approach. In production, use proper state management
text = message.text.strip()
# Try to determine the type based on context
# For now, we'll assume it's a reward if it's a number
try:
amount = int(text)
if 1 <= amount <= 1000:
# Generate reward QR codes
await generate_reward_qr_codes(message, amount)
return
except ValueError:
pass
# If not a number, ask for clarification
await message.answer(
"🎫 Пожалуйста, уточните тип QR-кода:\n\n"
"1. Для награды: введите число (1-1000)\n"
"2. Для викторины: введите 'quiz ID' (например: quiz 123)\n"
"3. Для магазина: введите 'shop действие' (например: shop discount_10)\n\n"
"Или используйте /admin для выбора типа",
reply_markup=create_admin_keyboard()
)
async def generate_reward_qr_codes(message: Message, amount: int, count: int = 5) -> None:
"""Generate reward QR codes"""
async with QRService() as qr_service:
try:
is_valid, error_msg = qr_service.validate_qr_request("reward", str(amount), count)
if not is_valid:
await message.answer(f"{error_msg}")
return
tokens = await qr_service.generate_qr_codes("reward", str(amount), count)
response_text = qr_service.format_qr_examples(
tokens,
"reward",
f"QR-коды для награды {amount}"
)
await message.answer(
response_text,
reply_markup=create_admin_keyboard()
)
except Exception as e:
logger.error(f"Error generating QR codes: {e}")
await message.answer(
f"❌ Ошибка при генерации QR-кодов: {e}",
reply_markup=create_admin_keyboard()
)
async def handle_admin_cancel_callback(callback: CallbackQuery) -> None:
"""Handle admin cancel callback"""
await callback.message.edit_text(
"❌ Операция отменена",
reply_markup=create_admin_keyboard()
)
await callback.answer()
async def handle_admin_stats_callback(callback: CallbackQuery) -> None:
"""Handle admin stats callback"""
if not config.has_admin_privileges(callback.from_user.id):
await callback.answer("❌ Доступ запрещен", show_alert=True)
return
# Placeholder for stats
await callback.answer("📊 Статистика в разработке", show_alert=True)
async def handle_admin_settings_callback(callback: CallbackQuery) -> None:
"""Handle admin settings callback"""
if not config.is_admin(callback.from_user.id):
await callback.answer("❌ Только администраторы могут менять настройки", show_alert=True)
return
# Placeholder for settings
await callback.answer("🔧 Настройки в разработке", show_alert=True)