Fix errors
This commit is contained in:
parent
5dd2c798e5
commit
3e133adf63
|
@ -86,3 +86,5 @@ dmypy.json
|
|||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
data/
|
|
@ -1,11 +1,14 @@
|
|||
import aiosqlite
|
||||
from typing import List, Optional
|
||||
from datetime import date
|
||||
import os
|
||||
import logging
|
||||
|
||||
from bot.config import settings
|
||||
from .models import User, Game, Streak, GameChoice
|
||||
|
||||
DATABASE_URL = settings.database_name
|
||||
os.makedirs(os.path.dirname(DATABASE_URL), exist_ok=True)
|
||||
|
||||
async def init_db():
|
||||
"""Инициализирует базу данных и создает таблицы, если их нет."""
|
||||
|
@ -58,7 +61,12 @@ async def add_user(telegram_id: int, username: Optional[str]) -> Optional[User]:
|
|||
(telegram_id, username)
|
||||
)
|
||||
await db.commit()
|
||||
user_id = (await db.execute("SELECT last_insert_rowid()")).fetchone()[0]
|
||||
cursor = await db.execute("SELECT last_insert_rowid()")
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
logging.error(f"Could not retrieve last_insert_rowid after inserting user {telegram_id}")
|
||||
return None
|
||||
user_id = row[0]
|
||||
|
||||
await ensure_streak_record(user_id)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import logging
|
||||
from datetime import date, timedelta
|
||||
from typing import Optional, Literal
|
||||
from typing import Optional, Literal, Dict, Any
|
||||
|
||||
from aiogram import Router, types, F, Bot
|
||||
from aiogram.filters import Command
|
||||
from aiogram.filters import Command, or_f
|
||||
|
||||
from bot.database.models import User as DbUser, Game as DbGame, GameChoice
|
||||
from bot.database.db import (
|
||||
|
@ -16,6 +16,7 @@ from bot.database.db import (
|
|||
get_game_on_date
|
||||
)
|
||||
from bot.keyboards.game_keyboard import get_game_choice_keyboard, GameChoiceCallback
|
||||
from bot.keyboards.reply_keyboards import PLAY_BUTTON_TEXT
|
||||
from bot.utils.game_logic import determine_winner, CHOICE_NAMES_RU
|
||||
from bot.utils.helpers import get_partner
|
||||
|
||||
|
@ -106,30 +107,40 @@ async def check_and_resolve_yesterdays_game(user_id: int, partner_id: int, bot:
|
|||
|
||||
return True
|
||||
|
||||
@router.message(Command("play"))
|
||||
async def handle_play(message: types.Message, db_user: DbUser, bot: Bot):
|
||||
@router.message(or_f(Command("play"), F.text == PLAY_BUTTON_TEXT))
|
||||
async def handle_play(
|
||||
message: types.Message,
|
||||
bot: Bot,
|
||||
**kwargs
|
||||
):
|
||||
"""Обработчик команды /play. Проверяет вчерашнюю игру и предлагает сделать ход."""
|
||||
partner = await get_partner(db_user.id)
|
||||
user_db_obj: Optional[DbUser] = kwargs.get('user_db_obj')
|
||||
if not user_db_obj:
|
||||
logging.error(f"Error in handle_play: user_db_obj not found in kwargs for user {message.from_user.id}")
|
||||
await message.answer("Произошла ошибка при получении ваших данных.")
|
||||
return
|
||||
|
||||
partner = await get_partner(user_db_obj.id)
|
||||
if not partner:
|
||||
await message.answer("Для игры нужен партнер. Дождитесь, пока второй пользователь присоединится.")
|
||||
return
|
||||
|
||||
try:
|
||||
await check_and_resolve_yesterdays_game(db_user.id, partner.id, bot)
|
||||
await check_and_resolve_yesterdays_game(user_db_obj.id, partner.id, bot)
|
||||
except Exception as e:
|
||||
logging.exception(f"Error checking/resolving yesterday's game for {db_user.id} and {partner.id}")
|
||||
logging.exception(f"Error checking/resolving yesterday's game for {user_db_obj.id} and {partner.id}")
|
||||
|
||||
game = await create_or_get_today_game(db_user.id, partner.id)
|
||||
game = await create_or_get_today_game(user_db_obj.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}")
|
||||
logging.error(f"Failed to create/get game for users {user_db_obj.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
|
||||
current_player_choice = game.player1_choice if game.player1_id == user_db_obj.id else game.player2_choice
|
||||
|
||||
if current_player_choice:
|
||||
choice_name_ru = CHOICE_NAMES_RU.get(current_player_choice, current_player_choice)
|
||||
|
@ -138,29 +149,38 @@ async def handle_play(message: types.Message, db_user: DbUser, bot: Bot):
|
|||
|
||||
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
|
||||
bot: Bot,
|
||||
**kwargs
|
||||
):
|
||||
"""Обработчик нажатия на кнопку выбора хода."""
|
||||
user_db_obj: Optional[DbUser] = kwargs.get('user_db_obj')
|
||||
if not user_db_obj:
|
||||
logging.error(f"Error in handle_game_choice: user_db_obj not found in kwargs for user {callback.from_user.id}")
|
||||
await callback.answer("Произошла ошибка при получении ваших данных.", show_alert=True)
|
||||
try:
|
||||
await callback.message.edit_text("Произошла ошибка при обработке вашего выбора.")
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
choice: GameChoice = callback_data.choice
|
||||
choice_name_ru = CHOICE_NAMES_RU.get(choice, choice)
|
||||
|
||||
partner = await get_partner(db_user.id)
|
||||
partner = await get_partner(user_db_obj.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)
|
||||
game = await create_or_get_today_game(user_db_obj.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")
|
||||
logging.error(f"Game not found for users {user_db_obj.id} and {partner.id} during callback")
|
||||
return
|
||||
|
||||
if game.is_finished:
|
||||
|
@ -168,7 +188,7 @@ async def handle_game_choice(
|
|||
await callback.message.edit_text("Вы опоздали, игра на сегодня уже закончилась!" )
|
||||
return
|
||||
|
||||
player_field = "player1_choice" if game.player1_id == db_user.id else "player2_choice"
|
||||
player_field = "player1_choice" if game.player1_id == user_db_obj.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)
|
||||
|
@ -176,11 +196,11 @@ async def handle_game_choice(
|
|||
await callback.message.edit_text(f"Вы уже выбрали: {current_choice_ru}\nОжидаем ход партнера... ✨")
|
||||
return
|
||||
|
||||
updated = await update_game_choice(game.id, db_user.id, choice)
|
||||
updated = await update_game_choice(game.id, user_db_obj.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}")
|
||||
logging.error(f"Failed to update choice for game {game.id}, user {user_db_obj.id}")
|
||||
return
|
||||
|
||||
await callback.answer(f"Вы выбрали: {choice_name_ru}")
|
||||
|
@ -204,7 +224,7 @@ async def handle_game_choice(
|
|||
|
||||
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(user_db_obj.telegram_id, "Ошибка: не удалось получить данные игроков для завершения игры.")
|
||||
await bot.send_message(partner.telegram_id, "Ошибка: не удалось получить данные игроков для завершения игры.")
|
||||
return
|
||||
|
||||
|
@ -239,7 +259,7 @@ async def handle_game_choice(
|
|||
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}"
|
||||
current_user_name = user_db_obj.username or f"Игрок {user_db_obj.telegram_id}"
|
||||
try:
|
||||
await bot.send_message(
|
||||
partner_user.telegram_id,
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
from aiogram import Router, types
|
||||
from aiogram import Router, types, F
|
||||
from aiogram.filters import CommandStart
|
||||
|
||||
from bot.database.models import User as DbUser
|
||||
from typing import Optional
|
||||
import logging
|
||||
# Импортируем новую клавиатуру
|
||||
from bot.keyboards.reply_keyboards import get_main_menu_keyboard
|
||||
|
||||
router = Router()
|
||||
|
||||
@router.message(CommandStart())
|
||||
async def handle_start(message: types.Message, db_user: DbUser):
|
||||
async def handle_start(message: types.Message, **kwargs):
|
||||
"""Обработчик команды /start для аутентифицированных пользователей."""
|
||||
username = db_user.username or f"Пользователь {db_user.telegram_id}"
|
||||
user_db_obj: Optional[DbUser] = kwargs.get('user_db_obj')
|
||||
if not user_db_obj:
|
||||
logging.error(f"Error in handle_start: user_db_obj not found in kwargs for user {message.from_user.id}")
|
||||
await message.answer("Произошла ошибка при получении ваших данных.")
|
||||
return
|
||||
|
||||
username = user_db_obj.username or f"Пользователь {user_db_obj.telegram_id}"
|
||||
# Отправляем приветствие и клавиатуру
|
||||
await message.answer(
|
||||
f"Привет, {username}! ✨\n\n"
|
||||
f"Это бот 'Кто больше любит'. Каждый день вы со своей парой можете сыграть в Камень-Ножницы-Бумага, чтобы определить, кто любит больше! 😉\n\n"
|
||||
f"Чтобы сделать ход, используй кнопки ниже (они появятся, когда придет время игры).\n"
|
||||
f"Для просмотра статистики используй команду /stats.\n\n"
|
||||
f"Удачи! ❤️"
|
||||
f"Это бот 'Кто больше любит'. Используй кнопки ниже, чтобы играть или смотреть статистику! 😉",
|
||||
reply_markup=get_main_menu_keyboard()
|
||||
)
|
||||
|
||||
# TODO: Возможно, убрать подробное описание правил из /start,
|
||||
# так как теперь есть кнопки для основных действий.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import logging
|
||||
import typing
|
||||
from aiogram import Router, types
|
||||
from aiogram.filters import Command
|
||||
from aiogram.filters import Command, or_f
|
||||
from aiogram import F
|
||||
|
||||
from bot.database.models import User as DbUser
|
||||
from bot.database.db import (
|
||||
|
@ -11,31 +13,38 @@ from bot.database.db import (
|
|||
get_user_by_id
|
||||
)
|
||||
from bot.utils.helpers import get_partner
|
||||
from bot.keyboards.reply_keyboards import STATS_BUTTON_TEXT
|
||||
|
||||
router = Router()
|
||||
|
||||
@router.message(Command("stats"))
|
||||
async def handle_stats(message: types.Message, db_user: DbUser):
|
||||
@router.message(or_f(Command("stats"), F.text == STATS_BUTTON_TEXT))
|
||||
async def handle_stats(message: types.Message, **kwargs):
|
||||
"""Обработчик команды /stats. Выводит статистику игры."""
|
||||
partner = await get_partner(db_user.id)
|
||||
user_db_obj: typing.Optional[DbUser] = kwargs.get('user_db_obj')
|
||||
if not user_db_obj:
|
||||
logging.error(f"Error in handle_stats: user_db_obj not found in kwargs for user {message.from_user.id}")
|
||||
await message.answer("Произошла ошибка при получении ваших данных.")
|
||||
return
|
||||
|
||||
partner = await get_partner(user_db_obj.id)
|
||||
if not partner:
|
||||
await message.answer("Не могу показать статистику, так как не найден партнер.")
|
||||
return
|
||||
|
||||
try:
|
||||
total_games = await get_total_games_count(db_user.id, partner.id)
|
||||
user_wins = await get_wins_count(db_user.id)
|
||||
total_games = await get_total_games_count(user_db_obj.id, partner.id)
|
||||
user_wins = await get_wins_count(user_db_obj.id)
|
||||
partner_wins = await get_wins_count(partner.id)
|
||||
draws = await get_draw_count(db_user.id, partner.id)
|
||||
user_streak = await get_streak(db_user.id)
|
||||
draws = await get_draw_count(user_db_obj.id, partner.id)
|
||||
user_streak = await get_streak(user_db_obj.id)
|
||||
partner_streak = await get_streak(partner.id)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception(f"Error fetching stats for user {db_user.id} and partner {partner.id}")
|
||||
logging.exception(f"Error fetching stats for user {user_db_obj.id} and partner {partner.id}")
|
||||
await message.answer("Произошла ошибка при получении статистики. Попробуйте позже.")
|
||||
return
|
||||
|
||||
user_name = db_user.username or f"Игрок {db_user.telegram_id}"
|
||||
user_name = user_db_obj.username or f"Игрок {user_db_obj.telegram_id}"
|
||||
partner_name = partner.username or f"Игрок {partner.telegram_id}"
|
||||
|
||||
stats_text = (
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
from aiogram import Router, types, F
|
||||
|
||||
router = Router()
|
||||
|
||||
@router.message(F.text)
|
||||
async def catch_all_messages(message: types.Message):
|
||||
"""Ловит все текстовые сообщения без явных команд."""
|
||||
if message.text.startswith("/"):
|
||||
return
|
||||
pass
|
|
@ -0,0 +1,23 @@
|
|||
# bot/keyboards/reply_keyboards.py
|
||||
|
||||
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
|
||||
|
||||
# Определим тексты кнопок как константы для удобства
|
||||
PLAY_BUTTON_TEXT = "Играть ▶️"
|
||||
STATS_BUTTON_TEXT = "Статистика 📊"
|
||||
|
||||
def get_main_menu_keyboard() -> ReplyKeyboardMarkup:
|
||||
"""Возвращает основную клавиатуру с кнопками Играть и Статистика."""
|
||||
kb = [
|
||||
[
|
||||
KeyboardButton(text=PLAY_BUTTON_TEXT),
|
||||
KeyboardButton(text=STATS_BUTTON_TEXT)
|
||||
]
|
||||
]
|
||||
keyboard = ReplyKeyboardMarkup(
|
||||
keyboard=kb,
|
||||
resize_keyboard=True, # Делает кнопки более компактными
|
||||
# one_time_keyboard=False # Клавиатура будет постоянной (это значение по умолчанию)
|
||||
input_field_placeholder="Нажмите Играть или Статистика"
|
||||
)
|
||||
return keyboard
|
14
bot/main.py
14
bot/main.py
|
@ -4,8 +4,9 @@ import sys
|
|||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.enums import ParseMode
|
||||
from aiogram.client.default import DefaultBotProperties
|
||||
|
||||
from bot.handlers import start, game, stats
|
||||
from bot.handlers import start, game, stats, text
|
||||
from bot.middlewares.auth_middleware import AuthMiddleware, load_initial_users
|
||||
from bot.database.db import init_db
|
||||
from bot.config import settings
|
||||
|
@ -16,15 +17,20 @@ async def main() -> None:
|
|||
|
||||
await load_initial_users()
|
||||
|
||||
bot = Bot(token=settings.bot_token.get_secret_value(), parse_mode=ParseMode.HTML)
|
||||
bot = Bot(
|
||||
token=settings.bot_token.get_secret_value(),
|
||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML)
|
||||
)
|
||||
dp = Dispatcher()
|
||||
|
||||
dp.update.outer_middleware(AuthMiddleware())
|
||||
dp.message.middleware(AuthMiddleware())
|
||||
dp.callback_query.middleware(AuthMiddleware())
|
||||
|
||||
|
||||
dp.include_router(start.router)
|
||||
dp.include_router(game.router)
|
||||
dp.include_router(stats.router)
|
||||
# TODO: Раскомментировать и добавить роутеры, когда они будут готовы
|
||||
dp.include_router(text.router)
|
||||
|
||||
logging.info("Starting bot...")
|
||||
await bot.delete_webhook(drop_pending_updates=True)
|
||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
|||
from typing import Callable, Dict, Any, Awaitable, Set
|
||||
|
||||
from aiogram import BaseMiddleware
|
||||
from aiogram.types import Message, TelegramObject, User as AiogramUser
|
||||
from aiogram.types import Message, CallbackQuery, TelegramObject, User as AiogramUser
|
||||
|
||||
from bot.config import settings
|
||||
from bot.database.db import get_all_users, add_user, get_user_by_telegram_id
|
||||
|
@ -36,7 +36,7 @@ class AuthMiddleware(BaseMiddleware):
|
|||
if not initial_users_loaded:
|
||||
await load_initial_users()
|
||||
|
||||
if not isinstance(event, Message):
|
||||
if not isinstance(event, (Message, CallbackQuery)):
|
||||
return await handler(event, data)
|
||||
|
||||
aiogram_user: AiogramUser = data.get('event_from_user')
|
||||
|
@ -49,7 +49,8 @@ class AuthMiddleware(BaseMiddleware):
|
|||
if telegram_id in authenticated_user_ids:
|
||||
db_user = await get_user_by_telegram_id(telegram_id)
|
||||
if db_user:
|
||||
data['db_user'] = db_user
|
||||
data['user_db_obj'] = db_user
|
||||
logging.info(f"AuthMiddleware: Found authenticated user {telegram_id}. Passing user_db_obj to handler. Data keys: {list(data.keys())}")
|
||||
return await handler(event, data)
|
||||
else:
|
||||
logging.warning(f"User {telegram_id} is in authenticated_user_ids but not found in DB.")
|
||||
|
@ -60,8 +61,9 @@ class AuthMiddleware(BaseMiddleware):
|
|||
db_user = await add_user(telegram_id, aiogram_user.username)
|
||||
if db_user:
|
||||
authenticated_user_ids.add(telegram_id)
|
||||
data['db_user'] = db_user
|
||||
data['user_db_obj'] = db_user
|
||||
logging.info(f"User {telegram_id} ({aiogram_user.username}) authenticated successfully. Total users: {len(authenticated_user_ids)}")
|
||||
await event.answer(f"Добро пожаловать, {db_user.username or f'Пользователь {db_user.telegram_id}'}! Вы успешно аутентифицированы. Используйте /start для начала.")
|
||||
return
|
||||
else:
|
||||
logging.error(f"Failed to add user {telegram_id} to DB after password check.")
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from typing import Optional
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
# from aiogram.types import TelegramObject # Больше не нужен
|
||||
|
||||
from bot.database.db import get_all_users
|
||||
from bot.database.models import User as DbUser
|
||||
# import logging # Убираем импорт, если он больше нигде не используется
|
||||
|
||||
async def get_partner(current_user_id: int) -> Optional[DbUser]:
|
||||
"""Находит второго зарегистрированного пользователя (партнера)."""
|
||||
|
@ -12,3 +15,7 @@ async def get_partner(current_user_id: int) -> Optional[DbUser]:
|
|||
if user.id is not None and user.id != current_user_id:
|
||||
return user
|
||||
return None
|
||||
|
||||
# УДАЛЯЕМ ФУНКЦИЮ ЗАВИСИМОСТИ
|
||||
# async def get_current_db_user(data: Dict[str, Any]) -> DbUser:
|
||||
# ...
|
Loading…
Reference in New Issue