очередь запросов и интервал 7 сек

This commit is contained in:
vrubelroman 2025-11-20 03:23:38 +03:00
parent 4dc5539da2
commit c16a11cf63
2 changed files with 203 additions and 32 deletions

View file

@ -21,6 +21,7 @@ from formatters import StatsFormatter
from i18n import t
from admin_bot import get_admin_bot, init_admin_bot
from message_counters import MessageCounters
from request_queue import get_request_queue
import time
import aiohttp
@ -42,6 +43,7 @@ class LichessBot:
self.period_start_times = {} # Store start times for each gamer
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 _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)."""
@ -164,12 +166,20 @@ class LichessBot:
if len(gamers_with_periods) == 0:
logger.warning("⚠️ No periodic notifications configured! Users need to set periods using /setperiod")
# Start request queue processor
self.request_queue._start_processor()
logger.info("✅ Request queue processor started")
for gamer in gamers_with_periods:
if gamer['period_minutes'] > 0:
user_id = gamer['user_id']
username = gamer['username']
period = gamer['period_minutes']
# Start periodic task with user_id and gamer
await self.start_periodic_task(gamer, user_id, gamer['period_minutes'])
logger.info(f"✅ Started periodic task for {gamer['username']} (user {user_id}) with period {gamer['period_minutes']} minutes")
await self.start_periodic_task(gamer, user_id, period)
logger.info(f"✅ Started periodic task for {username} (user {user_id}) with period {period} minutes")
logger.info(f"✅ All periodic tasks started. Total: {len([g for g in gamers_with_periods if g['period_minutes'] > 0])}")
# Start daily counter reset task
asyncio.create_task(self.daily_counter_reset_task())
@ -1186,11 +1196,13 @@ class LichessBot:
async def periodic_check(self, gamer: Dict[str, Any], user_id: int, period_minutes: int):
"""Periodic check for gamer activity"""
task_key = f"{gamer['id']}_{user_id}"
username = gamer['username']
# Запоминаем время начала отслеживания (текущее время минус период, чтобы сразу проверить последний период)
start_time = datetime.now() - timedelta(minutes=period_minutes)
# Инициализируем время начала отслеживания как текущее время
# Первая проверка произойдет через period_minutes минут
start_time = datetime.now()
self.period_start_times[task_key] = start_time
logger.info(f"Started periodic monitoring for {gamer['username']} with {period_minutes} minute intervals")
logger.info(f"🔄 Started periodic monitoring for {username} (user {user_id}) with {period_minutes} minute intervals")
consecutive_errors = 0
max_consecutive_errors = 5
@ -1221,33 +1233,36 @@ 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)
# Получаем время начала периода (время последней проверки или время старта задачи)
period_start = self.period_start_times.get(task_key, start_time)
# Получаем текущее время
now = datetime.now()
# Рассчитываем период: от (текущее время - период) до текущего времени
# Это гарантирует, что мы проверяем последний час активности
# Это гарантирует, что мы проверяем последний период активности
since_time = now - timedelta(minutes=period_minutes)
since_timestamp = int(since_time.timestamp() * 1000)
until_timestamp = int(now.timestamp() * 1000)
logger.info(f"Checking period for {gamer['username']}: {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 {now} (last {period_minutes} minutes)")
logger.info(f"📅 Unix timestamps: since={since_timestamp}, until={until_timestamp}")
# Делаем запросы к API с обработкой ошибок
# Делаем запросы к API через очередь с обработкой ошибок
games_data = None
puzzles_data = None
try:
games_data = await self.lichess_api.get_games_period(
# Добавляем запрос в очередь (будет выполнен с задержкой 7 секунд)
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
)
logger.info(f"Games API response received for {gamer['username']}")
logger.info(f"Games API response received for {gamer['username']}")
except Exception as e:
logger.error(f"Error getting games data for {gamer['username']}: {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")
@ -1259,12 +1274,15 @@ class LichessBot:
if gamer.get('token'):
try:
puzzles_data = await self.lichess_api.get_puzzles_period(
gamer['token'], since_timestamp, until_timestamp, max_puzzles=150
# Добавляем запрос в очередь (будет выполнен с задержкой 7 секунд)
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
)
logger.info(f"Puzzles API response received for {gamer['username']}")
logger.info(f"Puzzles API response received for {gamer['username']}")
except Exception as e:
logger.warning(f"Error getting puzzles data for {gamer['username']}: {e}")
logger.warning(f"⚠️ Error getting puzzles data for {gamer['username']}: {e}")
# Продолжаем без данных по пазлам
# Сбрасываем счетчик ошибок при успешном запросе
@ -1275,29 +1293,38 @@ class LichessBot:
total_games = 0
if games_data:
# Логируем структуру ответа для отладки
logger.debug(f"Games data structure for {gamer['username']}: {games_data}")
if games_data.get('data'):
total_games = games_data.get('data', {}).get('total', {}).get('games_played', 0)
has_games = total_games > 0
# Также проверяем games_count на верхнем уровне
elif games_data.get('games_count', 0) > 0:
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)
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:
logger.warning(f"No games_data returned for {gamer['username']}")
logger.warning(f"⚠️ No games_data returned for {username}")
has_puzzles = False
total_puzzles = 0
if puzzles_data:
if puzzles_data.get('data'):
total_puzzles = puzzles_data.get('data', {}).get('total_attempts', 0)
has_puzzles = total_puzzles > 0
# Также проверяем puzzles_in_period на верхнем уровне
elif puzzles_data.get('puzzles_in_period', 0) > 0:
logger.info(f"📊 Puzzles data structure for {username}: {puzzles_data}")
# Проверяем puzzles_in_period на верхнем уровне (приоритет)
if puzzles_data.get('puzzles_in_period', 0) > 0:
total_puzzles = puzzles_data.get('puzzles_in_period', 0)
has_puzzles = True
logger.info(f"✅ Found {total_puzzles} puzzles via puzzles_in_period field")
# Также проверяем data.total_attempts
elif puzzles_data.get('data') and puzzles_data.get('data', {}).get('total_attempts', 0) > 0:
total_puzzles = puzzles_data.get('data', {}).get('total_attempts', 0)
has_puzzles = True
logger.info(f"✅ Found {total_puzzles} puzzles via data.total_attempts field")
logger.info(f"Activity check for {gamer['username']}: has_games={has_games} (total={total_games}), has_puzzles={has_puzzles} (total={total_puzzles})")
logger.info(f"🔍 Activity check result for {username}: has_games={has_games} (total={total_games}), has_puzzles={has_puzzles} (total={total_puzzles})")
# Отправляем уведомление только если есть реальная активность
if has_games or has_puzzles: