diff --git a/bots/client_bot.py b/bots/client_bot.py index b3e06ea..d33c651 100644 --- a/bots/client_bot.py +++ b/bots/client_bot.py @@ -191,6 +191,76 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text(START_MSG, parse_mode=ParseMode.MARKDOWN_V2) +def _apply_round_results(tournament, games, rd): + standings = tournament['standings'] + sno_to_standing = {s['starting_sno']: s for s in standings if s.get('starting_sno')} + name_to_standing = { + re.sub(r'\s+', '', s['name'].lower()): s + for s in standings + } + name_parts_to_standing: dict[str, list] = {} + for s in standings: + parts = re.sub(r'\s+', '', s['name'].lower()).split() + for p in parts: + name_parts_to_standing.setdefault(p, []).append(s) + + def _find_player(sno, name_str): + p = sno_to_standing.get(sno) + if p: + return p + if name_str: + norm = re.sub(r'\s+', '', name_str.lower()) + p = name_to_standing.get(norm) + if p: + return p + parts = norm.split() + for part in parts: + candidates = name_parts_to_standing.get(part, []) + if len(candidates) == 1: + return candidates[0] + return None + + for g in games: + if g is None: + continue + ws, bs = g['white_sno'], g['black_sno'] + ws_result = g.get('white_score') + bs_result = g.get('black_score') + + if ws_result is None: + continue + + ws_val = float(ws_result) + bs_val = float(bs_result) + + wp = _find_player(ws, g.get('white_name', '')) + bp = _find_player(bs, g.get('black_name', '')) + + if wp: + opp_sno = bs if bs != 0 else (bp.get('starting_sno') if bp else 0) + existing = [r for r in wp.get('results', []) if r.get('round') != rd] + existing.append({'opponent': opp_sno, 'color': 'w', 'score': ws_val, 'round': rd}) + wp['results'] = existing + + if bp: + opp_sno = ws if ws != 0 else (wp.get('starting_sno') if wp else 0) + existing = [r for r in bp.get('results', []) if r.get('round') != rd] + existing.append({'opponent': opp_sno, 'color': 'b', 'score': bs_val, 'round': rd}) + bp['results'] = existing + + for s in standings: + sno = s.get('starting_sno') + if sno and len(s.get('results', [])) < rd: + orig_results = s.get('_orig_results', []) + if len(orig_results) >= rd: + res = orig_results[rd - 1] + res['round'] = rd + s['results'] = list(s.get('results', [])) + s['results'].append(res) + + tournament['current_round'] = rd + + async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE): user = update.effective_user text = update.message.text.strip() @@ -239,23 +309,36 @@ async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE): base_url = re.sub(r'[&?]turdet=\w+', '', base_url) base_url = re.sub(r'[&?]SNode=\w+', '', base_url) next_rd = tournament['current_round'] + 1 + initial_rd = next_rd from swiss_calc.parser import fetch_url as _fetch_url, parse_round_pairings try: - rd_html = _fetch_url(f'{base_url}&art=2&rd={next_rd}&turdet=YES') - rd_games = parse_round_pairings(rd_html, next_rd) - played = any(g.get('white_score') is not None for g in rd_games if g) - if played: - await update.message.reply_text( - f'⚠️ Тур {next_rd} уже сыгран — на сайте есть результаты\\.', - parse_mode=ParseMode.MARKDOWN_V2) + while next_rd <= tournament['num_rounds']: + rd_html = _fetch_url(f'{base_url}&art=2&rd={next_rd}&turdet=YES') + rd_games = parse_round_pairings(rd_html, next_rd) + played = any(g.get('white_score') is not None for g in rd_games if g) + if played: + _apply_round_results(tournament, rd_games, next_rd) + next_rd += 1 + continue + if rd_games: + await update.message.reply_text( + f'⚠️ Жеребьёвка тура {next_rd} уже опубликована на chess\\-results\\.com\\.\n' + 'Я всё равно посчитаю, но пары могут незначительно отличаться ' + '\\(разные редакции правил FIDE\\)\\.', + parse_mode=ParseMode.MARKDOWN_V2) + break + else: + await msg.edit_text('🏁 Турнир уже завершён\\. Жеребьёвки не будет\\.', + parse_mode=ParseMode.MARKDOWN_V2) record_request(user.id, url, tournament.get('name', ''), True) return - if rd_games: - await update.message.reply_text( - f'⚠️ Жеребьёвка тура {next_rd} уже опубликована на chess\\-results\\.com\\.\n' - 'Я всё равно посчитаю, но пары могут незначительно отличаться ' - '\\(разные редакции правил FIDE\\)\\.', + + if next_rd > initial_rd: + await msg.edit_text( + f'✅ *{_md_escape(tournament["name"])}*\n' + f'📅 Сыграно туров: {tournament["current_round"]} из {tournament["num_rounds"]}\n' + f'👥 Участников: {len(tournament["standings"])}', parse_mode=ParseMode.MARKDOWN_V2) except Exception: pass