From 977787c8586581dbab453be0ecd2f4fca57c74b3 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Wed, 3 Dec 2025 13:02:14 +0300 Subject: [PATCH 01/10] =?UTF-8?q?=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BA=D0=B0=D1=80=D1=82=D0=B8=D0=BD=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LichessClientTG_bot/helpEN.jpeg | Bin 19241 -> 23911 bytes LichessClientTG_bot/helpRU.jpeg | Bin 17942 -> 36957 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/LichessClientTG_bot/helpEN.jpeg b/LichessClientTG_bot/helpEN.jpeg index 5c440420fbeabef5a0ba3e8435eb13e21782de1b..eafbd6bdcd77386bb34a20adb71beb90a73da58f 100644 GIT binary patch literal 23911 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 C+ejt= diff --git a/LichessClientTG_bot/helpRU.jpeg b/LichessClientTG_bot/helpRU.jpeg index b6fe6fa05decc7aad15c7163e615c4cef2263675..b9c5369df713563918d048e16845aa8e49ac096f 100644 GIT binary patch delta 26758 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 delta 8854 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 L|9t2OW%U06Lvs1s From 6d51cf135d45a2cf0f7bcdd9ce5fd99d671e4d3f Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Wed, 3 Dec 2025 13:09:40 +0300 Subject: [PATCH 02/10] =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B5=D1=83=D0=B4=D0=B0=D1=87=D0=BD=D1=8B=D1=85=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BF=D1=8B=D1=82=D0=BE=D0=BA=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B8=D0=B3=D1=80=D0=BE=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B4=D0=BB=D0=B0=D0=B3=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C?= =?UTF-8?q?=20=D0=B8=D0=B3=D1=80=D0=BE=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LichessClientTG_bot/bot.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/LichessClientTG_bot/bot.py b/LichessClientTG_bot/bot.py index 88d1b93..1ce7e62 100644 --- a/LichessClientTG_bot/bot.py +++ b/LichessClientTG_bot/bot.py @@ -558,7 +558,8 @@ class LichessBot: user_exists = await self.lichess_api.check_user_exists(username) if not user_exists: await update.message.reply_text( - t('user_not_found', lang, username=username) + t('user_not_found', lang, username=username) + '\n\n' + t('addgamer_prompt', lang), + parse_mode='HTML' ) return WAITING_FOR_USERNAME @@ -569,13 +570,10 @@ class LichessBot: if existing_gamer: # Player is already being tracked by this user await update.message.reply_text( - t('gamer_already_added', lang, username=username) + t('gamer_already_added', lang, username=username) + '\n\n' + t('addgamer_prompt', lang), + parse_mode='HTML' ) - # Clear awaiting flag - try: - context.user_data['awaiting_addgamer_username'] = False - except Exception: - pass + # Keep awaiting flag - don't clear it, so user can try again return # Add gamer to database (without token) @@ -594,13 +592,10 @@ class LichessBot: # If add_user_gamer returned False, it means the pair already exists (shouldn't happen after our check, but just in case) if not added: await update.message.reply_text( - t('gamer_already_added', lang, username=username) + t('gamer_already_added', lang, username=username) + '\n\n' + t('addgamer_prompt', lang), + parse_mode='HTML' ) - # Clear awaiting flag - try: - context.user_data['awaiting_addgamer_username'] = False - except Exception: - pass + # Keep awaiting flag - don't clear it, so user can try again return # Set default period to 1 hour (60 minutes) for new gamer From b3b6a54e2d0e8292741a361ff2af1e5fe0a21278 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Sat, 6 Dec 2025 00:28:53 +0300 Subject: [PATCH 03/10] =?UTF-8?q?=D0=BF=D0=BE=D1=84=D0=B8=D0=BA=D1=81?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B1=D0=B0=D0=B3=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BA=D0=B8=20=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LichessClientTG_bot/bot.py | 132 ++++++++++++++++++++++++++-------- check_recent_games.py | 144 +++++++++++++++++++++++++++++++++++++ docker-compose.yml | 3 +- export_db.sh | 0 logs.sh | 6 ++ 5 files changed, 253 insertions(+), 32 deletions(-) create mode 100644 check_recent_games.py mode change 100644 => 100755 export_db.sh create mode 100755 logs.sh diff --git a/LichessClientTG_bot/bot.py b/LichessClientTG_bot/bot.py index 1ce7e62..bc344f2 100644 --- a/LichessClientTG_bot/bot.py +++ b/LichessClientTG_bot/bot.py @@ -1458,14 +1458,13 @@ class LichessBot: task_key = f"{gamer['id']}_{user_id}" username = gamer['username'] - # Инициализируем время начала отслеживания как текущее время - # Первая проверка произойдет через period_minutes минут - start_time = datetime.now() - self.period_start_times[task_key] = start_time + # НЕ устанавливаем period_start_times при инициализации + # Это позволит использовать логику первой проверки (else блок) logger.info(f"🔄 Started periodic monitoring for {username} (user {user_id}) with {period_minutes} minute intervals") consecutive_errors = 0 max_consecutive_errors = 5 + is_first_check = True # Флаг для первой проверки while True: try: @@ -1493,21 +1492,48 @@ class LichessBot: logger.info(f"Period changed for {gamer['username']} from {period_minutes} to {current_period} minutes") period_minutes = current_period - # Ждем заданное количество минут перед следующей проверкой - logger.info(f"⏳ Waiting {period_minutes} minutes before next check for {username}") - await asyncio.sleep(period_minutes * 60) + # Получаем сохраненное время последней проверки для расчета следующего периода + # Используем флаг is_first_check для первой проверки вместо проверки наличия ключа + last_check_time = self.period_start_times.get(task_key) + if is_first_check: + last_check_time = None # Принудительно делаем первую проверку + is_first_check = False - # Получаем текущее время - now = datetime.now() + if last_check_time: + # Уже была хотя бы одна проверка + # Рассчитываем, когда должен начаться следующий период + next_period_start = last_check_time + timedelta(minutes=period_minutes) + now = datetime.now() + + # Если следующий период еще не наступил, ждем + if next_period_start > now: + wait_seconds = (next_period_start - now).total_seconds() + logger.info(f"⏳ Waiting {wait_seconds:.1f} seconds until next period start ({next_period_start}) for {username}") + await asyncio.sleep(wait_seconds) + + # Используем сохраненное время как начало периода + since_time = last_check_time + # Конец периода - это момент, когда должен был начаться следующий период + period_end_approx = next_period_start + logger.info(f"📌 Using saved period: from {since_time} to {period_end_approx}") + else: + # Первая проверка - ждем period_minutes минут от момента запуска + logger.info(f"⏳ First check: waiting {period_minutes} minutes before first check for {username}") + await asyncio.sleep(period_minutes * 60) + + # Получаем текущее время + period_end_approx = datetime.now() + # Начало периода - текущее время минус period_minutes + since_time = period_end_approx - timedelta(minutes=period_minutes) + logger.info(f"📌 First check: period from {since_time} to {period_end_approx}") - # Рассчитываем период: от (текущее время - период) до текущего времени - # Это гарантирует, что мы проверяем последний период активности - since_time = now - timedelta(minutes=period_minutes) since_timestamp = int(since_time.timestamp() * 1000) - until_timestamp = int(now.timestamp() * 1000) + # Используем приблизительное время как until_timestamp + # После получения ответа пересчитаем фактическое время + until_timestamp_approx = int(period_end_approx.timestamp() * 1000) - logger.info(f"🔍 Checking activity for {username} (user {user_id}): period from {since_time} to {now} (last {period_minutes} minutes)") - logger.info(f"📅 Unix timestamps: since={since_timestamp}, until={until_timestamp}") + logger.info(f"🔍 Checking activity for {username} (user {user_id}): period from {since_time} to {period_end_approx} (approx, last {period_minutes} minutes)") + logger.info(f"📅 Unix timestamps: since={since_timestamp}, until_approx={until_timestamp_approx}") # Делаем запросы к API через очередь с обработкой ошибок games_data = None @@ -1518,18 +1544,21 @@ class LichessBot: logger.info(f"📥 Adding games request to queue for {gamer['username']}") games_data = await self.request_queue.add_request( self.lichess_api.get_games_period, - gamer['username'], since_timestamp, until_timestamp + gamer['username'], since_timestamp, until_timestamp_approx ) - logger.info(f"✅ Games API response received for {gamer['username']}") + # Фиксируем фактическое время получения ответа + request_end_time = datetime.now() + logger.info(f"✅ Games API response received for {gamer['username']} at {request_end_time}") except Exception as e: logger.error(f"❌ Error getting games data for {gamer['username']}: {e}") consecutive_errors += 1 if consecutive_errors >= max_consecutive_errors: logger.error(f"Too many consecutive errors for {gamer['username']}, stopping periodic check") break - # Продолжаем с обновлением времени начала, чтобы не зацикливаться - now = datetime.now() - self.period_start_times[task_key] = now + # Продолжаем с обновлением времени начала на планируемое время окончания периода + # чтобы не создавать пропусков в следующих проверках + self.period_start_times[task_key] = period_end_approx + logger.warning(f"⚠️ Error occurred, updated period_start_time to {period_end_approx} (planned period end)") continue if gamer.get('token'): @@ -1538,9 +1567,11 @@ class LichessBot: logger.info(f"📥 Adding puzzles request to queue for {gamer['username']}") puzzles_data = await self.request_queue.add_request( self.lichess_api.get_puzzles_period, - gamer['token'], since_timestamp, until_timestamp, 150 + gamer['token'], since_timestamp, until_timestamp_approx, 150 ) - logger.info(f"✅ Puzzles API response received for {gamer['username']}") + # Обновляем фактическое время после получения ответа по пазлам + request_end_time = datetime.now() + logger.info(f"✅ Puzzles API response received for {gamer['username']} at {request_end_time}") except Exception as e: logger.warning(f"⚠️ Error getting puzzles data for {gamer['username']}: {e}") # Продолжаем без данных по пазлам @@ -1556,15 +1587,29 @@ class LichessBot: logger.info(f"📊 Games data structure for {username}: {games_data}") # Проверяем games_count на верхнем уровне (приоритет) - if games_data.get('games_count', 0) > 0: - total_games = games_data.get('games_count', 0) + top_level_count = games_data.get('games_count', 0) + logger.debug(f"🔍 Top-level games_count: {top_level_count}") + + if top_level_count > 0: + total_games = top_level_count has_games = True logger.info(f"✅ Found {total_games} games via games_count field") - # Также проверяем data.total.games_played - elif games_data.get('data') and games_data.get('data', {}).get('total', {}).get('games_played', 0) > 0: - total_games = games_data.get('data', {}).get('total', {}).get('games_played', 0) - has_games = True - logger.info(f"✅ Found {total_games} games via data.total.games_played field") + else: + # Также проверяем data.total.games_played + games_data_dict = games_data.get('data') + if games_data_dict: + data_total = games_data_dict.get('total', {}) + total_games_played = data_total.get('games_played', 0) if data_total else 0 + else: + total_games_played = 0 + logger.debug(f"🔍 data.total.games_played: {total_games_played}") + + if total_games_played > 0: + total_games = total_games_played + has_games = True + logger.info(f"✅ Found {total_games} games via data.total.games_played field") + else: + logger.warning(f"⚠️ No games found: games_count={top_level_count}, data.total.games_played={total_games_played}") else: logger.warning(f"⚠️ No games_data returned for {username}") @@ -1618,8 +1663,22 @@ class LichessBot: else: logger.debug(f"⏭️ No activity found for {gamer['username']} in the last {period_minutes} minutes") - # Всегда обновляем время начала на текущее время после проверки (независимо от наличия активности) - self.period_start_times[task_key] = now + # Обновляем время начала следующего периода на ПЛАНИРУЕМОЕ время окончания текущего периода + # (period_end_approx), а не на фактическое время завершения запроса (request_end_time). + # Это гарантирует непрерывность периодов без пропусков: + # - Проверяем период A-B + # - Следующая проверка будет периода B-C + # - Без пропусков между A-B и B-C + # + # Использование request_end_time приведет к пропуску диапазона между period_end_approx и request_end_time + # + # period_end_approx уже установлено в начале итерации + self.period_start_times[task_key] = period_end_approx + logger.info(f"📌 Updated period_start_time for {username} to {period_end_approx} (planned period end, next period will start from here)") + if 'request_end_time' in locals(): + delay = (request_end_time - period_end_approx).total_seconds() + if delay > 0: + logger.info(f"⏱️ Request completed with {delay:.1f}s delay after planned period end") except asyncio.CancelledError: logger.info(f"Periodic check cancelled for {gamer['username']}") @@ -1638,6 +1697,17 @@ class LichessBot: del self.period_start_times[task_key] break + # Важно: обновляем period_start_times даже при ошибке, чтобы не зациклиться на одном периоде + # Используем period_end_approx, если он был установлен, иначе используем текущее время + if 'period_end_approx' in locals(): + self.period_start_times[task_key] = period_end_approx + logger.warning(f"⚠️ Error occurred, updated period_start_time to {period_end_approx} to prevent loop") + else: + # Если period_end_approx не был установлен (например, ошибка в начале), используем текущее время + now = datetime.now() + self.period_start_times[task_key] = now + logger.warning(f"⚠️ Error occurred early, updated period_start_time to {now} to prevent loop") + # Ждем перед повторной попыткой при ошибке await asyncio.sleep(60) # 1 minute delay before retry diff --git a/check_recent_games.py b/check_recent_games.py new file mode 100644 index 0000000..e577ea6 --- /dev/null +++ b/check_recent_games.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +"""Скрипт для проверки недавних игр пользователя через Lichess API""" + +import requests +import json +from datetime import datetime, timedelta +import sys + +def get_recent_games(username: str, minutes: int = 30): + """Получить игры за последние N минут""" + + # Вычисляем временные метки + now = datetime.now() + since_time = now - timedelta(minutes=minutes) + + since_ms = int(since_time.timestamp() * 1000) + until_ms = int(now.timestamp() * 1000) + + print(f"🔍 Проверяем игры для {username}") + print(f"⏰ Период: с {since_time.strftime('%Y-%m-%d %H:%M:%S')} до {now.strftime('%Y-%m-%d %H:%M:%S')}") + print(f"📅 Timestamps: since={since_ms}, until={until_ms}") + print() + + # Делаем запрос к Lichess API + url = f"https://lichess.org/api/games/user/{username}" + params = { + 'since': since_ms, + 'until': until_ms, + 'max': 1000, + 'rated': 'true' # Только рейтинговые + } + + headers = { + 'Accept': 'application/x-ndjson' + } + + try: + response = requests.get(url, params=params, headers=headers, timeout=30) + + if response.status_code == 404: + print(f"❌ Пользователь {username} не найден (404)") + return [] + elif response.status_code != 200: + print(f"❌ Ошибка API: {response.status_code} - {response.text[:200]}") + return [] + + # Парсим NDJSON + games = [] + content = response.text.strip() + + if content: + for line in content.split('\n'): + if line.strip(): + try: + game = json.loads(line) + games.append(game) + except json.JSONDecodeError as e: + print(f"⚠️ Ошибка парсинга JSON: {e}") + continue + + return games + + except Exception as e: + print(f"❌ Ошибка при запросе: {e}") + return [] + +def format_game_time(created_at_ms: int) -> str: + """Форматирует время создания игры""" + dt = datetime.fromtimestamp(created_at_ms / 1000) + return dt.strftime('%Y-%m-%d %H:%M:%S') + +def main(): + username = sys.argv[1] if len(sys.argv) > 1 else "vrubelroman" + minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 40 + + games = get_recent_games(username, minutes) + + if not games: + print("❌ Игры не найдены") + return + + print(f"✅ Найдено игр: {len(games)}") + print() + print("=" * 80) + + now = datetime.now() + + for i, game in enumerate(games, 1): + game_id = game.get('id', 'N/A') + created_at_ms = game.get('createdAt', 0) + speed = game.get('speed', 'unknown') + rated = game.get('rated', False) + + # Результат + white = game.get('players', {}).get('white', {}) + black = game.get('players', {}).get('black', {}) + white_user = white.get('user', {}).get('name', 'N/A') + black_user = black.get('user', {}).get('name', 'N/A') + winner = game.get('winner') + + # Определяем результат для пользователя + if white_user == username: + result = "Победа" if winner == "white" else ("Поражение" if winner == "black" else "Ничья") + opponent = black_user + elif black_user == username: + result = "Победа" if winner == "black" else ("Поражение" if winner == "white" else "Ничья") + opponent = white_user + else: + result = "N/A" + opponent = "N/A" + + game_time = format_game_time(created_at_ms) + time_ago = now - datetime.fromtimestamp(created_at_ms / 1000) + minutes_ago = int(time_ago.total_seconds() / 60) + + print(f"\n🎮 Игра #{i}") + print(f" ID: {game_id}") + print(f" Время: {game_time} ({minutes_ago} минут назад)") + print(f" Скорость: {speed}") + print(f" Рейтинговая: {'Да' if rated else 'Нет'}") + print(f" Соперник: {opponent}") + print(f" Результат: {result}") + + # Рейтинг + if white_user == username: + rating_change = white.get('ratingDiff', 0) + final_rating = white.get('rating', 0) + elif black_user == username: + rating_change = black.get('ratingDiff', 0) + final_rating = black.get('rating', 0) + else: + rating_change = 0 + final_rating = 0 + + if rating_change != 0 or final_rating != 0: + print(f" Рейтинг: {final_rating} ({rating_change:+d})") + + print() + print("=" * 80) + print(f"\n📊 Всего игр за последние {minutes} минут: {len(games)}") + +if __name__ == "__main__": + main() + diff --git a/docker-compose.yml b/docker-compose.yml index fedca0c..7bfe595 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,10 +11,11 @@ services: - ./LichessWebServices:/app restart: always healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"] interval: 30s timeout: 10s retries: 3 + start_period: 40s # Telegram Bot lichess-bot: diff --git a/export_db.sh b/export_db.sh old mode 100644 new mode 100755 diff --git a/logs.sh b/logs.sh new file mode 100755 index 0000000..a549a0d --- /dev/null +++ b/logs.sh @@ -0,0 +1,6 @@ + GNU nano 6.2 logchecker.sh +# Последние 500 строк, отфильтрованные по периодическим проверкам +docker logs --tail=1000 lichess-telegram-bot 2>&1 | grep -E "periodic|Checking activity|queue|Activity detected" +# Смотреть логи периодических проверок в реальном времени +#docker logs -f lichess-telegram-bot 2>&1 | grep --line-buffered -E "🔄|⏳|🔍|📥|✅|📊|periodic|queue|Activity" +docker logs -f lichess-telegram-bot 2>&1 | grep --line-buffered -E "Checking activity for|Games data structure for" From 2ac38440da02239b9903324ed17b8e62b7cfb85b Mon Sep 17 00:00:00 2001 From: vrubel Date: Thu, 22 Jan 2026 16:25:33 +0300 Subject: [PATCH 04/10] =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=E2=9C=85=20That's=20all=20=D0=B8=20=E2=8F=B3=20Processing?= =?UTF-8?q?=20request...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LichessClientTG_bot/bot.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/LichessClientTG_bot/bot.py b/LichessClientTG_bot/bot.py index bc344f2..64fd07a 100644 --- a/LichessClientTG_bot/bot.py +++ b/LichessClientTG_bot/bot.py @@ -943,12 +943,6 @@ class LichessBot: ) return - # Send initial message about processing - try: - await update.message.reply_text(t('stats_processing', lang), parse_mode='HTML') - except Exception: - pass - # Process each gamer has_any_activity = False for i, gamer in enumerate(gamers): @@ -1042,9 +1036,6 @@ class LichessBot: # If no activity for any player if not has_any_activity: await update.message.reply_text(t('no_activity', lang)) - else: - # Send final message that all is done - await update.message.reply_text(t('stats_all_done', lang)) # Increment counter for the period command if period == "today": @@ -1087,12 +1078,6 @@ class LichessBot: ) return - # Send initial message about processing - try: - await update.message.reply_text(t('last_year_1000_processing', lang), parse_mode='HTML') - except Exception: - pass - now_ms = int(time.time() * 1000) year_ms = 365 * 24 * 3600 * 1000 since_ms = now_ms - year_ms @@ -1141,9 +1126,6 @@ class LichessBot: # If no activity for any player if not has_any_activity: await update.message.reply_text(t('no_activity', lang)) - else: - # Send final message that all is done - await update.message.reply_text(t('stats_all_done', lang)) self.counters.increment('last_year_1000') From 0de0deb14f795b16d770c17cf8be563a238453f5 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Wed, 4 Feb 2026 23:51:32 +0300 Subject: [PATCH 05/10] delay in config and setup = 5sec --- LichessClientTG_bot/config.py | 2 ++ LichessClientTG_bot/request_queue.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/LichessClientTG_bot/config.py b/LichessClientTG_bot/config.py index f6ab393..f3f17f3 100644 --- a/LichessClientTG_bot/config.py +++ b/LichessClientTG_bot/config.py @@ -27,6 +27,8 @@ else: # Lichess API Configuration LICHESS_API_BASE_URL = "https://lichess.org/api" LICHESS_STATS_API_BASE_URL = "http://localhost:8001" # For Docker container access +# Минимальная задержка (сек) между запросами к Lichess в очереди мониторинга (избежание бана) +LICHESS_REQUEST_QUEUE_MIN_DELAY = 5.0 # Database Configuration def _resolve_database_path() -> str: diff --git a/LichessClientTG_bot/request_queue.py b/LichessClientTG_bot/request_queue.py index d0c0efc..3a81a3d 100644 --- a/LichessClientTG_bot/request_queue.py +++ b/LichessClientTG_bot/request_queue.py @@ -7,6 +7,8 @@ import logging from typing import Callable, Any, Optional, Dict from datetime import datetime +import config + logger = logging.getLogger(__name__) class RequestQueue: @@ -139,6 +141,6 @@ def get_request_queue() -> RequestQueue: """Get the global request queue instance""" global _request_queue if _request_queue is None: - _request_queue = RequestQueue(min_delay=7.0) + _request_queue = RequestQueue(min_delay=config.LICHESS_REQUEST_QUEUE_MIN_DELAY) return _request_queue From 20f96d0e0abd83d34a9e6d92cc41075e39cf4747 Mon Sep 17 00:00:00 2001 From: vrubel Date: Thu, 5 Feb 2026 01:38:33 +0300 Subject: [PATCH 06/10] fix critical bugs --- LichessClientTG_bot/config.py | 4 ++-- LichessWebServices/lichess_client.py | 8 +++----- LichessWebServices/stats_service.py | 8 ++++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/LichessClientTG_bot/config.py b/LichessClientTG_bot/config.py index f3f17f3..bec7e2c 100644 --- a/LichessClientTG_bot/config.py +++ b/LichessClientTG_bot/config.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv load_dotenv() -IS_PROD = False +IS_PROD = True # Telegram Bot Configuration (Production) TELEGRAM_BOT_TOKEN_PROD = "8241474807:AAH684LTY93aXRou4-LtqU5-p8LuEjzYn8U" @@ -28,7 +28,7 @@ else: LICHESS_API_BASE_URL = "https://lichess.org/api" LICHESS_STATS_API_BASE_URL = "http://localhost:8001" # For Docker container access # Минимальная задержка (сек) между запросами к Lichess в очереди мониторинга (избежание бана) -LICHESS_REQUEST_QUEUE_MIN_DELAY = 5.0 +LICHESS_REQUEST_QUEUE_MIN_DELAY = 4.0 # Database Configuration def _resolve_database_path() -> str: diff --git a/LichessWebServices/lichess_client.py b/LichessWebServices/lichess_client.py index 7d2e698..49e3a59 100644 --- a/LichessWebServices/lichess_client.py +++ b/LichessWebServices/lichess_client.py @@ -121,17 +121,15 @@ class LichessClient: # Формируем URL для получения игр пользователя url = f"{self.base_url}/games/user/{username}" - # Параметры запроса + # Параметры запроса. Параметр 'rated' в API Lichess не передаём: + # при rated=true API часто возвращает 0 игр даже для рейтинговых партий. + # Фильтрация по рейтинговости делается в stats_service после получения списка. params = { 'since': since_ms, # Начало периода 'until': until_ms, # Конец периода 'max': 1000 # Максимум игр за запрос (лимит Lichess API) } - # Добавляем фильтр по рейтинговым играм, если нужно - if rated_only: - params['rated'] = 'true' - # Заголовки для получения NDJSON формата headers = { 'Accept': 'application/x-ndjson' # Запрашиваем NDJSON формат diff --git a/LichessWebServices/stats_service.py b/LichessWebServices/stats_service.py index b699d5f..05a66a3 100644 --- a/LichessWebServices/stats_service.py +++ b/LichessWebServices/stats_service.py @@ -648,8 +648,8 @@ class StatsService: since_ms = since_timestamp * 1000 until_ms = until_timestamp * 1000 - # Получаем игры - games = await self.lichess_client.get_games_of_period(username, since_ms, until_ms, rated_only) + # Получаем игры (без фильтра rated в запросе к Lichess — см. lichess_client) + games = await self.lichess_client.get_games_of_period(username, since_ms, until_ms, rated_only=False) if games is None: return GamesOfPeriodResponse( @@ -660,6 +660,10 @@ class StatsService: games_count=0 ) + # Фильтр по рейтинговости на нашей стороне (API Lichess с param rated даёт неверный результат) + if rated_only: + games = [g for g in games if g.get('rated') is True] + if not games: return GamesOfPeriodResponse( message=f"Игры за указанный период не найдены", From 3ffcf97c3ffe38541f665d733984259b9828b0d3 Mon Sep 17 00:00:00 2001 From: vrubel Date: Thu, 5 Feb 2026 23:53:36 +0300 Subject: [PATCH 07/10] isProd = false --- LichessClientTG_bot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LichessClientTG_bot/config.py b/LichessClientTG_bot/config.py index bec7e2c..e1b966b 100644 --- a/LichessClientTG_bot/config.py +++ b/LichessClientTG_bot/config.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv load_dotenv() -IS_PROD = True +IS_PROD = False # Telegram Bot Configuration (Production) TELEGRAM_BOT_TOKEN_PROD = "8241474807:AAH684LTY93aXRou4-LtqU5-p8LuEjzYn8U" From 63e28c279c552350bdbd5ec6680d125424cbbd4f Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Sat, 21 Mar 2026 22:58:47 +0300 Subject: [PATCH 08/10] fix by codex --- LichessClientTG_bot/bot.py | 45 +++++++++++++++++-------------- LichessClientTG_bot/database.py | 41 ++++++++++++++++++++++++++++ LichessClientTG_bot/formatters.py | 2 +- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/LichessClientTG_bot/bot.py b/LichessClientTG_bot/bot.py index 64fd07a..deff442 100644 --- a/LichessClientTG_bot/bot.py +++ b/LichessClientTG_bot/bot.py @@ -47,6 +47,15 @@ class LichessBot: self.application = None # Will be set when application is created self.counters = MessageCounters() # Message counters self.request_queue = get_request_queue() # Request queue for rate limiting + + async def stop_periodic_task(self, gamer_id: int, user_id: int): + """Stop periodic task for a user-gamer pair.""" + task_key = f"{gamer_id}_{user_id}" + task = self.periodic_tasks.pop(task_key, None) + if task: + task.cancel() + logger.info(f"Cancelled periodic task for gamer_id={gamer_id}, user_id={user_id}") + self.period_start_times.pop(task_key, None) async def _notify_admin_new_player(self, player_username: str, added_by_user_id: int, added_by_username: Optional[str], is_new_gamer: bool = False): """Notify admin about newly linked player (always try to send).""" @@ -1275,8 +1284,10 @@ class LichessBot: # Set period for this user-gamer pair self.db.set_user_gamer_period(user_id, gamer_id, period) + self.db.clear_period_checkpoint(user_id, gamer_id) if period == 0: + await self.stop_periodic_task(gamer_id, user_id) await query.edit_message_text( t('notifications_disabled', lang, username=selected_gamer['username']) ) @@ -1440,8 +1451,12 @@ class LichessBot: task_key = f"{gamer['id']}_{user_id}" username = gamer['username'] - # НЕ устанавливаем period_start_times при инициализации - # Это позволит использовать логику первой проверки (else блок) + checkpoint_ts = self.db.get_period_checkpoint(user_id, gamer['id']) + if checkpoint_ts is not None: + restored_time = datetime.fromtimestamp(checkpoint_ts) + self.period_start_times[task_key] = restored_time + logger.info(f"♻️ Restored periodic checkpoint for {username} (user {user_id}) at {restored_time}") + logger.info(f"🔄 Started periodic monitoring for {username} (user {user_id}) with {period_minutes} minute intervals") consecutive_errors = 0 @@ -1477,9 +1492,9 @@ class LichessBot: # Получаем сохраненное время последней проверки для расчета следующего периода # Используем флаг is_first_check для первой проверки вместо проверки наличия ключа last_check_time = self.period_start_times.get(task_key) - if is_first_check: + if is_first_check and checkpoint_ts is None: last_check_time = None # Принудительно делаем первую проверку - is_first_check = False + is_first_check = False if last_check_time: # Уже была хотя бы одна проверка @@ -1528,6 +1543,8 @@ class LichessBot: self.lichess_api.get_games_period, gamer['username'], since_timestamp, until_timestamp_approx ) + if games_data is None: + raise RuntimeError("Games period API returned no data") # Фиксируем фактическое время получения ответа request_end_time = datetime.now() logger.info(f"✅ Games API response received for {gamer['username']} at {request_end_time}") @@ -1537,10 +1554,8 @@ class LichessBot: if consecutive_errors >= max_consecutive_errors: logger.error(f"Too many consecutive errors for {gamer['username']}, stopping periodic check") break - # Продолжаем с обновлением времени начала на планируемое время окончания периода - # чтобы не создавать пропусков в следующих проверках - self.period_start_times[task_key] = period_end_approx - logger.warning(f"⚠️ Error occurred, updated period_start_time to {period_end_approx} (planned period end)") + logger.warning(f"⚠️ Games data unavailable for {gamer['username']}; retrying the same period in 60 seconds") + await asyncio.sleep(60) continue if gamer.get('token'): @@ -1656,6 +1671,7 @@ class LichessBot: # # period_end_approx уже установлено в начале итерации self.period_start_times[task_key] = period_end_approx + self.db.set_period_checkpoint(user_id, gamer['id'], int(period_end_approx.timestamp())) logger.info(f"📌 Updated period_start_time for {username} to {period_end_approx} (planned period end, next period will start from here)") if 'request_end_time' in locals(): delay = (request_end_time - period_end_approx).total_seconds() @@ -1678,18 +1694,7 @@ class LichessBot: if task_key in self.period_start_times: del self.period_start_times[task_key] break - - # Важно: обновляем period_start_times даже при ошибке, чтобы не зациклиться на одном периоде - # Используем period_end_approx, если он был установлен, иначе используем текущее время - if 'period_end_approx' in locals(): - self.period_start_times[task_key] = period_end_approx - logger.warning(f"⚠️ Error occurred, updated period_start_time to {period_end_approx} to prevent loop") - else: - # Если period_end_approx не был установлен (например, ошибка в начале), используем текущее время - now = datetime.now() - self.period_start_times[task_key] = now - logger.warning(f"⚠️ Error occurred early, updated period_start_time to {now} to prevent loop") - + # Ждем перед повторной попыткой при ошибке await asyncio.sleep(60) # 1 minute delay before retry diff --git a/LichessClientTG_bot/database.py b/LichessClientTG_bot/database.py index 34f2ef5..b60db2a 100644 --- a/LichessClientTG_bot/database.py +++ b/LichessClientTG_bot/database.py @@ -64,6 +64,7 @@ class Database: token TEXT, is_active BOOLEAN DEFAULT FALSE, period_minutes INTEGER DEFAULT 0, + last_period_end_ts INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES telegram_users(user_id), FOREIGN KEY (gamer_id) REFERENCES gamers(id), @@ -77,6 +78,13 @@ class Database: except sqlite3.OperationalError: # Column already exists pass + + # Add last_period_end_ts column to persist periodic checkpoint across restarts + try: + cursor.execute("ALTER TABLE user_gamers ADD COLUMN last_period_end_ts INTEGER") + except sqlite3.OperationalError: + # Column already exists + pass # Create admin_settings table for admin bot configuration cursor.execute(''' @@ -326,6 +334,39 @@ class Database: (period_minutes, user_id, gamer_id) ) conn.commit() + + def get_period_checkpoint(self, user_id: int, gamer_id: int) -> Optional[int]: + """Get persisted period checkpoint timestamp (seconds since epoch).""" + with sqlite3.connect(self.db_path) as conn: + cursor = conn.cursor() + cursor.execute( + "SELECT last_period_end_ts FROM user_gamers WHERE user_id = ? AND gamer_id = ?", + (user_id, gamer_id) + ) + row = cursor.fetchone() + if not row or row[0] is None: + return None + return int(row[0]) + + def set_period_checkpoint(self, user_id: int, gamer_id: int, checkpoint_ts: int): + """Persist period checkpoint timestamp (seconds since epoch).""" + with sqlite3.connect(self.db_path) as conn: + cursor = conn.cursor() + cursor.execute( + "UPDATE user_gamers SET last_period_end_ts = ? WHERE user_id = ? AND gamer_id = ?", + (checkpoint_ts, user_id, gamer_id) + ) + conn.commit() + + def clear_period_checkpoint(self, user_id: int, gamer_id: int): + """Clear persisted period checkpoint.""" + with sqlite3.connect(self.db_path) as conn: + cursor = conn.cursor() + cursor.execute( + "UPDATE user_gamers SET last_period_end_ts = NULL WHERE user_id = ? AND gamer_id = ?", + (user_id, gamer_id) + ) + conn.commit() def remove_user_gamer(self, user_id: int, gamer_id: int) -> bool: """Remove gamer from user's tracked list""" diff --git a/LichessClientTG_bot/formatters.py b/LichessClientTG_bot/formatters.py index e0bf77d..2244740 100644 --- a/LichessClientTG_bot/formatters.py +++ b/LichessClientTG_bot/formatters.py @@ -164,7 +164,7 @@ class StatsFormatter: emoji = StatsFormatter._get_game_type_emoji(game_type) games_count = game_data.get('games_played', 0) rating_change = game_data.get('rating_change', 0) - rating = game_data.get('rating', 0) + rating = game_data.get('final_rating', game_data.get('rating', 0)) wins = game_data.get('wins', 0) losses = game_data.get('losses', 0) draws = game_data.get('draws', 0) From 0e3a0bfc488e76895f30798c7f827961ef844c6b Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Sat, 21 Mar 2026 23:14:51 +0300 Subject: [PATCH 09/10] Change API host port to 8002 --- LichessClientTG_bot/config.py | 2 +- LichessClientTG_bot/run.sh | 6 +++--- docker-compose.yml | 5 ++--- start.sh | 3 +-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/LichessClientTG_bot/config.py b/LichessClientTG_bot/config.py index e1b966b..727c83e 100644 --- a/LichessClientTG_bot/config.py +++ b/LichessClientTG_bot/config.py @@ -26,7 +26,7 @@ else: # Lichess API Configuration LICHESS_API_BASE_URL = "https://lichess.org/api" -LICHESS_STATS_API_BASE_URL = "http://localhost:8001" # For Docker container access +LICHESS_STATS_API_BASE_URL = "http://localhost:8002" # Host port for stats API when bot runs with host networking # Минимальная задержка (сек) между запросами к Lichess в очереди мониторинга (избежание бана) LICHESS_REQUEST_QUEUE_MIN_DELAY = 4.0 diff --git a/LichessClientTG_bot/run.sh b/LichessClientTG_bot/run.sh index 7c54d2d..fe4de4b 100755 --- a/LichessClientTG_bot/run.sh +++ b/LichessClientTG_bot/run.sh @@ -13,10 +13,10 @@ fi # Check if the API service is running echo "🔍 Checking Lichess API service..." -if curl -s http://localhost:8001/docs > /dev/null 2>&1; then - echo "✅ Lichess API service is running on http://localhost:8001" +if curl -s http://localhost:8002/docs > /dev/null 2>&1; then + echo "✅ Lichess API service is running on http://localhost:8002" else - echo "⚠️ Warning: Lichess API service is not accessible at http://localhost:8001" + echo "⚠️ Warning: Lichess API service is not accessible at http://localhost:8002" echo " Make sure your API service is running before starting the bot." fi diff --git a/docker-compose.yml b/docker-compose.yml index 7bfe595..dac9f95 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: build: ./LichessWebServices container_name: lichess-api ports: - - "8001:8000" + - "8002:8000" environment: - PYTHONUNBUFFERED=1 volumes: @@ -32,7 +32,7 @@ services: depends_on: - lichess-api healthcheck: - test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8001/health', timeout=5)"] + test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8002/health', timeout=5)"] interval: 30s timeout: 10s retries: 3 @@ -74,4 +74,3 @@ networks: - diff --git a/start.sh b/start.sh index 1d85b60..daed230 100755 --- a/start.sh +++ b/start.sh @@ -53,7 +53,7 @@ echo "" echo "✅ Все сервисы запущены!" echo "" echo "🌐 Доступные сервисы:" -echo " - API документация: http://localhost:8001/docs" +echo " - API документация: http://localhost:8002/docs" echo " - Веб-интерфейс: http://localhost:5000" echo "" echo "📋 Для просмотра логов используйте:" @@ -64,4 +64,3 @@ echo " ${COMPOSE_CMD_DISPLAY} down" - From a5e3e6903d4e0598a1fe9cb53687a4f7d390d169 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Tue, 9 Jun 2026 11:50:47 +0000 Subject: [PATCH 10/10] some --- LichessClientTG_bot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LichessClientTG_bot/config.py b/LichessClientTG_bot/config.py index 727c83e..a8b46d6 100644 --- a/LichessClientTG_bot/config.py +++ b/LichessClientTG_bot/config.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv load_dotenv() -IS_PROD = False +IS_PROD = True # Telegram Bot Configuration (Production) TELEGRAM_BOT_TOKEN_PROD = "8241474807:AAH684LTY93aXRou4-LtqU5-p8LuEjzYn8U"