diff --git a/LichessClientTG_bot/bot.py b/LichessClientTG_bot/bot.py index 80d6cf0..80ea661 100644 --- a/LichessClientTG_bot/bot.py +++ b/LichessClientTG_bot/bot.py @@ -21,6 +21,7 @@ from formatters import StatsFormatter from i18n import t from admin_bot import get_admin_bot, init_admin_bot from message_counters import MessageCounters +import time # Configure logging logging.basicConfig( @@ -624,6 +625,32 @@ class LichessBot: """Week command""" await self.get_stats(update, context, "week") + async def last_year_or_1000games(self, update: Update, context: ContextTypes.DEFAULT_TYPE): + """Get last year stats or last 1000 rated games, whichever limits first""" + user_id = update.effective_user.id + active_gamer = self.db.get_user_active_gamer(user_id) + lang = self.get_user_language_from_update(update) + if not active_gamer: + await update.message.reply_text( + t('no_active_gamer', lang) + ) + return + username = active_gamer['username'] + now_ms = int(time.time() * 1000) + year_ms = 365 * 24 * 3600 * 1000 + since_ms = now_ms - year_ms + try: + data = await self.lichess_api.get_games_period(username, since_ms, now_ms, rated_only=True) + if not data: + await update.message.reply_text(t('no_data', lang)) + return + text = StatsFormatter.format_last_year_or_1000(data, username, lang) + await update.message.reply_text(text) + self.counters.increment('last_year_1000') + except Exception as e: + logger.error(f"/lastYear_or_1000games error: {e}") + await update.message.reply_text(f"Error: {e}") + async def setperiod(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """Set period command""" user_id = update.effective_user.id @@ -906,6 +933,7 @@ class LichessBot: application.add_handler(CommandHandler("today", self.today)) application.add_handler(CommandHandler("yesterday", self.yesterday)) application.add_handler(CommandHandler("week", self.week)) + application.add_handler(CommandHandler("lastYear_or_1000games", self.last_year_or_1000games)) application.add_handler(CommandHandler("setperiod", self.setperiod)) application.add_handler(CommandHandler("lang", self.check_language)) application.add_handler(CommandHandler("resetlang", self.reset_language)) diff --git a/LichessClientTG_bot/formatters.py b/LichessClientTG_bot/formatters.py index b0abbcd..16c2039 100644 --- a/LichessClientTG_bot/formatters.py +++ b/LichessClientTG_bot/formatters.py @@ -169,3 +169,64 @@ class StatsFormatter: result += t('no_activity', lang) return result.rstrip() + + @staticmethod + def format_last_year_or_1000(data: Dict[str, Any], username: str, lang: str = 'en') -> str: + """ + Format response for last year or last 1000 games. + Expects GamesOfPeriodResponse-like payload. + """ + if not data: + return "📭 No data" + games_count = data.get('games_count', 0) + period_start = data.get('period_start') + period_end = data.get('period_end') + stats = (data.get('data') or {}) + # Title and subheader + if games_count >= 1000: + header = f"📈 {username}: last 1000 rated games" + # Use period_start as earliest known bound (server does not expose earliest game timestamp) + if isinstance(period_start, int): + earliest = datetime.fromtimestamp(period_start).strftime("%d.%m.%Y") + header += f"\nНачало этих 1000 партий: {earliest}" + else: + header = f"📈 {username}: last year (rated), games: {games_count}" + if isinstance(period_start, int) and isinstance(period_end, int): + start_str = datetime.fromtimestamp(period_start).strftime("%d.%m.%Y") + end_str = datetime.fromtimestamp(period_end).strftime("%d.%m.%Y") + header += f"\nПериод: {start_str}–{end_str}" + # Body per mode + lines = [] + for mode in ["bullet", "blitz", "rapid", "classical", "correspondence"]: + mode_stats = stats.get(mode) + if not mode_stats: + continue + games_played = mode_stats.get('games_played', 0) + if games_played == 0: + continue + emoji = StatsFormatter._get_game_type_emoji(mode) + wins = mode_stats.get('wins', 0) + losses = mode_stats.get('losses', 0) + draws = mode_stats.get('draws', 0) + rating_change = mode_stats.get('rating_change', 0) + rating_change_str = StatsFormatter._format_rating_change(rating_change) + rating = mode_stats.get('rating') + rating_str = rating if rating is not None else "—" + lines.append( + f"{emoji} {mode.title()}: {games_played} Δ {rating_change_str} R {rating_str} (+{wins} -{losses} ={draws})" + ) + # Total + total = stats.get('total') or {} + total_line = "" + if total: + tg = total.get('games_played', 0) + tw = total.get('wins', 0) + tl = total.get('losses', 0) + td = total.get('draws', 0) + trc = total.get('rating_change', 0) + trc_str = StatsFormatter._format_rating_change(trc) + tr = total.get('rating') + tr_str = tr if tr is not None else "—" + total_line = f"\nИтого: {tg} Δ {trc_str} R {tr_str} (+{tw} -{tl} ={td})" + body = "\n".join(lines) + total_line + return f"{header}\n{body}".rstrip() diff --git a/LichessClientTG_bot/lichess_api.py b/LichessClientTG_bot/lichess_api.py index d8f0056..e11d78a 100644 --- a/LichessClientTG_bot/lichess_api.py +++ b/LichessClientTG_bot/lichess_api.py @@ -77,11 +77,13 @@ class LichessAPI: logger.error(f"Error getting week stats: {e}") return None - async def get_games_period(self, username: str, since: int, until: int) -> Optional[Dict[str, Any]]: + async def get_games_period(self, username: str, since: int, until: int, rated_only: Optional[bool] = None) -> Optional[Dict[str, Any]]: """Get games for a specific period""" try: url = f"{self.stats_base_url}/games/{username}/period" params = {"since": since, "until": until} + if rated_only is not None: + params["rated_only"] = "true" if rated_only else "false" logger.info(f"🔍 LichessAPI.get_games_period: URL={url}, params={params}") async with aiohttp.ClientSession() as session: