No description
Find a file
vrubel 39f1f7e857
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 14s
feat: inline remove button in /myplayers, localized no-players message
- /myplayers: each player card gets inline 🗑 button — tap to remove instantly
- removal via CallbackQueryHandler (remove:<fide_id>), no text command needed
- /removeplayer command removed
- no-players message localized (ru/en) via LOCALE
- /myplayers header and labels (round, pts) also localized

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 23:04:32 +00:00
.forgejo/workflows Fix CI test step: use docker run instead of compose 2026-06-14 21:47:37 +00:00
bots feat: inline remove button in /myplayers, localized no-players message 2026-06-20 23:04:32 +00:00
swiss_calc Парсер: поддержка латиницы (международные турниры) + таблица: колонки Рейт и Доп1 2026-06-19 14:51:15 +00:00
.env.example Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
.gitignore Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
bbpPairings.exe chessCalc: исправлена точность жеребьёвки до 100% 2026-06-14 20:29:27 +00:00
docker-compose.prod.yml Add CI/CD pipeline (Forgejo) and prod docker-compose 2026-06-14 21:41:07 +00:00
docker-compose.yml Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
Dockerfile Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
README.md docs: comprehensive README with player tracking, cache, and jobs 2026-06-20 22:14:38 +00:00
requirements.txt Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
test_all_rounds.py chessCalc: исправлена точность жеребьёвки до 100% 2026-06-14 20:29:27 +00:00
test_round_by_round.py chessCalc: исправлена точность жеребьёвки до 100% 2026-06-14 20:29:27 +00:00

♟️ chessCalc — калькулятор швейцарской жеребьёвки + авто-отслеживание игроков

Расчёт пар следующего тура шахматного турнира до официальной публикации на chess-results.com. Плюс подписка на игрока по FIDE ID: бот сам находит турниры, отслеживает результаты и присылает жеребьёвку с подсветкой игрока.

Два Telegram-бота: клиентский и админский.


Возможности

Клиентский бот

Команда Описание
/addplayer Подписаться на игрока по FIDE ID — бот сам найдёт турнир на chess-results
/myplayers Список активных подписок
/removeplayer <N> Удалить подписку
/cancel Отменить текущую операцию
URL турнира Прислать ссылку chess-results.com — получить жеребьёвку следующего тура

Авто-отслеживание:

  • Каждые 5 минут — проверка новых результатов подписанных игроков
  • При завершении тура — расчёт и отправка жеребьёвки следующего тура (±5 пар вокруг игрока, зелёная галочка)
  • Результат каждого тура: ♟ победа/ничья/поражение

Админский бот

  • /stat — уникальные пользователи, всего запросов, запросов за сегодня

Как запустить

# 1. Токены
cp .env.example .env   # отредактировать токены

# 2. Запуск
docker compose up -d

CLI (отладка)

# Пары следующего тура
docker compose run --rm client-bot python3 -m swiss_calc 'URL'

# JSON
docker compose run --rm client-bot python3 -m swiss_calc --json 'URL'

# Положение
docker compose run --rm client-bot python3 -m swiss_calc --standings 'URL'

Архитектура

chessCalc/
├── Dockerfile                  # python:3.11-slim + bbpPairings
├── docker-compose.yml          # client-bot + admin-bot + volume bot-data
├── .env.example                # шаблон токенов
├── .env                        # реальные токены (gitignored)
├── bbpPairings.exe             # FIDE-движок Dutch System (C++, 2.4 MB)
├── requirements.txt            # requests, bs4, python-telegram-bot, rich
├── swiss_calc/                 # Ядро — парсинг + расчёт
│   ├── __main__.py             # CLI entry point
│   ├── parser.py               # Парсинг chess-results.com (art=2/4/5)
│   ├── trf_generator.py        # Генерация TRF-16 для bbpPairings
│   ├── bbp_wrapper.py          # Вызов bbpPairings.exe через subprocess
│   ├── swiss.py                # Оркестрация + fallback Swiss-алгоритм
│   └── display.py              # Форматирование вывода (CLI)
├── bots/                       # Telegram-боты
│   ├── client_bot.py           # Клиентский бот
│   ├── admin_bot.py            # Админский бот (/stat)
│   ├── tracker.py              # Отслеживание игроков, кэш турниров, джобы
│   └── stats.py                # Статистика (SQLite)
└── TASKS.md                    # Журнал изменений

Поток данных — жеребьёвка

URL турнира
      │
      ▼
┌─────────────────────────────────────────┐
│  parser.fetch_tournament()              │
│  1. art=4 — положение                   │
│  2. art=5 — стартовый список             │
│  3. Сопоставление имён → SNo             │
│  4. art=2&rd=1..N — пары каждого тура    │
└─────────────────────────────────────────┘
      │  tournament_data
      ▼
┌─────────────────────────────────────────┐
│  swiss.calculate_next_round()           │
│  1. Генерация TRF-16                    │
│  2. bbpPairings --dutch (FIDE 2025)     │
│     └→ fallback: упрощённый Swiss       │
└─────────────────────────────────────────┘
      │  пары (доски)
      ▼
┌─────────────────────────────────────────┐
│  format_pairings() → _render_chunks()   │
│  MarkdownV2 таблица в code-блоке        │
└─────────────────────────────────────────┘

Поток данных — подписка на игрока

FIDE ID (напр. 55867510)
      │
      ▼
┌─────────────────────────────────────────┐
│  tracker.fetch_fide_player()            │
│  Парсинг ratings.fide.com/profile/{ID}  │
│  → имя, рейтинг, FED, каноничный FIDE ID│
└─────────────────────────────────────────┘
      │  player dict
      ▼
┌─────────────────────────────────────────┐
│  tracker.scan_for_player()              │
│  SQL-запрос к tnr_cache:                │
│  SELECT tnr FROM tnr_cache              │
│  WHERE players_json LIKE '%{fide_id}%'  │
│  → список TNR с этим игроком             │
└─────────────────────────────────────────┘
      │  tournaments list
      ▼
┌─────────────────────────────────────────┐
│  Авто-подписка + уведомление            │
│  → запись в subscriptions               │
│  → сообщение пользователю               │
└─────────────────────────────────────────┘

Локальный кэш турниров (tnr_cache)

Таблица SQLite с полным списком игроков каждого турнира:

tnr_cache
├── tnr          INTEGER PRIMARY KEY   — номер турнира на chess-results
├── name         TEXT                   — название турнира
├── start_date   TEXT                   — дата начала
├── end_date     TEXT                   — дата окончания
└── players_json TEXT                   — JSON: {fide_id: {sno, name, rating, fed}}
  • При первом фетче art=0 страницы турнира данные сохраняются в кэш
  • 1979+ турниров в кэше (1.4M кластер, 2025-2026)
  • Поиск игрока — один SQL LIKE-запрос, ~0.015 секунды, без HTTP

Прогрев кэша (warmup_cache)

  • При старте: полный скан кластера 1.4M с шагом 1 (~50 минут)
  • Сохраняет прогресс в tnr_state — при перезапуске продолжает
  • После завершения (warmup_tnr = done) — пропускает, если кэш > 1000 записей
  • Каждые 6 часов — перепроверка кластера

Фоновые задачи

Задача Интервал Что делает
check_all_subscriptions 5 мин Проверяет новые результаты, считает и отправляет жеребьёвку
rescan_new_tournaments 1 час Ищет новые TNR > max_tnr_seen, авто-подписывает
rescan_existing_tournaments 2 часа Рефетчит art=0 для всех TNR (мимо кэша), находит новых игроков
warmup_cache 6 часов Обновляет tnr_cache (если нужен)

База данных (SQLite)

Таблицы в /app/data/tournaments.db (Docker volume bot-data):

Таблица Назначение
subscriptions Активные подписки: user_id, fide_id, player_name, tournament_url, ...
tnr_cache Кэш турниров: tnr, name, players_json
tnr_state Состояние: max_tnr_seen, warmup_tnr, warmup progress
users / requests Статистика использования (админский бот)

Авто-миграция схемы: недостающие колонки добавляются при каждом _get_conn().


Источники данных

Источник URL Что даёт
FIDE профиль ratings.fide.com/profile/{ID} Имя, рейтинг, федерация, каноничный FIDE ID
chess-results art=0 chess-results.com/tnr{N}.aspx?lan=11&art=0 Список всех игроков турнира с FIDE ID
chess-results art=4 …&art=4 Положение (имена, очки, тайбрейки)
chess-results art=5 …&art=5 Стартовый список (SNo, рейтинги)
chess-results art=2 …&art=2&rd=N Пары тура N

Парсер поддерживает 12-колоночный (новые турниры) и 10-колоночный (старые) форматы.


Движок жеребьёвки

Основной: bbpPairings — C++ реализация Dutch System по правилам FIDE 2025/2026.

Резервный: упрощённый швейцарский алгоритм на Python (fold + перебор offset).


Точность жеребьёвки

Проверено на турнирах Первенства России (2026):

Турнир Уч. Точность
1393121 39 100%
1393124 93 100%
1393133 96 100%
1393137 14 100%
1393131 112 85% *

* Расхождения только в нижних досках из-за ручных bye/forfeit-ов.


Ограничения

  1. Ручные bye/forfeit-ы — решение арбитра, алгоритм не предсказывает
  2. 10-колоночный формат — старые турниры без SNo, точность ~80%
  3. Круговые турниры — не поддерживаются
  4. TNR-пространство не сплошное — кластеры с пропусками, требуется кэш
  5. Латиница/кириллица — имена на chess-results могут отличаться от FIDE, матчинг по SNo