feat: rescan_existing_tournaments — detect new players in cached tournaments every 2h
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 6s
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 6s
This commit is contained in:
parent
07b016bff9
commit
d18c5016ce
2 changed files with 75 additions and 0 deletions
|
|
@ -721,6 +721,8 @@ def main():
|
||||||
tracker.check_all_subscriptions, interval=300, first=10)
|
tracker.check_all_subscriptions, interval=300, first=10)
|
||||||
app.job_queue.run_repeating(
|
app.job_queue.run_repeating(
|
||||||
tracker.rescan_new_tournaments, interval=3600, first=60)
|
tracker.rescan_new_tournaments, interval=3600, first=60)
|
||||||
|
app.job_queue.run_repeating(
|
||||||
|
tracker.rescan_existing_tournaments, interval=7200, first=300)
|
||||||
app.job_queue.run_repeating(
|
app.job_queue.run_repeating(
|
||||||
tracker.warmup_cache, interval=21600, first=10)
|
tracker.warmup_cache, interval=21600, first=10)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,16 @@ def _fetch_and_parse_art0(tnr: int) -> dict | None:
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def _refetch_art0(tnr: int) -> dict | None:
|
||||||
|
"""Always fetch art=0 via HTTP, update cache. Returns parsed dict or None."""
|
||||||
|
html = _fetch_art0(tnr)
|
||||||
|
if html is None:
|
||||||
|
return None
|
||||||
|
parsed = _parse_art0_page(html)
|
||||||
|
_save_tnr_cache(tnr, parsed)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
# ═══ Tournament scanner (art=0 pages) ═══
|
# ═══ Tournament scanner (art=0 pages) ═══
|
||||||
|
|
||||||
ART0_URL = 'https://chess-results.com/tnr{tnr}.aspx?lan=11&art=0&turdet=YES'
|
ART0_URL = 'https://chess-results.com/tnr{tnr}.aspx?lan=11&art=0&turdet=YES'
|
||||||
|
|
@ -684,6 +694,69 @@ def _warmup_cache_sync():
|
||||||
set_tnr_state('warmup_tnr', 'done')
|
set_tnr_state('warmup_tnr', 'done')
|
||||||
|
|
||||||
|
|
||||||
|
async def rescan_existing_tournaments(context):
|
||||||
|
"""Every 2h: refetch art=0 for all cached TNRs, detect new players."""
|
||||||
|
import time
|
||||||
|
from bots.client_bot import _md_escape as _md_esc
|
||||||
|
|
||||||
|
conn = _get_conn()
|
||||||
|
cached_tnrs = [r[0] for r in conn.execute('SELECT tnr FROM tnr_cache ORDER BY tnr DESC').fetchall()]
|
||||||
|
conn.close()
|
||||||
|
if not cached_tnrs:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Collect subscribed FIDE IDs and their users
|
||||||
|
conn = _get_conn()
|
||||||
|
sub_rows = conn.execute(
|
||||||
|
'SELECT DISTINCT user_id, fide_id, lang FROM subscriptions WHERE active = 1'
|
||||||
|
).fetchall()
|
||||||
|
conn.close()
|
||||||
|
if not sub_rows:
|
||||||
|
return
|
||||||
|
fide_to_users = {}
|
||||||
|
for user_id, fide_id, lang in sub_rows:
|
||||||
|
fide_to_users.setdefault(fide_id, []).append((user_id, lang))
|
||||||
|
|
||||||
|
for tnr in cached_tnrs:
|
||||||
|
old_data = _get_cached_tnr(tnr)
|
||||||
|
old_fids = set(old_data['players'].keys()) if old_data else set()
|
||||||
|
|
||||||
|
new_data = _refetch_art0(tnr)
|
||||||
|
if new_data is None:
|
||||||
|
continue # tournament deleted or unreachable
|
||||||
|
new_fids = set(new_data['players'].keys())
|
||||||
|
|
||||||
|
# New players that appeared since last cached snapshot
|
||||||
|
appeared = new_fids - old_fids
|
||||||
|
for fid in appeared:
|
||||||
|
if fid not in fide_to_users:
|
||||||
|
continue
|
||||||
|
player_info = new_data['players'][fid]
|
||||||
|
url = f'https://chess-results.com/tnr{tnr}.aspx?lan=11'
|
||||||
|
for user_id, lang in fide_to_users[fid]:
|
||||||
|
player = {'fide_id': fid, 'name': player_info['name'],
|
||||||
|
'rating': player_info.get('rating', 0), 'fed': ''}
|
||||||
|
add_subscription(
|
||||||
|
user_id, player, url, new_data['name'] or f'Tournament {tnr}',
|
||||||
|
player_info['sno'], 0, 0.0, lang,
|
||||||
|
new_data.get('start_date', ''), new_data.get('end_date', ''))
|
||||||
|
try:
|
||||||
|
safe_name = _md_esc(player_info['name'])
|
||||||
|
safe_tour = _md_esc(new_data['name'] or f'Tournament {tnr}')
|
||||||
|
dates = ''
|
||||||
|
if new_data.get('start_date') and new_data.get('end_date'):
|
||||||
|
dates = f'\\({new_data["start_date"]} — {new_data["end_date"]}\\)'
|
||||||
|
await context.bot.send_message(
|
||||||
|
user_id,
|
||||||
|
f'🆕 *{safe_name}* появился в турнире\!\n'
|
||||||
|
f'📅 *{safe_tour}*{dates}\n'
|
||||||
|
'Автоматически подписал — буду отслеживать\.',
|
||||||
|
parse_mode='MarkdownV2')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
async def check_all_subscriptions(context):
|
async def check_all_subscriptions(context):
|
||||||
from swiss_calc.parser import fetch_tournament
|
from swiss_calc.parser import fetch_tournament
|
||||||
from swiss_calc.swiss import calculate_next_round
|
from swiss_calc.swiss import calculate_next_round
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue