пофиксил баг отправки уведомлений
This commit is contained in:
parent
6d51cf135d
commit
b3b6a54e2d
5 changed files with 253 additions and 32 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue