nards/bot/main.py
2024-12-21 23:12:17 +07:00

211 lines
9.1 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 Bot, Dispatcher, types, F
from aiogram.filters import Command
from aiogram.types import WebAppInfo, KeyboardButton, ReplyKeyboardMarkup
from bot.database import Database
import json
import asyncio
from bot.config import BOT_TOKEN, WEBAPP_URL
import logging
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Инициализация бота и диспетчера
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
db = Database()
# Упрощенная клавиатура
def get_main_keyboard():
keyboard = [
[
KeyboardButton(
text="Записать броски 🎲",
web_app=WebAppInfo(url=WEBAPP_URL)
)
],
[KeyboardButton(text="Статистика 📊")]
]
return ReplyKeyboardMarkup(keyboard=keyboard, resize_keyboard=True)
@dp.message(Command("start"))
async def start_command(message: types.Message):
logger.info(f"User {message.from_user.id} ({message.from_user.username}) started the bot")
await message.answer(
"🎲 Добро пожаловать в счётчик кубиков для нард!\n\n"
"Нажмите «Записать броски» чтобы начать новую игру.",
reply_markup=get_main_keyboard()
)
# Обновляем функцию подсчета статистики в handle_webapp_data
def calculate_throw_sum(throw):
dice1, dice2 = throw['dice']
base_sum = (dice1 + dice2) * 2 if dice1 == dice2 else dice1 + dice2
return base_sum - (throw.get('unusedPoints', 0))
@dp.message(F.web_app_data)
async def handle_webapp_data(message: types.Message):
try:
user = message.from_user
logger.info(f"Received webapp data from user {user.id} ({user.username})")
data = json.loads(message.web_app_data.data)
if data['type'] == 'game_session':
# Создаем новую игру
game_id = db.create_game(user.id, user.username)
logger.info(f"Created new game {game_id} for user {user.id}")
# Записываем все броски
throws = data['throws']
for throw in throws:
db.add_throw(
game_id,
throw['dice'][0],
throw['dice'][1],
throw.get('unusedPoints', 0)
)
# Завершаем игру
db.end_game(game_id)
logger.info(f"Game {game_id} completed with {len(throws)} throws")
# Анализ комбинаций
combinations = {}
for throw in throws:
dice = sorted([throw['dice'][0], throw['dice'][1]])
key = f"{dice[0]}-{dice[1]}"
combinations[key] = combinations.get(key, 0) + 1
# Сортируем комбинации по частоте
sorted_combinations = sorted(
combinations.items(),
key=lambda x: (-x[1], x[0]) # Сортировка по убыванию частоты, потом по комбинации
)
# Подсчет статистики
total_throws = len(throws)
total_base_sum = sum((t['dice'][0] + t['dice'][1]) * 2 if t['dice'][0] == t['dice'][1] else t['dice'][0] + t['dice'][1] for t in throws)
total_final_sum = sum(calculate_throw_sum(t) for t in throws)
avg_sum = total_base_sum / total_throws if total_throws > 0 else 0
avg_sum_with_minus = total_final_sum / total_throws if total_throws > 0 else 0
doubles = sum(1 for t in throws if t['dice'][0] == t['dice'][1])
# Находим максимальный и минимальный броски
throws_with_sums = [(t, calculate_throw_sum(t)) for t in throws]
max_throw = max(throws_with_sums, key=lambda x: x[1])
min_throw = min(throws_with_sums, key=lambda x: x[1])
# Формируем сообщение
response = "🎲 Итоги игры\n\n"
# Основная статистика
response += "📊 Общая статистика:\n"
response += f"• Бросков: {total_throws}\n"
response += f"• Сумма очков: {total_final_sum}"
if total_base_sum != total_final_sum:
unused_sum = total_base_sum - total_final_sum
response += f" (потенциал: {total_base_sum})\n"
response += f"• Неиспользовано: {unused_sum} очков\n"
response += f"• Эффективность: {(total_final_sum / total_base_sum * 100):.1f}%\n"
else:
response += "\n"
response += f"• Дублей: {doubles} ({(doubles/total_throws*100):.1f}%)\n"
# Средние значения
response += "\n📈 Средние значения:\n"
response += f"За бросок: {avg_sum_with_minus:.1f}"
if total_base_sum != total_final_sum:
response += f" (макс. {avg_sum:.1f})\n"
else:
response += "\n"
# Топ комбинаций
response += "\n🎯 Частые комбинации:\n"
for combo, count in sorted_combinations[:5]: # Показываем топ-5 комбинаций
percentage = (count / total_throws) * 100
is_double = combo[0] == combo[2] # Проверяем, дубль ли это
response += f"{combo} {'🎯' if is_double else ''}: {count}x ({percentage:.1f}%)\n"
# Последние броски
response += "\n🎲 Последние броски:\n"
for i, throw in enumerate(throws[:5]):
sum_value = calculate_throw_sum(throw)
is_double = throw['dice'][0] == throw['dice'][1]
unused = throw.get('unusedPoints', 0)
response += f"{i+1}. {throw['dice'][0]}-{throw['dice'][1]}"
if is_double:
response += " 🎯"
if unused > 0:
response += f" = {sum_value} (-{unused})"
else:
response += f" = {sum_value}"
response += "\n"
if total_throws > 5:
response += f"... и ещё {total_throws - 5} бросков"
await message.answer(response)
logger.info(f"Sent game statistics to user {user.id}")
except Exception as e:
logger.error(f"Error handling webapp data: {e}", exc_info=True)
await message.answer("❌ Произошла ошибка при обработке данных.")
@dp.message(Command("statistics"))
@dp.message(F.text == "Статистика 📊")
async def statistics_command(message: types.Message):
user_id = message.from_user.id
logger.info(f"User {user_id} requested statistics")
stats = db.get_statistics(user_id)
response = "📊 Подробная статистика:\n\n"
if stats['total_throws']:
total_base_sum = stats['total_base_sum'] or 0
total_final_sum = stats['total_final_sum'] or 0
total_unused = stats['total_unused'] or 0
avg_sum = total_final_sum / stats['total_throws']
response += (
f"🎲 Броски:\n"
f"Всего бросков: {stats['total_throws']}\n"
f"• Количество игр: {stats['total_games']}\n"
f"• Среднее бросков за игру: {stats['total_throws'] / stats['total_games']:.1f}\n\n"
f"🎯 Очки:\n"
f"• Общая сумма: {total_final_sum}\n"
f"• Сумма без минусов: {total_base_sum}\n"
f"• Средняя сумма за бросок: {avg_sum:.1f}\n"
f"• Эффективность: {(total_final_sum / total_base_sum * 100):.1f}%\n\n"
)
if total_unused > 0:
avg_unused = total_unused / stats['total_throws']
response += (
f"❌ Неиспользованные очки:\n"
f"Всего неиспользовано: {total_unused}\n"
f"В среднем за бросок: {avg_unused:.1f}\n"
f"• Процент потерь: {(total_unused / total_base_sum * 100):.1f}%\n"
)
else:
response += "У вас пока нет записанных бросков."
await message.answer(response)
logger.info(f"Sent statistics to user {user_id}")
# Запуск бота
async def main():
logger.info("Starting bot")
await dp.start_polling(bot)
if __name__ == '__main__':
asyncio.run(main())