251 lines
12 KiB
Python
251 lines
12 KiB
Python
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"<b>{p1_name}</b> ({p1_choice_ru}) побеждает! 🎉\n"
|
||
f"<i>({p2_name} не сделал(а) свой ход)</i>"
|
||
)
|
||
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"<b>{p2_name}</b> ({p2_choice_ru}) побеждает! 🎉\n"
|
||
f"<i>({p1_name} не сделал(а) свой ход)</i>"
|
||
)
|
||
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"<b>{p1_name}</b> ({p1_choice_ru}) побеждает <b>{p2_name}</b> ({p2_choice_ru})!\n{p1_name} сегодня любит больше! ❤️"
|
||
elif winner_relation == 2:
|
||
winner_db_id = updated_game.player2_id
|
||
result_text = f"<b>{p2_name}</b> ({p2_choice_ru}) побеждает <b>{p1_name}</b> ({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})") |