Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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>"
Expand Down
40 changes: 32 additions & 8 deletions bot/exts/advent_of_code/_cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
40 changes: 27 additions & 13 deletions bot/exts/advent_of_code/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand All @@ -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']})",
),
Expand All @@ -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)


Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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

Expand Down