import logging from datetime import date, timedelta from typing import Optional, Literal from aiogram import Router, types, F, Bot from aiogram.filters import Command from bot.database.models import User as DbUser, Game as DbGame, GameChoice from bot.database.db import ( create_or_get_today_game, update_game_choice, get_user_by_id, finish_game, update_streak, get_game_by_id, get_game_on_date ) from bot.keyboards.game_keyboard import get_game_choice_keyboard, GameChoiceCallback from bot.utils.game_logic import determine_winner, CHOICE_NAMES_RU from bot.utils.helpers import get_partner router = Router() async def check_and_resolve_yesterdays_game(user_id: int, partner_id: int, bot: Bot) -> bool: """ Проверяет наличие незавершенной игры за вчерашний день и завершает ее по правилам. Возвращает True, если игра была найдена и обработана, иначе False. """ yesterday = date.today() - timedelta(days=1) game = await get_game_on_date(user_id, partner_id, yesterday) if not game or game.is_finished: return False logging.info(f"Found unfinished game from yesterday ({yesterday}) for users {user_id} and {partner_id}. Resolving...") p1_id = game.player1_id p2_id = game.player2_id p1_choice = game.player1_choice p2_choice = game.player2_choice p1_user = await get_user_by_id(p1_id) p2_user = await get_user_by_id(p2_id) if not p1_user or not p2_user: logging.error(f"Could not find user data for yesterday's game {game.id}") await finish_game(game.id, None) return True p1_name = p1_user.username or f"Игрок {p1_user.telegram_id}" p2_name = p2_user.username or f"Игрок {p2_user.telegram_id}" winner_db_id: Optional[int] = None result_message: str = "" p1_win: bool = False p2_win: bool = False if p1_choice and not p2_choice: winner_db_id = p1_id p1_choice_ru = CHOICE_NAMES_RU.get(p1_choice, p1_choice) result_message = ( f"⏳ Вчерашняя игра ({yesterday}):\n" f"{p1_name} ({p1_choice_ru}) побеждает! 🎉\n" f"({p2_name} не сделал(а) свой ход)" ) p1_win = True p2_win = False logging.info(f"Yesterday's game {game.id}: Player 1 ({p1_id}) wins by default.") elif not p1_choice and p2_choice: winner_db_id = p2_id p2_choice_ru = CHOICE_NAMES_RU.get(p2_choice, p2_choice) result_message = ( f"⏳ Вчерашняя игра ({yesterday}):\n" f"{p2_name} ({p2_choice_ru}) побеждает! 🎉\n" f"({p1_name} не сделал(а) свой ход)" ) p1_win = False p2_win = True logging.info(f"Yesterday's game {game.id}: Player 2 ({p2_id}) wins by default.") else: winner_db_id = None result_message = ( f"⏳ Вчерашняя игра ({yesterday}):\n" f"Ничья! Игра аннулирована, так как не все сделали ход. 🤷‍♀️🤷‍♂️" ) p1_win = False p2_win = False logging.info(f"Yesterday's game {game.id}: Draw (at least one player did not choose).") await finish_game(game.id, winner_db_id) await update_streak(p1_id, win=p1_win) await update_streak(p2_id, win=p2_win) full_result_message = result_message + "\n\nНачинаем игру на сегодня! /play" try: await bot.send_message(p1_user.telegram_id, full_result_message, parse_mode="HTML") except Exception as e: logging.error(f"Failed to send yesterday result to user {p1_user.telegram_id}: {e}") try: await bot.send_message(p2_user.telegram_id, full_result_message, parse_mode="HTML") except Exception as e: logging.error(f"Failed to send yesterday result to user {p2_user.telegram_id}: {e}") return True @router.message(Command("play")) async def handle_play(message: types.Message, db_user: DbUser, bot: Bot): """Обработчик команды /play. Проверяет вчерашнюю игру и предлагает сделать ход.""" partner = await get_partner(db_user.id) if not partner: await message.answer("Для игры нужен партнер. Дождитесь, пока второй пользователь присоединится.") return try: await check_and_resolve_yesterdays_game(db_user.id, partner.id, bot) except Exception as e: logging.exception(f"Error checking/resolving yesterday's game for {db_user.id} and {partner.id}") game = await create_or_get_today_game(db_user.id, partner.id) if not game: await message.answer("Не удалось начать игру. Попробуйте позже.") logging.error(f"Failed to create/get game for users {db_user.id} and {partner.id}") return if game.is_finished: await message.answer("Вы уже сыграли сегодня! Приходите завтра ❤️") return current_player_choice = game.player1_choice if game.player1_id == db_user.id else game.player2_choice if current_player_choice: choice_name_ru = CHOICE_NAMES_RU.get(current_player_choice, current_player_choice) await message.answer(f"Вы уже сделали свой ход сегодня ({choice_name_ru}). Ожидаем хода партнера! 😉") return await message.answer("Кто больше любит сегодня? 😉 Сделайте свой выбор:", reply_markup=get_game_choice_keyboard()) @router.callback_query(GameChoiceCallback.filter()) async def handle_game_choice( callback: types.CallbackQuery, callback_data: GameChoiceCallback, db_user: DbUser, bot: Bot ): """Обработчик нажатия на кнопку выбора хода.""" choice: GameChoice = callback_data.choice choice_name_ru = CHOICE_NAMES_RU.get(choice, choice) partner = await get_partner(db_user.id) if not partner: await callback.answer("Ошибка: не найден партнер.", show_alert=True) await callback.message.edit_text("Не удалось обработать ваш ход: партнер не найден.") return game = await create_or_get_today_game(db_user.id, partner.id) if not game: await callback.answer("Ошибка: не найдена текущая игра.", show_alert=True) await callback.message.edit_text("Не удалось обработать ваш ход: игра не найдена.") logging.error(f"Game not found for users {db_user.id} and {partner.id} during callback") return if game.is_finished: await callback.answer("Игра уже завершена.", show_alert=True) await callback.message.edit_text("Вы опоздали, игра на сегодня уже закончилась!" ) return player_field = "player1_choice" if game.player1_id == db_user.id else "player2_choice" current_choice = getattr(game, player_field) if current_choice is not None: current_choice_ru = CHOICE_NAMES_RU.get(current_choice, current_choice) await callback.answer("Вы уже сделали свой ход.", show_alert=True) await callback.message.edit_text(f"Вы уже выбрали: {current_choice_ru}\nОжидаем ход партнера... ✨") return updated = await update_game_choice(game.id, db_user.id, choice) if not updated: await callback.answer("Ошибка: не удалось сохранить ваш выбор.", show_alert=True) await callback.message.edit_text("Произошла ошибка при сохранении вашего хода. Попробуйте еще раз.") logging.error(f"Failed to update choice for game {game.id}, user {db_user.id}") return await callback.answer(f"Вы выбрали: {choice_name_ru}") await callback.message.edit_text(f"Вы выбрали: {choice_name_ru}\nОжидаем ход партнера... ✨") updated_game = await get_game_by_id(game.id) if not updated_game: logging.error(f"Failed to fetch updated game {game.id} after choice update.") return if updated_game.player1_choice and updated_game.player2_choice: winner_relation = determine_winner(updated_game.player1_choice, updated_game.player2_choice) winner_db_id: Optional[int] = None result_text = "" p1_choice_ru = CHOICE_NAMES_RU[updated_game.player1_choice] p2_choice_ru = CHOICE_NAMES_RU[updated_game.player2_choice] p1_user = await get_user_by_id(updated_game.player1_id) p2_user = await get_user_by_id(updated_game.player2_id) if not p1_user or not p2_user: logging.error(f"Could not find user data for game {updated_game.id}") await bot.send_message(db_user.telegram_id, "Ошибка: не удалось получить данные игроков для завершения игры.") await bot.send_message(partner.telegram_id, "Ошибка: не удалось получить данные игроков для завершения игры.") return p1_name = p1_user.username or f"Игрок {p1_user.telegram_id}" p2_name = p2_user.username or f"Игрок {p2_user.telegram_id}" if winner_relation == 1: winner_db_id = updated_game.player1_id result_text = f"{p1_name} ({p1_choice_ru}) побеждает {p2_name} ({p2_choice_ru})!\n{p1_name} сегодня любит больше! ❤️" elif winner_relation == 2: winner_db_id = updated_game.player2_id result_text = f"{p2_name} ({p2_choice_ru}) побеждает {p1_name} ({p1_choice_ru})!\n{p2_name} сегодня любит больше! ❤️" else: result_text = f"Ничья! Оба выбрали {p1_choice_ru}.\nСегодня вы любите друг друга одинаково сильно! 🥰" await finish_game(updated_game.id, winner_db_id) await update_streak(updated_game.player1_id, win=(winner_relation == 1)) await update_streak(updated_game.player2_id, win=(winner_relation == 2)) final_message = f"Игра за {updated_game.game_date.strftime('%d.%m.%Y')} завершена! 🎉\n\n{result_text}\n\nПосмотреть статистику: /stats" try: await bot.send_message(p1_user.telegram_id, final_message, parse_mode="HTML") except Exception as e: logging.error(f"Failed to send result to user {p1_user.telegram_id}: {e}") try: await bot.send_message(p2_user.telegram_id, final_message, parse_mode="HTML") except Exception as e: logging.error(f"Failed to send result to user {p2_user.telegram_id}: {e}") else: partner_user = await get_user_by_id(partner.id) if partner_user: current_user_name = db_user.username or f"Игрок {db_user.telegram_id}" try: await bot.send_message( partner_user.telegram_id, f"Ваш партнер, {current_user_name}, сделал свой ход! 😉\nТеперь ваша очередь решить, кто любит больше! Используйте команду /play, чтобы сделать ход." ) except Exception as e: logging.error(f"Failed to notify partner {partner_user.telegram_id}: {e}") else: logging.error(f"Could not find partner user data for notification (partner_id: {partner.id})")