EN language only
This commit is contained in:
parent
3ec1fe614d
commit
3362bf89e2
4 changed files with 353 additions and 94 deletions
|
|
@ -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_"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue