diff --git a/bot/exts/advent_of_code/_cog.py b/bot/exts/advent_of_code/_cog.py index b601550..4408005 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 = " (and your personal stats compared to the top 10)" 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 {'the requested user (\U0001f536)' if is_explict_name else 'your'}" + " personal stats compared to the top 10)" + ) + 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 9d016ed..6894d01 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -9,7 +9,6 @@ import aiohttp import arrow import discord -from discord.ext import commands from pydis_core.utils import logging, paste_service import bot @@ -69,6 +68,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. @@ -159,20 +162,29 @@ 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.""" leaderboard_lines = [HEADER] 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"(\U0001f536) {data['name']}" leaderboard_lines.insert( 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']})" - ) + stars=f"({data['star_1']}, {data['star_2']})", + ), ) self_placement_exists = True continue @@ -185,12 +197,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) @@ -285,7 +292,12 @@ def _get_top_leaderboard(full_leaderboard: str) -> str: @_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. @@ -337,7 +349,11 @@ async def fetch_leaderboard(invalidate_cache: bool = False, self_placement_name: json.loads(cached_leaderboard["placement_leaderboard"]) )["leaderboard"] cached_leaderboard["placement_leaderboard"] = _get_top_leaderboard( - _format_leaderboard(formatted_placement_leaderboard, self_placement_name=self_placement_name) + _format_leaderboard( + formatted_placement_leaderboard, + self_placement_name=self_placement_name, + use_you_for_placement=use_you_for_placement, + ) ) return cached_leaderboard