fix: auto-tournament search via local tnr_cache index
- Extract canonical FIDE ID from profile page (profile-info-id) - Add tnr_cache table for local tournament index with players - scan_for_player uses cache-only search (instant SQL, 0 HTTP) - warmup_cache builds full index at step 1 (1445000→1434000) - Save/resume warmup progress via tnr_state - Auto-migrate missing columns (start_date/end_date) - Add hourly rescan_new_tournaments job - Fix discover_max_tnr (larger probe steps, lower default) - Use asyncio.to_thread() for warmup (no crutches)
This commit is contained in:
parent
7ebe1aa66e
commit
89f5861ee3
2 changed files with 514 additions and 18 deletions
|
|
@ -262,9 +262,10 @@ async def my_players(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||
for s in subs:
|
||||
safe_name = _md_escape(s['player_name'])
|
||||
safe_tour = _md_escape(s['tournament_name'] or '—')
|
||||
last_pts_str = _md_escape(f'{s["last_points"]:.1f}')
|
||||
lines.append(
|
||||
f' {s["id"]}\\. {safe_name} \({s["rating"]}, {s["fed"]}\) '
|
||||
f'— {safe_tour} — {s["last_points"]:.1f} очк\\.')
|
||||
f'— {safe_tour} — {last_pts_str} очк\\.')
|
||||
lines.append('\n/removeplayer <номер> — удалить подписку')
|
||||
await update.message.reply_text('\n'.join(lines),
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
|
@ -315,19 +316,47 @@ async def _lookup_and_confirm(update: Update, context, fide_id: int):
|
|||
|
||||
safe_name = _md_escape(player['name'])
|
||||
title_str = f', {player["title"]}' if player.get('title') else ''
|
||||
|
||||
msg = (
|
||||
await update.message.reply_text(
|
||||
f'✅ *{safe_name}*{title_str}\n'
|
||||
f'{"🇫 " if player["fed"] else ""}{player["fed"]} \\| Рейтинг: {player["rating"]}\n\n'
|
||||
'Теперь отправь ссылку на турнир chess\\-results\\.com, '
|
||||
'где играет этот игрок\.\n'
|
||||
'/cancel — отмена'
|
||||
)
|
||||
context.user_data['pending_fide'] = {
|
||||
'player': player,
|
||||
'lang': lang_code,
|
||||
}
|
||||
await update.message.reply_text(msg, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
'🔍 Ищу турниры на chess\\-results\\.com\\.\\.',
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
||||
# Run scan asynchronously (avoid blocking the event loop)
|
||||
# Use canonical FIDE ID from profile page (may differ from URL-extracted ID)
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
loop = asyncio.get_event_loop()
|
||||
tournaments = await loop.run_in_executor(
|
||||
None, tracker.scan_for_player, player['fide_id'])
|
||||
|
||||
if tournaments:
|
||||
# Auto-subscribe to all found tournaments
|
||||
lines = [f'✅ *Найдено турниров: {len(tournaments)}*']
|
||||
for t in tournaments:
|
||||
tracker.add_subscription(
|
||||
user.id, player, t['url'], t['name'], t['sno'],
|
||||
0, 0.0, lang_code, t['start_date'], t['end_date'])
|
||||
dates = ''
|
||||
if t['start_date'] and t['end_date']:
|
||||
dates = f' \({t["start_date"]} — {t["end_date"]}\)'
|
||||
safe_tour = _md_escape(t['name'])
|
||||
lines.append(f' • {safe_tour}{dates}')
|
||||
lines.append('\nЯ буду присылать результаты и жеребьёвку\.\n'
|
||||
'/myplayers — список подписок')
|
||||
await update.message.reply_text('\n'.join(lines),
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
else:
|
||||
# No tournaments found — fallback to manual URL
|
||||
context.user_data['pending_fide'] = {
|
||||
'player': player,
|
||||
'lang': lang_code,
|
||||
}
|
||||
await update.message.reply_text(
|
||||
'❌ Турниров не найдено автоматически\\.\n\n'
|
||||
'Отправь ссылку на турнир chess\\-results\\.com, '
|
||||
'где играет этот игрок\\.\n'
|
||||
'/cancel — отмена',
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
||||
|
||||
def _apply_round_results(tournament, games, rd):
|
||||
|
|
@ -442,7 +471,7 @@ async def _subscribe_to_tournament(update: Update, context, url: str, pending: d
|
|||
await msg.edit_text(
|
||||
f'✅ *{safe_name}* найден в турнире\\.\n'
|
||||
f'📅 *{safe_tour}*\n'
|
||||
f'Ст\\.№ {p_sno}, {points:.1f} очк\\. — отслеживаю\!\n\n'
|
||||
f'Ст\\.№ {p_sno}, {_md_escape(f"{points:.1f}")} очк\\. — отслеживаю\!\n\n'
|
||||
'Я буду присылать результаты игрока и жеребьёвку следующих туров\.\n'
|
||||
'/myplayers — список подписок',
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
|
@ -630,6 +659,12 @@ def main():
|
|||
|
||||
app.job_queue.run_repeating(
|
||||
tracker.check_all_subscriptions, interval=300, first=10)
|
||||
app.job_queue.run_repeating(
|
||||
tracker.rescan_new_tournaments, interval=3600, first=60)
|
||||
app.job_queue.run_repeating(
|
||||
tracker.warmup_cache, interval=21600, first=10)
|
||||
|
||||
print('Client bot started', file=sys.stderr)
|
||||
|
||||
print('Client bot started', file=sys.stderr)
|
||||
app.run_polling()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue