add per-game accuracy and outcome table to stats/notifications
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 31s

Games-of-period responses can now include a per-game breakdown
(include_games) with Lichess post-analysis accuracy, plus which side
the tracked player was on and their rating change for that game.

- lichess_client.py requests accuracy=true from Lichess; stats_service
  computes per-mode average accuracy and builds GameRow entries
  (tracked_is_white, tracked_rating_diff) for blitz/rapid/classical
- /games/{username}/period gained an include_games query param
- formatters.py renders a column-aligned monospace table per game:
  outcome circle (win/loss/draw relative to the tracked player),
  rating change, accuracy, names/ratings, result — for today/yesterday
  and periodic notifications; week keeps an aggregate accuracy line
- usernames are Markdown-escaped before formatting since messages are
  now sent with parse_mode='Markdown'
This commit is contained in:
vrubelroman 2026-07-03 09:38:51 +00:00
parent 4a783225af
commit d479e14bf9
8 changed files with 364 additions and 75 deletions

View file

@ -1070,8 +1070,40 @@ class LichessBot:
# Only send response if there's activity
if has_activity:
formatted_response = StatsFormatter.format_stats_response(data, username, period, lang)
await update.message.reply_text(formatted_response)
# Дополнительный, параллельный запрос за реальными партиями периода —
# только чтобы добавить точность (accuracy) и, для today/yesterday,
# построчный разбор партий. Шапку (данные из /activity) не трогаем:
# при любой ошибке здесь просто не покажем точность.
accuracy_extra = None
try:
now_dt = datetime.now()
if period == "today":
since_dt = now_dt.replace(hour=0, minute=0, second=0, microsecond=0)
until_dt = now_dt
want_rows = True
elif period == "yesterday":
today_midnight = now_dt.replace(hour=0, minute=0, second=0, microsecond=0)
since_dt = today_midnight - timedelta(days=1)
until_dt = today_midnight
want_rows = True
else: # week — то же окно (today-6 .. now), что использует _is_date_in_range(days_back=7)
since_dt = now_dt.replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=6)
until_dt = now_dt
want_rows = False
accuracy_extra = await self.lichess_api.get_games_period(
username,
int(since_dt.timestamp() * 1000),
int(until_dt.timestamp() * 1000),
rated_only=True,
include_games=want_rows
)
except Exception as e:
logger.warning(f"⚠️ Supplementary accuracy fetch failed for {username}/{period}: {e}")
accuracy_extra = None
formatted_response = StatsFormatter.format_stats_response(data, username, period, lang, accuracy_extra=accuracy_extra)
await update.message.reply_text(formatted_response, parse_mode='Markdown')
has_any_activity = True
else:
logger.info(f" No activity found for {username}, skipping response")
@ -1159,7 +1191,7 @@ class LichessBot:
if games_count > 0:
# Format and send immediately
text = StatsFormatter.format_last_year_or_1000(data, username, lang)
await update.message.reply_text(text)
await update.message.reply_text(text, parse_mode='Markdown')
has_any_activity = True
# Wait 3 seconds before next request (except after the last one)
@ -1585,7 +1617,8 @@ class LichessBot:
logger.info(f"📥 Adding games request to queue for {gamer['username']}")
games_data = await self.request_queue.add_request(
self.lichess_api.get_games_period,
gamer['username'], since_timestamp, until_timestamp_approx
gamer['username'], since_timestamp, until_timestamp_approx,
include_games=True
)
if games_data is None:
raise RuntimeError("Games period API returned no data")
@ -1695,7 +1728,8 @@ class LichessBot:
await self.application.bot.send_message(
chat_id=user_id,
text=notification
text=notification,
parse_mode='Markdown'
)
logger.info(f"✅ Sent periodic notification for {gamer['username']} to user {user_id}")
# Increment periodic notification counter