From e189a03a48e73ab24f6258dbd62b00b5a7bcb7d0 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sun, 9 Nov 2025 00:58:33 -0500 Subject: [PATCH 1/3] feat: always show the leaderboard even if the provided user isn't in the leaderboard closes GH-137 --- bot/constants.py | 1 + bot/exts/advent_of_code/_cog.py | 35 ++++++++++++++++++++----- bot/exts/advent_of_code/_helpers.py | 40 +++++++++++++++++++---------- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 1ee4cb8..b07573d 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -165,6 +165,7 @@ class _Emojis(EnvConfig, env_prefix="EMOJI_"): trashcan: str = "<:trashcan:637136429717389331>" star: str = "\u2B50" christmas_tree: str = "\U0001F384" + orange_diamond: str = "\U0001f536" team_tuple: str = "<:team_tuple:1224089419003334768>" team_list: str = "<:team_list:1224089544257962134>" team_dict: str = "<:team_dict:1224089495373353021>" diff --git a/bot/exts/advent_of_code/_cog.py b/bot/exts/advent_of_code/_cog.py index 1927eea..c3aa7fd 100644 --- a/bot/exts/advent_of_code/_cog.py +++ b/bot/exts/advent_of_code/_cog.py @@ -385,12 +385,23 @@ async def aoc_leaderboard(self, ctx: commands.Context, *, aoc_name: str | None = aoc_name = aoc_name[1:-1] # Check if an advent of code account is linked in the Redis Cache if aoc_name is not given + is_explict_name: bool = bool(aoc_name) if (aoc_cache_name := await _caches.account_links.get(ctx.author.id)) and aoc_name is None: aoc_name = aoc_cache_name + name_not_found: bool = False async with ctx.typing(): try: - leaderboard = await _helpers.fetch_leaderboard(self_placement_name=aoc_name) + try: + leaderboard = await _helpers.fetch_leaderboard( + self_placement_name=aoc_name, use_you_for_placement=not is_explict_name + ) + except _helpers.UserNotInLeaderboardError: + if is_explict_name: + raise commands.BadArgument("Sorry, the provided profile does not exist in this leaderboard.") + aoc_name = None + name_not_found = True + leaderboard = await _helpers.fetch_leaderboard() except _helpers.FetchingLeaderboardFailedError: await ctx.send(":x: Unable to fetch leaderboard!") return @@ -398,15 +409,25 @@ async def aoc_leaderboard(self, ctx: commands.Context, *, aoc_name: str | None = number_of_participants = leaderboard["number_of_participants"] top_count = min(AocConfig.leaderboard_displayed_members, number_of_participants) - self_placement_header = f" (and your personal stats compared to the top {top_count})" if aoc_name else "" - header = f"Here's our current top {top_count}{self_placement_header}! {Emojis.christmas_tree * 3}" + header = "" + if name_not_found: + header += ( + f"\n:x: Your linked Advent of Code account '{aoc_cache_name}' was not found in the leaderboard." + " Showing top leaderboard only. " + "Wait up to 30 minutes after joining the leaderboard for your stats to appear.\n\n" + ) + header += f"Here's our current top {top_count}" + if aoc_name: + header += ( + f" (and {f"the requested user ({Emojis.orange_diamond})" if is_explict_name else 'your'}" + f" personal stats compared to the top {top_count})" + ) + header += f"! {Emojis.christmas_tree * 3}" table = ( - "```\n" - f"{leaderboard['placement_leaderboard'] if aoc_name else leaderboard['top_leaderboard']}" - "\n```" + f"```\n{leaderboard['placement_leaderboard'] if aoc_name else leaderboard['top_leaderboard']} \n```" ) - info_embed = _helpers.get_summary_embed(leaderboard) + info_embed = _helpers.get_summary_embed(leaderboard) await ctx.send(content=f"{header}\n\n{table}", embed=info_embed) return diff --git a/bot/exts/advent_of_code/_helpers.py b/bot/exts/advent_of_code/_helpers.py index 5313d87..dfaba16 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -9,12 +9,11 @@ import aiohttp import arrow import discord -from discord.ext import commands from pydis_core.utils import logging, paste_service import bot from bot.bot import SirRobin -from bot.constants import AdventOfCode, Channels, Colours, Roles +from bot.constants import AdventOfCode, Channels, Colours, Emojis, Roles from bot.exts.advent_of_code import _caches log = logging.get_logger(__name__) @@ -79,6 +78,10 @@ class FetchingLeaderboardFailedError(Exception): """Raised when one or more leaderboards could not be fetched at all.""" +class UserNotInLeaderboardError(Exception): + """Raised when a user is not found in the requested leaderboard.""" + + def leaderboard_sorting_function(entry: tuple[str, dict]) -> tuple[int, int]: """ Provide a sorting value for our leaderboard. @@ -177,7 +180,12 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict: return {"daily_stats": daily_stats, "leaderboard": sorted_leaderboard, "per_day_and_star": per_day_star_stats} -def _format_leaderboard(leaderboard: dict[str, dict], self_placement_name: str | None = None) -> str: +def _format_leaderboard( + leaderboard: dict[str, dict], + self_placement_name: str | None = None, + *, + use_you_for_placement: bool = True, +) -> str: """ Format the leaderboard using the AOC_TABLE_TEMPLATE. @@ -188,12 +196,16 @@ def _format_leaderboard(leaderboard: dict[str, dict], self_placement_name: str | self_placement_exists = False for rank, data in enumerate(leaderboard.values(), start=1): if self_placement_name and data["name"].lower() == self_placement_name.lower(): + if use_you_for_placement: + name = f"(You) {data['name']}" + else: + name = f"({Emojis.orange_diamond}) {data['name']}" leaderboard_lines.insert( # Plus one to account for the user being one rank outside of the shown users rank if rank <= AdventOfCode.leaderboard_displayed_members + 1 else 1, AOC_TABLE_TEMPLATE.format( rank=rank, - name=f"(You) {data['name']}", + name=name, score=str(data["score"]), stars=f"({data['star_1']}, {data['star_2']})", ), @@ -210,12 +222,7 @@ def _format_leaderboard(leaderboard: dict[str, dict], self_placement_name: str | ) ) if self_placement_name and not self_placement_exists: - raise commands.BadArgument( - "Sorry, your profile does not exist in this leaderboard." - "\n\n" - "To join our leaderboard, run the command `/aoc join`." - " If you've joined recently, please wait up to 30 minutes for our leaderboard to refresh." - ) + raise UserNotInLeaderboardError return "\n".join(leaderboard_lines) @@ -317,7 +324,12 @@ def _get_top_leaderboard(full_leaderboard: str, *, include_self: bool = False) - @_caches.leaderboard_cache.atomic_transaction -async def fetch_leaderboard(invalidate_cache: bool = False, self_placement_name: str | None = None) -> dict: +async def fetch_leaderboard( + invalidate_cache: bool = False, + self_placement_name: str | None = None, + *, + use_you_for_placement: bool = True, +) -> dict: """ Get the current Python Discord combined leaderboard. @@ -369,13 +381,15 @@ async def fetch_leaderboard(invalidate_cache: bool = False, self_placement_name: json.loads(cached_leaderboard["placement_leaderboard"]) )["leaderboard"] full_formatted_leaderboard = _format_leaderboard( - formatted_placement_leaderboard, self_placement_name=self_placement_name + formatted_placement_leaderboard, + self_placement_name=self_placement_name, + use_you_for_placement=use_you_for_placement, ) user_placement = _get_user_placement(self_placement_name, formatted_placement_leaderboard) include_self = bool(user_placement and user_placement > AdventOfCode.leaderboard_displayed_members) cached_leaderboard["placement_leaderboard"] = _get_top_leaderboard( full_formatted_leaderboard, - include_self=include_self + include_self=include_self, ) return cached_leaderboard From bdb59539899248333853377a519d41e126f84e85 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 15 Nov 2025 15:24:29 -0500 Subject: [PATCH 2/3] style: fix styling issues - whitespace around ifs - update quotes, remove typehint - remove unnecessary type arg --- bot/exts/advent_of_code/_cog.py | 9 ++++++--- bot/exts/advent_of_code/_helpers.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bot/exts/advent_of_code/_cog.py b/bot/exts/advent_of_code/_cog.py index c3aa7fd..fd1f1c4 100644 --- a/bot/exts/advent_of_code/_cog.py +++ b/bot/exts/advent_of_code/_cog.py @@ -385,11 +385,11 @@ async def aoc_leaderboard(self, ctx: commands.Context, *, aoc_name: str | None = aoc_name = aoc_name[1:-1] # Check if an advent of code account is linked in the Redis Cache if aoc_name is not given - is_explict_name: bool = bool(aoc_name) + is_explict_name = bool(aoc_name) if (aoc_cache_name := await _caches.account_links.get(ctx.author.id)) and aoc_name is None: aoc_name = aoc_cache_name - name_not_found: bool = False + name_not_found = False async with ctx.typing(): try: try: @@ -407,8 +407,8 @@ async def aoc_leaderboard(self, ctx: commands.Context, *, aoc_name: str | None = return number_of_participants = leaderboard["number_of_participants"] - top_count = min(AocConfig.leaderboard_displayed_members, number_of_participants) + header = "" if name_not_found: header += ( @@ -416,12 +416,15 @@ async def aoc_leaderboard(self, ctx: commands.Context, *, aoc_name: str | None = " Showing top leaderboard only. " "Wait up to 30 minutes after joining the leaderboard for your stats to appear.\n\n" ) + header += f"Here's our current top {top_count}" + if aoc_name: header += ( f" (and {f"the requested user ({Emojis.orange_diamond})" if is_explict_name else 'your'}" f" personal stats compared to the top {top_count})" ) + header += f"! {Emojis.christmas_tree * 3}" table = ( f"```\n{leaderboard['placement_leaderboard'] if aoc_name else leaderboard['top_leaderboard']} \n```" diff --git a/bot/exts/advent_of_code/_helpers.py b/bot/exts/advent_of_code/_helpers.py index dfaba16..1e5112e 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -197,7 +197,7 @@ def _format_leaderboard( for rank, data in enumerate(leaderboard.values(), start=1): if self_placement_name and data["name"].lower() == self_placement_name.lower(): if use_you_for_placement: - name = f"(You) {data['name']}" + name = f"(You) {data["name"]}" else: name = f"({Emojis.orange_diamond}) {data['name']}" leaderboard_lines.insert( From eb5fb1536c1e31150e6b6f140a45e8edd51cebdf Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Sat, 29 Nov 2025 14:55:07 +0200 Subject: [PATCH 3/3] Improve messages --- bot/exts/advent_of_code/_cog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/advent_of_code/_cog.py b/bot/exts/advent_of_code/_cog.py index fd1f1c4..f0d8d1c 100644 --- a/bot/exts/advent_of_code/_cog.py +++ b/bot/exts/advent_of_code/_cog.py @@ -414,14 +414,14 @@ async def aoc_leaderboard(self, ctx: commands.Context, *, aoc_name: str | None = header += ( f"\n:x: Your linked Advent of Code account '{aoc_cache_name}' was not found in the leaderboard." " Showing top leaderboard only. " - "Wait up to 30 minutes after joining the leaderboard for your stats to appear.\n\n" + "Wait up to 30 minutes after joining the leaderboard (with `/aoc join`) for your stats to appear.\n\n" ) header += f"Here's our current top {top_count}" if aoc_name: header += ( - f" (and {f"the requested user ({Emojis.orange_diamond})" if is_explict_name else 'your'}" + f" (and {f"the requested user's ({Emojis.orange_diamond})" if is_explict_name else 'your'}" f" personal stats compared to the top {top_count})" )