EN language only

This commit is contained in:
vrubelroman 2025-11-12 23:20:01 +03:00
parent 3ec1fe614d
commit 3362bf89e2
4 changed files with 353 additions and 94 deletions

View file

@ -1,5 +1,6 @@
import asyncio
import logging
import sqlite3
from datetime import datetime, timedelta
from typing import Dict, Any, Optional
@ -17,6 +18,7 @@ from config import (
from database import Database
from lichess_api import LichessAPI
from formatters import StatsFormatter
from i18n import t
# Configure logging
logging.basicConfig(
@ -35,6 +37,20 @@ class LichessBot:
self.periodic_tasks = {} # Store periodic tasks
self.period_start_times = {} # Store start times for each gamer
self.application = None # Will be set when application is created
def get_user_language_from_update(self, update: Update) -> str:
"""Always return English language"""
# Update user info in database
user = update.effective_user
if user:
self.db.add_or_get_telegram_user(
user_id=user.id,
username=user.username,
first_name=user.first_name,
last_name=user.last_name,
language_code=None
)
return 'en'
async def start_existing_periodic_tasks(self):
"""Start periodic tasks for all user-gamer pairs that have periods set"""
@ -56,41 +72,17 @@ class LichessBot:
"""Start command handler"""
# Register user in database
user = update.effective_user
lang_code = user.language_code if user else None
self.db.add_or_get_telegram_user(
user_id=user.id,
username=user.username,
first_name=user.first_name,
last_name=user.last_name
last_name=user.last_name,
language_code=lang_code
)
await update.message.reply_text(
"Бот отслеживает игру одного или нескольких игроков на Lichess.\n"
"Он показывает рейтинги, дневную, вчерашнюю и недельную активность.\n"
"При указании user_token (с правом чтения задач) можно также получать данные по задачам.\n"
"Бот поддерживает автоматические проверки с заданным интервалом и отправляет отчёт, если за это время была активность.\n\n"
"Пример за неделю:\n"
"🧩 Задачи: 114 (✅ 81 - ❌ 33)\n\n"
"🔥 Blitz — 5 игр • 🔴 -10\n"
"Рейтинг: 2245\n"
"✅ Победы: 1\n"
"❌ Поражения: 3\n"
"🤝 Ничьи: 1\n\n"
"🐇 Rapid — 19 игр • 🟢 +20\n"
"Рейтинг: 2248\n"
"✅ Победы: 8\n"
"❌ Поражения: 4\n"
"🤝 Ничьи: 7\n\n"
"📋 Доступные команды:\n"
"/addgamer - Добавить игрока Lichess для отслеживания (только username)\n"
"/addtoken - Добавить игрока с токеном для получения данных по задачам\n"
"/getgamers - Выбрать активного игрока (для которого будут работать команды /today и тд)\n"
"/delgamer - Удалить игрока из списка отслеживаемых\n"
"/today - Статистика за сегодня\n"
"/yesterday - Статистика за вчера\n"
"/week - Статистика за неделю\n"
"/setperiod - Настроить периодические уведомления активного игрока\n"
"(активный игрок меняется в меню команды /getgamers)"
)
lang = self.get_user_language_from_update(update)
await update.message.reply_text(t('start_message', lang))
async def start_and_addgamer(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Start command that automatically launches addgamer"""
@ -101,16 +93,14 @@ class LichessBot:
async def addgamer_start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Start addgamer command - simple username only"""
await update.message.reply_text("👤 Введите Lichess username игрока для отслеживания:")
lang = self.get_user_language_from_update(update)
await update.message.reply_text(t('addgamer_prompt', lang))
return WAITING_FOR_USERNAME
async def addtoken_start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Start addtoken command - token required"""
await update.message.reply_text(
"🔑 Введите Lichess API токен, чтобы получать данные по задачам.\n"
"Токен создаётся в настройках профиля — дайте ему только право puzzle:read.\n"
"После этого будет добавлен игрок, соответствующий этому токену."
)
lang = self.get_user_language_from_update(update)
await update.message.reply_text(t('addtoken_prompt', lang))
return WAITING_FOR_TOKEN
async def handle_token(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
@ -127,11 +117,12 @@ class LichessBot:
user_gamers = self.db.get_user_gamers(user_id)
existing_gamer = next((g for g in user_gamers if g['username'] == username), None)
lang = self.get_user_language_from_update(update)
if existing_gamer:
# Update token for existing gamer
self.db.add_user_gamer(user_id, existing_gamer['id'], token)
await update.message.reply_text(
f"✅ Токен добавлен для игрока {username}!"
t('token_added', lang, username=username)
)
else:
# Add new gamer and link with token
@ -147,17 +138,19 @@ class LichessBot:
self.db.set_user_active_gamer(user_id, gamer_id)
await update.message.reply_text(
f"✅ Игрок {username} добавлен с токеном!"
t('gamer_added_with_token', lang, username=username)
)
return ConversationHandler.END
else:
lang = self.get_user_language_from_update(update)
await update.message.reply_text(
"Не удалось получить username из токена. Попробуйте еще раз."
t('token_username_error', lang)
)
return WAITING_FOR_TOKEN
else:
lang = self.get_user_language_from_update(update)
await update.message.reply_text(
"❌ Неверный токен. Попробуйте еще раз."
t('invalid_token', lang)
)
return WAITING_FOR_TOKEN
@ -166,9 +159,10 @@ class LichessBot:
username = update.message.text.strip()
user_id = update.effective_user.id
lang = self.get_user_language_from_update(update)
if not username:
await update.message.reply_text(
"❌ Username не может быть пустым. Попробуйте еще раз."
t('empty_username', lang)
)
return WAITING_FOR_USERNAME
@ -176,7 +170,7 @@ class LichessBot:
user_exists = await self.lichess_api.check_user_exists(username)
if not user_exists:
await update.message.reply_text(
f"❌ Игрок {username} не найден на Lichess. Проверьте правильность написания имени."
t('user_not_found', lang, username=username)
)
return WAITING_FOR_USERNAME
@ -193,8 +187,9 @@ class LichessBot:
if len(user_gamers) == 1:
self.db.set_user_active_gamer(user_id, gamer_id)
lang = self.get_user_language_from_update(update)
await update.message.reply_text(
f"✅ Игрок {username} успешно добавлен!"
t('gamer_added', lang, username=username)
)
return ConversationHandler.END
@ -206,12 +201,13 @@ class LichessBot:
logger.info(f"getgamers: user_id={user_id}, found {len(gamers)} gamers")
lang = self.get_user_language_from_update(update)
if not gamers:
await update.message.reply_text("📭 В базе нет игроков. Используйте /adduser для добавления.")
await update.message.reply_text(t('no_gamers', lang))
return
# Show loading message
loading_msg = await update.message.reply_text("🔄 Загружаем рейтинги игроков...")
loading_msg = await update.message.reply_text(t('loading_ratings', lang))
# Prepare data for each gamer
gamers_data = []
@ -240,7 +236,8 @@ class LichessBot:
# Add period information if period > 0
period_minutes = gamer.get('period_minutes', 0)
period_text = f" · {period_minutes}м" if period_minutes > 0 else ""
period_suffix = t('period_minutes_suffix', lang)
period_text = f" · {period_minutes}{period_suffix}" if period_minutes > 0 else ""
gamers_data.append({
'id': gamer['id'],
@ -257,6 +254,9 @@ class LichessBot:
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
# Still add the gamer with N/A ratings
period_minutes = gamer.get('period_minutes', 0)
period_suffix = t('period_minutes_suffix', lang)
period_text = f" · {period_minutes}{period_suffix}" if period_minutes > 0 else ""
gamers_data.append({
'id': gamer['id'],
'status': "🟢" if gamer['is_active'] else "",
@ -264,7 +264,7 @@ class LichessBot:
'bullet': 'N/A',
'blitz': 'N/A',
'rapid': 'N/A',
'period': f" · {gamer.get('period_minutes', 0)}м" if gamer.get('period_minutes', 0) > 0 else ""
'period': period_text
})
logger.info(f"Added gamer {gamer['username']} with N/A ratings due to error")
@ -277,7 +277,7 @@ class LichessBot:
)
logger.info(f"getgamers: prepared {len(gamers_data)} gamers for display")
gamers_text = "👥 <b>Выберите активного игрока:</b>\n\n" + "\n".join(text_lines)
gamers_text = t('select_active_gamer', lang) + "\n".join(text_lines)
logger.info(f"getgamers: message length: {len(gamers_text)} characters")
@ -325,26 +325,37 @@ class LichessBot:
gamers = self.db.get_user_gamers(user_id)
selected_gamer = next((g for g in gamers if g['id'] == gamer_id), None)
# Для callback query обновляем язык пользователя если есть в update
if update.effective_user:
self.db.add_or_get_telegram_user(
user_id=update.effective_user.id,
username=update.effective_user.username,
first_name=update.effective_user.first_name,
last_name=update.effective_user.last_name,
language_code=update.effective_user.language_code
)
lang = self.db.get_user_language(user_id)
if selected_gamer:
# Set active gamer for this user
self.db.set_user_active_gamer(user_id, gamer_id)
await query.edit_message_text(
f"✅ Активный игрок: {selected_gamer['username']}"
t('active_gamer_set', lang, username=selected_gamer['username'])
)
else:
await query.edit_message_text("❌ Игрок не найден")
await query.edit_message_text(t('gamer_not_found', lang))
async def delgamer(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show gamers list for deletion"""
user_id = update.effective_user.id
gamers = self.db.get_user_gamers(user_id)
lang = self.get_user_language_from_update(update)
if not gamers:
await update.message.reply_text("📭 У вас нет отслеживаемых игроков.")
await update.message.reply_text(t('no_gamers_to_delete', lang))
return
# Show loading message
loading_msg = await update.message.reply_text("🔄 Загружаем список игроков...")
loading_msg = await update.message.reply_text(t('loading_gamers', lang))
# Create text message with stats
text_lines = []
@ -370,7 +381,8 @@ class LichessBot:
bullet_rating = blitz_rating = rapid_rating = 'N/A'
period_minutes = gamer.get('period_minutes', 0)
period_text = f" · {period_minutes}м" if period_minutes > 0 else ""
period_suffix = t('period_minutes_suffix', lang)
period_text = f" · {period_minutes}{period_suffix}" if period_minutes > 0 else ""
text_lines.append(
f"{status} <b>{username}</b> "
@ -383,7 +395,7 @@ class LichessBot:
callback_data=f"delete_{gamer['id']}"
)])
gamers_text = "🗑️ <b>Выберите игрока для удаления:</b>\n\n" + "\n".join(text_lines)
gamers_text = t('select_gamer_to_delete', lang) + "\n".join(text_lines)
reply_markup = InlineKeyboardMarkup(keyboard)
@ -418,19 +430,29 @@ class LichessBot:
gamers = self.db.get_user_gamers(user_id)
gamer_to_delete = next((g for g in gamers if g['id'] == gamer_id), None)
# Для callback query получаем язык из БД
if update.effective_user:
self.db.add_or_get_telegram_user(
user_id=update.effective_user.id,
username=update.effective_user.username,
first_name=update.effective_user.first_name,
last_name=update.effective_user.last_name,
language_code=update.effective_user.language_code
)
lang = self.db.get_user_language(user_id)
if gamer_to_delete:
username = gamer_to_delete['username']
deleted = self.db.remove_user_gamer(user_id, gamer_id)
if deleted:
await query.edit_message_text(
f"✅ Игрок <b>{username}</b> удален из списка отслеживаемых.",
t('gamer_deleted', lang, username=username),
parse_mode='HTML'
)
else:
await query.edit_message_text("Не удалось удалить игрока")
await query.edit_message_text(t('delete_failed', lang))
else:
await query.edit_message_text("❌ Игрок не найден")
await query.edit_message_text(t('gamer_not_found', lang))
async def get_stats(self, update: Update, context: ContextTypes.DEFAULT_TYPE, period: str):
"""Get statistics for a period"""
@ -439,9 +461,10 @@ class LichessBot:
# Get active gamer for this user
active_gamer = self.db.get_user_active_gamer(user_id)
lang = self.get_user_language_from_update(update)
if not active_gamer:
await update.message.reply_text(
"❌ Нет активного игрока. Используйте /getgamers для выбора."
t('no_active_gamer', lang)
)
return
@ -455,11 +478,11 @@ class LichessBot:
elif period == "week":
data = await self.lichess_api.get_week_stats(username)
else:
await update.message.reply_text("❌ Неизвестный период")
await update.message.reply_text(t('unknown_period', lang))
return
# Format and send response
formatted_response = StatsFormatter.format_stats_response(data, username, period)
formatted_response = StatsFormatter.format_stats_response(data, username, period, lang)
await update.message.reply_text(formatted_response)
async def today(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
@ -481,24 +504,24 @@ class LichessBot:
# Get active gamer for this user
active_gamer = self.db.get_user_active_gamer(user_id)
lang = self.get_user_language_from_update(update)
if not active_gamer:
await update.message.reply_text(
"❌ Нет активного игрока. Используйте /getgamers для выбора."
t('no_active_gamer', lang)
)
return
keyboard = []
for period in PERIOD_OPTIONS:
if period == 0:
button_text = "❌ Отключить уведомления"
button_text = t('disable_notifications', lang)
else:
button_text = f"{period} минут"
button_text = t('period_minutes', lang, period=period)
keyboard.append([InlineKeyboardButton(button_text, callback_data=f"period_{period}")])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
f"⏱️ Выберите период для игрока {active_gamer['username']}:\n"
f"📱 Уведомления будут приходить в личные сообщения",
t('select_period', lang, username=active_gamer['username']),
reply_markup=reply_markup
)
@ -513,23 +536,83 @@ class LichessBot:
# Get active gamer for this user
active_gamer = self.db.get_user_active_gamer(user_id)
# Для callback query получаем язык из БД
if update.effective_user:
self.db.add_or_get_telegram_user(
user_id=update.effective_user.id,
username=update.effective_user.username,
first_name=update.effective_user.first_name,
last_name=update.effective_user.last_name,
language_code=update.effective_user.language_code
)
lang = self.db.get_user_language(user_id)
if active_gamer:
# Set period for this user-gamer pair
self.db.set_user_gamer_period(user_id, active_gamer['id'], period)
if period == 0:
await query.edit_message_text(
f"✅ Уведомления для {active_gamer['username']} отключены"
t('notifications_disabled', lang, username=active_gamer['username'])
)
else:
await query.edit_message_text(
f"✅ Период {period} минут установлен для {active_gamer['username']}\n"
f"📱 Уведомления будут приходить в личные сообщения"
t('period_set', lang, period=period, username=active_gamer['username'])
)
# Start periodic task for this gamer (send to user's personal messages)
await self.start_periodic_task(active_gamer, user_id, period)
async def check_language(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Check and display current language settings"""
user = update.effective_user
if user:
# Обновляем язык из update
lang = self.get_user_language_from_update(update)
# Получаем язык из БД для отображения
db_lang = self.db.get_user_language(user.id)
# Получаем language_code из БД напрямую
with sqlite3.connect(self.db.db_path) as conn:
cursor = conn.cursor()
cursor.execute("SELECT language_code FROM telegram_users WHERE user_id = ?", (user.id,))
row = cursor.fetchone()
db_language_code = row[0] if row and row[0] else None
message = (
f"🌐 Language Info:\n"
f"Current language: {lang}\n"
f"DB language: {db_lang}\n"
f"DB language_code: {db_language_code}\n"
f"Update language_code: {user.language_code}\n\n"
f"Language used: {lang}\n\n"
f"Bot uses English language only."
)
await update.message.reply_text(message)
else:
await update.message.reply_text("❌ Failed to get user information")
async def reset_language(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Reset user language in database - will be detected from /start"""
user = update.effective_user
if user:
# Сбрасываем язык в БД (устанавливаем NULL)
with sqlite3.connect(self.db.db_path) as conn:
cursor = conn.cursor()
cursor.execute(
"UPDATE telegram_users SET language_code = NULL WHERE user_id = ?",
(user.id,)
)
conn.commit()
# Language is always English
lang = 'en'
await update.message.reply_text(
"✅ Language reset! Bot uses English language only."
)
else:
await update.message.reply_text("❌ Failed to get user information")
async def start_periodic_task(self, gamer: Dict[str, Any], user_id: int, period_minutes: int):
"""Start periodic task for a gamer"""
task_key = f"{gamer['id']}_{user_id}"
@ -622,8 +705,10 @@ class LichessBot:
# Отправляем уведомление только если есть реальная активность
if has_games or has_puzzles:
try:
# Get user language from database
user_lang = self.db.get_user_language(user_id)
notification = StatsFormatter.format_period_notification(
gamer['username'], games_data, puzzles_data, period_minutes
gamer['username'], games_data, puzzles_data, period_minutes, lang=user_lang
)
if self.application:
@ -690,6 +775,8 @@ class LichessBot:
application.add_handler(CommandHandler("yesterday", self.yesterday))
application.add_handler(CommandHandler("week", self.week))
application.add_handler(CommandHandler("setperiod", self.setperiod))
application.add_handler(CommandHandler("lang", self.check_language))
application.add_handler(CommandHandler("resetlang", self.reset_language))
# Callback handlers
application.add_handler(CallbackQueryHandler(self.select_gamer, pattern="^select_"))