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..f0d8d1c 100644 --- a/bot/exts/advent_of_code/_cog.py +++ b/bot/exts/advent_of_code/_cog.py @@ -385,28 +385,52 @@ 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(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 = 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 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 (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's ({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..1e5112e 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