Files
new_lottery_bot/account_handlers.py
2025-11-15 20:03:49 +09:00

376 lines
14 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.

"""
Обработчики для работы со счетами в розыгрышах
"""
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.filters import StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from sqlalchemy.ext.asyncio import AsyncSession
from config import ADMIN_IDS
from database import async_session_maker
from services import LotteryService
from account_services import AccountParticipationService
from account_utils import parse_accounts_from_message, validate_account_number
from typing import List
# Состояния FSM для работы со счетами
class AccountStates(StatesGroup):
waiting_for_lottery_choice = State() # Выбор розыгрыша для добавления счетов
waiting_for_winner_lottery = State() # Выбор розыгрыша для установки победителя
waiting_for_winner_place = State() # Выбор места победителя
searching_accounts = State() # Поиск счетов
# Создаем роутер
account_router = Router()
def is_admin(user_id: int) -> bool:
"""Проверка прав администратора"""
return user_id in ADMIN_IDS
@account_router.message(
F.text,
StateFilter(None),
~F.text.startswith('/') # Исключаем команды
)
async def detect_account_input(message: Message, state: FSMContext):
"""
Обнаружение ввода счетов в сообщении
Активируется только для администраторов
"""
if not is_admin(message.from_user.id):
return
# Парсим счета из сообщения
accounts = parse_accounts_from_message(message.text)
if not accounts:
return # Счета не обнаружены, пропускаем
# Сохраняем счета в состоянии
await state.update_data(detected_accounts=accounts)
# Формируем сообщение
accounts_text = "\n".join([f"{acc}" for acc in accounts])
count = len(accounts)
text = (
f"🔍 <b>Обнаружен ввод счет{'а' if count == 1 else 'ов'}</b>\n\n"
f"Найдено: <b>{count}</b>\n\n"
f"{accounts_text}\n\n"
f"Выберите действие:"
)
# Кнопки выбора действия
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(
text=" Добавить в розыгрыш",
callback_data="account_action:add_to_lottery"
)],
[InlineKeyboardButton(
text="👑 Сделать победителем",
callback_data="account_action:set_as_winner"
)],
[InlineKeyboardButton(
text="❌ Отмена",
callback_data="account_action:cancel"
)]
])
await message.answer(text, reply_markup=keyboard, parse_mode="HTML")
@account_router.callback_query(F.data == "account_action:cancel")
async def cancel_account_action(callback: CallbackQuery, state: FSMContext):
"""Отмена действия со счетами"""
await state.clear()
await callback.message.edit_text("❌ Действие отменено")
await callback.answer()
@account_router.callback_query(F.data == "account_action:add_to_lottery")
async def choose_lottery_for_accounts(callback: CallbackQuery, state: FSMContext):
"""Выбор розыгрыша для добавления счетов"""
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_active_lotteries(session, limit=20)
if not lotteries:
await callback.message.edit_text(
"❌ Нет активных розыгрышей.\n\n"
"Сначала создайте розыгрыш через /admin"
)
await state.clear()
await callback.answer()
return
# Формируем кнопки с розыгрышами
buttons = []
for lottery in lotteries:
buttons.append([InlineKeyboardButton(
text=f"🎲 {lottery.title[:40]}",
callback_data=f"add_accounts_to:{lottery.id}"
)])
buttons.append([InlineKeyboardButton(
text="❌ Отмена",
callback_data="account_action:cancel"
)])
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
await callback.message.edit_text(
"📋 <b>Выберите розыгрыш:</b>",
reply_markup=keyboard,
parse_mode="HTML"
)
await state.set_state(AccountStates.waiting_for_lottery_choice)
await callback.answer()
@account_router.callback_query(F.data.startswith("add_accounts_to:"))
async def add_accounts_to_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])
# Получаем сохраненные счета
data = await state.get_data()
accounts = data.get("detected_accounts", [])
if not accounts:
await callback.message.edit_text("❌ Счета не найдены")
await state.clear()
await callback.answer()
return
# Показываем процесс
await callback.message.edit_text("⏳ Добавляем счета...")
async with async_session_maker() as session:
# Получаем информацию о розыгрыше
lottery = await LotteryService.get_lottery(session, lottery_id)
if not lottery:
await callback.message.edit_text("❌ Розыгрыш не найден")
await state.clear()
await callback.answer()
return
# Добавляем счета
results = await AccountParticipationService.add_accounts_bulk(
session, lottery_id, accounts
)
# Формируем результат
text = f"<b>Результаты добавления в розыгрыш:</b>\n<i>{lottery.title}</i>\n\n"
text += f"✅ Добавлено: <b>{results['added']}</b>\n"
text += f"⚠️ Пропущено: <b>{results['skipped']}</b>\n\n"
if results['details']:
text += "<b>Детали:</b>\n"
text += "\n".join(results['details'][:20]) # Показываем первые 20
if len(results['details']) > 20:
text += f"\n... и ещё {len(results['details']) - 20}"
if results['errors']:
text += f"\n\n<b>Ошибки:</b>\n"
text += "\n".join(results['errors'][:10])
await callback.message.edit_text(text, parse_mode="HTML")
await state.clear()
await callback.answer("✅ Готово!")
@account_router.callback_query(F.data == "account_action:set_as_winner")
async def choose_lottery_for_winner(callback: CallbackQuery, state: FSMContext):
"""Выбор розыгрыша для установки победителя"""
if not is_admin(callback.from_user.id):
await callback.answer("⛔ Доступно только администраторам", show_alert=True)
return
# Проверяем, что у нас только один счет
data = await state.get_data()
accounts = data.get("detected_accounts", [])
if len(accounts) != 1:
await callback.message.edit_text(
"❌ Для установки победителя введите <b>один</b> счет",
parse_mode="HTML"
)
await state.clear()
await callback.answer()
return
async with async_session_maker() as session:
# Получаем все розыгрыши (активные и завершенные)
lotteries = await LotteryService.get_all_lotteries(session, limit=30)
if not lotteries:
await callback.message.edit_text(
"❌ Нет розыгрышей.\n\n"
"Сначала создайте розыгрыш через /admin"
)
await state.clear()
await callback.answer()
return
# Формируем кнопки
buttons = []
for lottery in lotteries:
status = "" if lottery.is_completed else "🎲"
buttons.append([InlineKeyboardButton(
text=f"{status} {lottery.title[:35]}",
callback_data=f"winner_lottery:{lottery.id}"
)])
buttons.append([InlineKeyboardButton(
text="❌ Отмена",
callback_data="account_action:cancel"
)])
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
account = accounts[0]
await callback.message.edit_text(
f"👑 <b>Установка победителя</b>\n\n"
f"Счет: <code>{account}</code>\n\n"
f"Выберите розыгрыш:",
reply_markup=keyboard,
parse_mode="HTML"
)
await state.set_state(AccountStates.waiting_for_winner_lottery)
await callback.answer()
@account_router.callback_query(F.data.startswith("winner_lottery:"))
async def choose_winner_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])
# Сохраняем ID розыгрыша
await state.update_data(winner_lottery_id=lottery_id)
async with async_session_maker() as session:
lottery = await LotteryService.get_lottery(session, lottery_id)
if not lottery:
await callback.message.edit_text("❌ Розыгрыш не найден")
await state.clear()
await callback.answer()
return
# Получаем призы
prizes = lottery.prizes or []
# Формируем кнопки с местами
buttons = []
for i, prize in enumerate(prizes[:10], 1): # Максимум 10 мест
prize_text = prize if isinstance(prize, str) else prize.get('description', f'Приз {i}')
buttons.append([InlineKeyboardButton(
text=f"🏆 Место {i}: {prize_text[:30]}",
callback_data=f"winner_place:{i}"
)])
# Если призов нет, предлагаем места 1-5
if not buttons:
for i in range(1, 6):
buttons.append([InlineKeyboardButton(
text=f"🏆 Место {i}",
callback_data=f"winner_place:{i}"
)])
buttons.append([InlineKeyboardButton(
text="❌ Отмена",
callback_data="account_action:cancel"
)])
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
data = await state.get_data()
account = data.get("detected_accounts", [])[0]
await callback.message.edit_text(
f"👑 <b>Установка победителя</b>\n\n"
f"Розыгрыш: <i>{lottery.title}</i>\n"
f"Счет: <code>{account}</code>\n\n"
f"Выберите место:",
reply_markup=keyboard,
parse_mode="HTML"
)
await state.set_state(AccountStates.waiting_for_winner_place)
await callback.answer()
@account_router.callback_query(F.data.startswith("winner_place:"))
async def set_account_winner(callback: CallbackQuery, state: FSMContext):
"""Установка счета как победителя"""
if not is_admin(callback.from_user.id):
await callback.answer("⛔ Доступно только администраторам", show_alert=True)
return
place = int(callback.data.split(":")[1])
# Получаем данные
data = await state.get_data()
account = data.get("detected_accounts", [])[0]
lottery_id = data.get("winner_lottery_id")
if not account or not lottery_id:
await callback.message.edit_text("❌ Ошибка: данные не найдены")
await state.clear()
await callback.answer()
return
# Показываем процесс
await callback.message.edit_text("⏳ Устанавливаем победителя...")
async with async_session_maker() as session:
lottery = await LotteryService.get_lottery(session, lottery_id)
# Получаем приз для этого места
prize = None
if lottery.prizes and len(lottery.prizes) >= place:
prize_info = lottery.prizes[place - 1]
prize = prize_info if isinstance(prize_info, str) else prize_info.get('description')
# Устанавливаем победителя
result = await AccountParticipationService.set_account_as_winner(
session, lottery_id, account, place, prize
)
if result["success"]:
text = (
f"✅ <b>Победитель установлен!</b>\n\n"
f"Розыгрыш: <i>{lottery.title}</i>\n"
f"Счет: <code>{account}</code>\n"
f"Место: <b>{place}</b>\n"
)
if prize:
text += f"Приз: <i>{prize}</i>"
await callback.answer("✅ Победитель установлен!", show_alert=True)
else:
text = f"{result['message']}"
await callback.answer("❌ Ошибка", show_alert=True)
await callback.message.edit_text(text, parse_mode="HTML")
await state.clear()