feat: rapid/blitz ratings, instant addplayer, hyperlinks, bug fixes
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 7s
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 7s
- fetch_fide_player: parse rapid and blitz ratings from FIDE profile - subscriptions: add rapid/blitz columns (auto-migration) - /myplayers card: show rapid (📍) and blitz (⚡) ratings - /addplayer confirmation: show rapid/blitz alongside standard rating - find_player_in_cache: extract instant SQL-only search from scan_for_player - /addplayer: use find_player_in_cache instead of full HTTP scan — instant response - tournament names in /myplayers and /addplayer are now hyperlinks - add_player_watch: ON CONFLICT DO UPDATE to reactivate deleted players - my_players: try/except per card with plain-text fallback, error logged to stderr Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
39f1f7e857
commit
66b10d9425
2 changed files with 92 additions and 43 deletions
|
|
@ -332,8 +332,16 @@ def _player_card(p: dict, loc: dict) -> tuple[str, InlineKeyboardMarkup]:
|
|||
rating = p['rating'] or '—'
|
||||
fide_id = p['fide_id']
|
||||
|
||||
rapid = p.get('rapid') or 0
|
||||
blitz = p.get('blitz') or 0
|
||||
ratings = str(rating)
|
||||
if rapid:
|
||||
ratings += f' 📍{rapid}'
|
||||
if blitz:
|
||||
ratings += f' ⚡{blitz}'
|
||||
|
||||
lines = [f'👤 *{safe_name}*']
|
||||
lines.append(f'🏳 {_md_escape(fed)} · {rating}')
|
||||
lines.append(f'🏳 {_md_escape(fed)} · {_md_escape(ratings)}')
|
||||
|
||||
tours = p['tournaments']
|
||||
waiting = all(t['name'] is None for t in tours)
|
||||
|
|
@ -343,10 +351,13 @@ def _player_card(p: dict, loc: dict) -> tuple[str, InlineKeyboardMarkup]:
|
|||
for t in tours:
|
||||
if t['name'] is None:
|
||||
continue
|
||||
safe_tour = _md_escape(t['name'])
|
||||
pts = _md_escape(f'{t["points"]:.1f}')
|
||||
rd = t['round_done']
|
||||
lines.append(f'📌 {safe_tour}')
|
||||
if t.get('url'):
|
||||
tour_link = f'[{_md_escape(t["name"])}]({t["url"]})'
|
||||
else:
|
||||
tour_link = _md_escape(t['name'])
|
||||
lines.append(f'📌 {tour_link}')
|
||||
lines.append(f' {pts} {loc["points_label"]}, {loc["round_label"]} {rd}')
|
||||
|
||||
keyboard = InlineKeyboardMarkup([[
|
||||
|
|
@ -368,8 +379,17 @@ async def my_players(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||
await update.message.reply_text(header, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
for p in players:
|
||||
text, keyboard = _player_card(p, loc)
|
||||
await update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN_V2,
|
||||
reply_markup=keyboard)
|
||||
try:
|
||||
await update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN_V2,
|
||||
reply_markup=keyboard,
|
||||
disable_web_page_preview=True)
|
||||
except Exception as e:
|
||||
import sys
|
||||
print(f'my_players card error: {e}\ntext={text!r}', file=sys.stderr)
|
||||
# Fallback: send as plain text without formatting
|
||||
plain = text.replace('\\', '').replace('*', '').replace('_', '')
|
||||
await update.message.reply_text(plain, reply_markup=keyboard,
|
||||
disable_web_page_preview=True)
|
||||
|
||||
|
||||
async def remove_player_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
|
|
@ -422,18 +442,20 @@ async def _lookup_and_confirm(update: Update, context, fide_id: int):
|
|||
title = player.get('title') or ''
|
||||
title_str = f', {_md_escape(title)}' if title and title != 'None' else ''
|
||||
fed = player.get('fed') or ''
|
||||
ratings_parts = [f'Рейтинг: {player["rating"]}']
|
||||
if player.get('rapid'):
|
||||
ratings_parts.append(f'📍{player["rapid"]}')
|
||||
if player.get('blitz'):
|
||||
ratings_parts.append(f'⚡{player["blitz"]}')
|
||||
ratings_str = _md_escape(' '.join(ratings_parts))
|
||||
await update.message.reply_text(
|
||||
f'✅ *{safe_name}*{title_str}\n'
|
||||
f'{"🇫 " if fed else ""}{_md_escape(fed)} \\| Рейтинг: {player["rating"]}\n\n'
|
||||
f'{"🇫 " if fed else ""}{_md_escape(fed)} \\| {ratings_str}\n\n'
|
||||
'🔍 Ищу турниры на 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'])
|
||||
# SQL-only cache search (instant, no HTTP). Background jobs handle HTTP discovery.
|
||||
tournaments = tracker.find_player_in_cache(player['fide_id'])
|
||||
|
||||
if tournaments:
|
||||
# Auto-subscribe to all found tournaments
|
||||
|
|
@ -445,8 +467,8 @@ async def _lookup_and_confirm(update: Update, context, fide_id: int):
|
|||
dates = ''
|
||||
if t['start_date'] and t['end_date']:
|
||||
dates = f' \\({_md_escape(t["start_date"])} — {_md_escape(t["end_date"])}\\)'
|
||||
safe_tour = _md_escape(t['name'])
|
||||
lines.append(f' • {safe_tour}{dates}')
|
||||
tour_link = f'[{_md_escape(t["name"])}]({t["url"]})'
|
||||
lines.append(f' • {tour_link}{dates}')
|
||||
lines.append('\nЯ буду присылать результаты и жеребьёвку\.\n'
|
||||
'/myplayers \\- список подписок')
|
||||
await update.message.reply_text('\n'.join(lines),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue