"""Команды для повторного розыгрыша неподтвержденных выигрышей""" from aiogram import Router, F from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup from aiogram.filters import Command from sqlalchemy import select, and_ from datetime import datetime, timezone, timedelta import random from src.filters.case_insensitive import CaseInsensitiveCommand from src.core.database import async_session_maker from src.core.registration_services import AccountService, WinnerNotificationService from src.core.services import LotteryService from src.core.models import User, Winner from src.core.config import ADMIN_IDS from src.core.permissions import admin_only router = Router() @router.message(CaseInsensitiveCommand("check_unclaimed")) @admin_only async def check_unclaimed_winners(message: Message): """ Проверить неподтвержденные выигрыши (более 24 часов) (регистронезависимо) Формат: /check_unclaimed """ parts = message.text.split() if len(parts) != 2: await message.answer( "❌ Неверный формат команды\n\n" "Используйте: /check_unclaimed " ) return try: lottery_id = int(parts[1]) except ValueError: await message.answer("❌ lottery_id должен быть числом") return try: async with async_session_maker() as session: from sqlalchemy.orm import selectinload from src.core.models import Lottery # Загружаем розыгрыш с участниками lottery_result = await session.execute( select(Lottery) .options(selectinload(Lottery.participations)) .where(Lottery.id == lottery_id) ) lottery = lottery_result.scalar_one_or_none() if not lottery: await message.answer(f"❌ Розыгрыш #{lottery_id} не найден") return winners = await LotteryService.get_winners(session, lottery_id) if not winners: await message.answer(f"В розыгрыше '{lottery.title}' нет победителей") return # Находим неподтвержденные выигрыши старше 24 часов now = datetime.now(timezone.utc) unclaimed = [] for winner in winners: if not winner.is_claimed and winner.is_notified: # Проверяем, прошло ли 24 часа time_passed = now - winner.created_at if time_passed.total_seconds() > 24 * 3600: # 24 часа unclaimed.append({ 'winner': winner, 'hours_passed': int(time_passed.total_seconds() / 3600) }) if not unclaimed: await message.answer( f"✅ Все победители розыгрыша '{lottery.title}' подтвердили выигрыш\n" f"или срок подтверждения еще не истек." ) return text = f"⚠️ **Неподтвержденные выигрыши в розыгрыше '{lottery.title}':**\n\n" for item in unclaimed: winner = item['winner'] hours = item['hours_passed'] text += f"🏆 {winner.place} место - {winner.prize}\n" # Получаем информацию о победителе async with async_session_maker() as session: if winner.user_id: user_result = await session.execute( select(User).where(User.id == winner.user_id) ) user = user_result.scalar_one_or_none() if user: text += f" 👤 {user.first_name}" if user.club_card_number: text += f" (КК: {user.club_card_number})" text += "\n" if winner.account_number: text += f" 💳 {winner.account_number}\n" text += f" ⏰ Прошло: {hours} часов\n\n" text += f"\n📊 Всего неподтвержденных: {len(unclaimed)}\n\n" text += f"Используйте /redraw {lottery_id} для повторного розыгрыша" await message.answer(text, parse_mode="Markdown") except Exception as e: await message.answer(f"❌ Ошибка: {str(e)}") @router.message(CaseInsensitiveCommand("redraw")) @admin_only async def redraw_lottery(message: Message): """ Переиграть розыгрыш для неподтвержденных выигрышей (регистронезависимо) Формат: /redraw """ parts = message.text.split() if len(parts) != 2: await message.answer( "❌ Неверный формат команды\n\n" "Используйте: /redraw " ) return try: lottery_id = int(parts[1]) except ValueError: await message.answer("❌ lottery_id должен быть числом") return try: async with async_session_maker() as session: from sqlalchemy.orm import selectinload from src.core.models import Lottery # Загружаем розыгрыш с участниками lottery_result = await session.execute( select(Lottery) .options(selectinload(Lottery.participations)) .where(Lottery.id == lottery_id) ) lottery = lottery_result.scalar_one_or_none() if not lottery: await message.answer(f"❌ Розыгрыш #{lottery_id} не найден") return winners = await LotteryService.get_winners(session, lottery_id) # Находим неподтвержденные выигрыши старше 24 часов now = datetime.now(timezone.utc) unclaimed_winners = [] for winner in winners: if not winner.is_claimed and winner.is_notified: time_passed = now - winner.created_at if time_passed.total_seconds() > 24 * 3600: unclaimed_winners.append(winner) if not unclaimed_winners: await message.answer( "✅ Нет неподтвержденных выигрышей старше 24 часов.\n" "Повторный розыгрыш не требуется." ) return # Получаем всех участников, исключая текущих победителей all_participants = [] current_winner_accounts = set() for winner in winners: if winner.account_number: current_winner_accounts.add(winner.account_number) for p in lottery.participations: if p.account_number and p.account_number not in current_winner_accounts: all_participants.append(p) if not all_participants: await message.answer( "❌ Нет доступных участников для повторного розыгрыша.\n" "Все участники уже являются победителями." ) return # Переигрываем каждое неподтвержденное место redraw_results = [] for old_winner in unclaimed_winners: if not all_participants: break # Выбираем нового победителя new_participant = random.choice(all_participants) all_participants.remove(new_participant) # Удаляем старого победителя await session.delete(old_winner) # Создаем нового победителя new_winner = Winner( lottery_id=lottery_id, user_id=None, account_number=new_participant.account_number, account_id=new_participant.account_id, place=old_winner.place, prize=old_winner.prize, is_manual=False, is_notified=False, is_claimed=False ) session.add(new_winner) redraw_results.append({ 'place': old_winner.place, 'prize': old_winner.prize, 'old_account': old_winner.account_number, 'new_account': new_participant.account_number }) await session.commit() # Отправляем уведомления новым победителям for result in redraw_results: # Находим нового победителя new_winner_result = await session.execute( select(Winner).where( and_( Winner.lottery_id == lottery_id, Winner.place == result['place'], Winner.account_number == result['new_account'] ) ) ) new_winner = new_winner_result.scalar_one_or_none() if new_winner: # Отправляем уведомление новому победителю owner = await AccountService.get_account_owner(session, new_winner.account_number) if owner and owner.telegram_id: # Создаем токен верификации await WinnerNotificationService.create_verification_token( session, new_winner.id ) # Формируем сообщение notification_message = ( f"🎉 Поздравляем! Ваш счет выиграл!\n\n" f"🎯 Розыгрыш: {lottery.title}\n" f"🏆 Место: {new_winner.place}\n" f"🎁 Приз: {new_winner.prize}\n" f"💳 Счет: {new_winner.account_number}\n\n" f"⏰ **У вас есть 24 часа для подтверждения!**\n\n" f"Нажмите кнопку ниже, чтобы подтвердить получение приза." ) keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton( text="✅ Подтвердить получение приза", callback_data=f"confirm_win_{new_winner.id}" )] ]) try: await message.bot.send_message( owner.telegram_id, notification_message, reply_markup=keyboard, parse_mode="Markdown" ) new_winner.is_notified = True await session.commit() except: pass # Формируем отчет для админа text = f"🔄 **Повторный розыгрыш завершен!**\n\n" text += f"🎯 Розыгрыш: {lottery.title}\n" text += f"📊 Переиграно мест: {len(redraw_results)}\n\n" for result in redraw_results: text += f"🏆 {result['place']} место - {result['prize']}\n" text += f" ❌ Было: {result['old_account']}\n" text += f" ✅ Стало: {result['new_account']}\n\n" text += "📨 Новым победителям отправлены уведомления" await message.answer(text, parse_mode="Markdown") except Exception as e: await message.answer(f"❌ Ошибка: {str(e)}") @router.callback_query(F.data.startswith("confirm_win_")) async def confirm_winner_callback(callback_query): """Обработка подтверждения выигрыша победителем""" from aiogram.types import CallbackQuery winner_id = int(callback_query.data.split("_")[-1]) async with async_session_maker() as session: # Получаем информацию о победителе winner_result = await session.execute( select(Winner).where(Winner.id == winner_id) ) winner = winner_result.scalar_one_or_none() if not winner: await callback_query.answer("❌ Победитель не найден", show_alert=True) return if winner.is_claimed: await callback_query.answer( "✅ Этот выигрыш уже подтвержден!", show_alert=True ) return # Проверяем, что пользователь является владельцем счёта if winner.account_number: owner = await AccountService.get_account_owner(session, winner.account_number) if not owner or owner.telegram_id != callback_query.from_user.id: await callback_query.answer( "❌ Вы не являетесь владельцем этого счёта", show_alert=True ) return # Проверяем срок действия (24 часа с момента создания winner) if winner.created_at: time_since_creation = datetime.now(timezone.utc) - winner.created_at if time_since_creation > timedelta(hours=24): await callback_query.answer( "❌ Срок подтверждения истёк (24 часа). Приз будет разыгран заново.", show_alert=True ) return # Подтверждаем выигрыш winner.is_claimed = True winner.claimed_at = datetime.now(timezone.utc) await session.commit() # Получаем данные о розыгрыше и пользователе lottery = await LotteryService.get_lottery(session, winner.lottery_id) # Получаем информацию о пользователе owner = None if winner.account_number: owner = await AccountService.get_account_owner(session, winner.account_number) elif winner.user_id: user_result = await session.execute( select(User).where(User.id == winner.user_id) ) owner = user_result.scalar_one_or_none() # Формируем отображаемое имя display_name = "Пользователь" if owner: if owner.nickname: display_name = owner.nickname elif owner.username: display_name = f"@{owner.username}" elif owner.first_name: display_name = owner.first_name # Отправляем подтверждение пользователю confirmation_text = ( f"✅ **Выигрыш подтвержден!**\n\n" f"🎯 Розыгрыш: {lottery.title}\n" f"🏆 Место: {winner.place}\n" f"🎁 Приз: {winner.prize}\n" f"💳 Счет: {winner.account_number}\n\n" f"📞 С вами свяжется администратор для вручения приза.\n" f"Спасибо за участие!" ) await callback_query.message.edit_text( confirmation_text, parse_mode="Markdown" ) # Уведомляем админов с nickname и клубной картой for admin_id in ADMIN_IDS: try: # Формируем информацию для админа user_info = display_name if owner and owner.club_card_number: user_info = f"{display_name} (карта: {owner.club_card_number})" admin_text = ( f"✅ **Подтверждение выигрыша**\n\n" f"👤 Пользователь: {user_info}\n" f"🎯 Розыгрыш: {lottery.title}\n" f"🏆 Место: {winner.place}\n" f"🎁 Приз: {winner.prize}\n" f"💳 Счет: {winner.account_number}" ) from aiogram import Bot from src.core.config import BOT_TOKEN bot = Bot(token=BOT_TOKEN) await bot.send_message(admin_id, admin_text, parse_mode="Markdown") except Exception as e: import logging logging.getLogger(__name__).error(f"Ошибка отправки админу {admin_id}: {e}") await callback_query.answer("✅ Выигрыш подтвержден!", show_alert=True)