From a8e913820965f07d3559c5ba46ee26f04d45815a Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 8 Nov 2025 22:37:39 -0500 Subject: [PATCH 1/5] update aoc to support 12 stars this year rather than all 25 --- bot/exts/advent_of_code/_helpers.py | 25 +++++++++++++------ .../advent_of_code/views/dayandstarview.py | 4 ++- bot/exts/summer_aoc.py | 1 + 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/bot/exts/advent_of_code/_helpers.py b/bot/exts/advent_of_code/_helpers.py index 9d016ed..5f3b529 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -56,6 +56,16 @@ # for each star to compute the rank score. StarResult = collections.namedtuple("StarResult", "member_id completion_time") +# In 2025, AOC was changed to be held from Dec 1 to 12, with 12 days rather than 25. +# This implementation is done in such a way that any arbitary number of days can be supported. +def days_in_year(year:int | None = None) -> int: + """Return the number of days in the current Advent of Code year.""" + if year is None: + year = AdventOfCode.year + return 25 if year < 2025 else 12 + +DAYS_THIS_YEAR = days_in_year() + class UnexpectedRedirect(aiohttp.ClientError): """Raised when an unexpected redirect was detected.""" @@ -427,13 +437,15 @@ async def get_public_join_code(author: discord.Member) -> str | None: def is_in_advent() -> bool: """ - Check if we're currently on an Advent of Code day, excluding 25 December. + Check if we're currently on an Advent of Code day, excluding the final day of AOC for that year. This helper function is used to check whether or not a feature that prepares something for the next Advent of Code challenge should run. As the puzzle - published on the 25th is the last puzzle, this check excludes that date. + published on the final day is the last puzzle, this check excludes that date. """ - return arrow.now(EST).day in range(1, 25) and arrow.now(EST).month == 12 + # Advent of Code always begins on December 1st, and runs for one month + now = arrow.now(EST) + return now.month == 12 and now.day in range(1, DAYS_THIS_YEAR) def time_left_to_est_midnight() -> tuple[datetime.datetime, datetime.timedelta]: @@ -471,7 +483,7 @@ async def countdown_status(bot: SirRobin) -> None: # sleeping for the entire year, it will only wait in the currently # configured year. This means that the task will only start hibernating once # we start preparing the next event by changing environment variables. - last_challenge = arrow.get(datetime.datetime(AdventOfCode.year, 12, 25, tzinfo=datetime.UTC), EST) + last_challenge = arrow.get(datetime.datetime(AdventOfCode.year, 12, DAYS_THIS_YEAR, tzinfo=datetime.UTC), EST) end = last_challenge + datetime.timedelta(hours=1) while arrow.now(EST) < end: @@ -516,9 +528,8 @@ async def new_puzzle_notification(bot: SirRobin) -> None: log.error("Could not find the AoC role to announce the daily puzzle") return - # The last event day is 25 December, so we only have to schedule - # a reminder if the current day is before 25 December. - end = arrow.get(datetime.datetime(AdventOfCode.year, 12, 25, tzinfo=datetime.UTC), EST) + # Only schedule a reminder if the current day is before the final day December. + end = arrow.get(datetime.datetime(AdventOfCode.year, 12, DAYS_THIS_YEAR, tzinfo=datetime.UTC), EST) while arrow.now(EST) < end: log.trace("Started puzzle notification loop.") tomorrow, time_left = time_left_to_est_midnight() diff --git a/bot/exts/advent_of_code/views/dayandstarview.py b/bot/exts/advent_of_code/views/dayandstarview.py index a815126..e2c263b 100644 --- a/bot/exts/advent_of_code/views/dayandstarview.py +++ b/bot/exts/advent_of_code/views/dayandstarview.py @@ -2,6 +2,8 @@ import discord +from bot.exts.advent_of_code._helpers import DAYS_THIS_YEAR + AOC_DAY_AND_STAR_TEMPLATE = "{rank: >4} | {name:25.25} | {completion_time: >10}" @@ -52,7 +54,7 @@ async def interaction_check(self, interaction: discord.Interaction) -> bool: @discord.ui.select( placeholder="Day", - options=[discord.SelectOption(label=str(i)) for i in range(1, 26)], + options=[discord.SelectOption(label=str(i)) for i in range(1, DAYS_THIS_YEAR + 1)], custom_id="day_select", ) async def day_select(self, interaction: discord.Interaction, select: discord.ui.Select) -> None: diff --git a/bot/exts/summer_aoc.py b/bot/exts/summer_aoc.py index 856cf76..a86ea8a 100644 --- a/bot/exts/summer_aoc.py +++ b/bot/exts/summer_aoc.py @@ -15,6 +15,7 @@ log = logging.get_logger(__name__) +# TODO: Add support for different years in accordance with AOC changes AOC_URL = "https://adventofcode.com/{year}/day/{day}" LAST_DAY = 25 FIRST_YEAR = 2015 From f798be03fc149739e7f7e1fb5eff1f2a78c14684 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 8 Nov 2025 22:38:03 -0500 Subject: [PATCH 2/5] update summer to get days for year --- bot/exts/summer_aoc.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/bot/exts/summer_aoc.py b/bot/exts/summer_aoc.py index a86ea8a..878e149 100644 --- a/bot/exts/summer_aoc.py +++ b/bot/exts/summer_aoc.py @@ -11,13 +11,13 @@ from bot.bot import SirRobin from bot.constants import Bot, Channels, Roles +from bot.exts.advent_of_code._helpers import days_in_year from bot.utils.time import time_until log = logging.get_logger(__name__) # TODO: Add support for different years in accordance with AOC changes AOC_URL = "https://adventofcode.com/{year}/day/{day}" -LAST_DAY = 25 FIRST_YEAR = 2015 LAST_YEAR = arrow.get().year - 1 PUBLIC_NAME = "Revival of Code" @@ -64,8 +64,9 @@ def __init__(self, bot: SirRobin): self.wait_task: asyncio.Task | None = None self.loop_task: tasks.Loop | None = None - self.is_running = False + self.is_running: bool = False self.year: int | None = None + self.days_this_year: int | None = None self.current_day: int | None = None self.day_interval: int | None = None self.post_time = 0 @@ -146,6 +147,7 @@ async def start(self, ctx: commands.Context, year: int, day_interval: int, post_ self.is_running = True self.year = year + self.days_this_year = days_in_year(year) self.current_day = 1 self.day_interval = day_interval self.post_time = post_time @@ -175,8 +177,8 @@ async def force_day(self, ctx: commands.Context, day: int, now: Literal["now"] | await ctx.send(embed=embed) return - if not 1 <= day <= LAST_DAY: - raise commands.BadArgument(f"Start day must be between 1 and {LAST_DAY}, inclusive") + if not 1 <= day <= self.days_this_year: + raise commands.BadArgument(f"Start day must be between 1 and {self.days_this_year}, inclusive") log.info(f"Setting the current day of Summer AoC to {day}") await self.stop_event() @@ -188,7 +190,7 @@ async def force_day(self, ctx: commands.Context, day: int, now: Literal["now"] | embed = self.get_info_embed() if now: - if self.current_day > LAST_DAY: + if self.current_day > self.days_this_year: title = "Puzzle posted and event is now ending" else: title = "Puzzle posted and event is now running" @@ -198,7 +200,7 @@ async def force_day(self, ctx: commands.Context, day: int, now: Literal["now"] | embed.title = title embed.color = discord.Color.green() await ctx.send(embed=embed) - if self.current_day <= LAST_DAY: + if self.current_day <= self.days_this_year: await self.start_event() @summer_aoc_group.command(name="stop") @@ -291,7 +293,7 @@ async def stop_event(self) -> bool: async def post_puzzle(self) -> None: """Create a thread for the current day's puzzle.""" - if self.current_day > LAST_DAY: + if self.current_day > self.days_this_year: log.error("Attempted to post puzzle after last day, stopping event") await self.stop_event() return @@ -306,7 +308,7 @@ async def post_puzzle(self) -> None: self.current_day += 1 await self.save_event_state() - if self.current_day > LAST_DAY: + if self.current_day > self.days_this_year: await self.stop_event() def get_info_embed(self) -> discord.Embed: @@ -326,7 +328,7 @@ def get_info_embed(self) -> discord.Embed: def get_puzzle_embed(self) -> discord.Embed: """Generate an embed for the day's puzzle post.""" - if self.current_day == LAST_DAY: + if self.current_day == self.days_this_year: next_puzzle_text = LAST_PUZZLE_TEXT.format(timestamp=int(arrow.get(REAL_AOC_START).timestamp())) else: next_puzzle_text = NEXT_PUZZLE_TEXT.format( From e01f5db8a1f3050d7fb1c33c50d490209610d97e Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 8 Nov 2025 22:49:14 -0500 Subject: [PATCH 3/5] add lb fix in day counts --- bot/exts/advent_of_code/_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/advent_of_code/_helpers.py b/bot/exts/advent_of_code/_helpers.py index 5f3b529..75202d7 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -158,7 +158,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict: # Create summary stats for the stars completed for each day of the event. daily_stats = {} - for day in range(1, 26): + for day in range(1, DAYS_THIS_YEAR + 1): day = str(day) star_one = len(star_results.get((day, "1"), [])) star_two = len(star_results.get((day, "2"), [])) From 1486d58878eec549a98e0d378e5f9d04b101a694 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 8 Nov 2025 23:17:55 -0500 Subject: [PATCH 4/5] update completionist count --- bot/exts/advent_of_code/_cog.py | 6 +++--- bot/exts/advent_of_code/_helpers.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/advent_of_code/_cog.py b/bot/exts/advent_of_code/_cog.py index b601550..7f2957b 100644 --- a/bot/exts/advent_of_code/_cog.py +++ b/bot/exts/advent_of_code/_cog.py @@ -82,7 +82,7 @@ async def cog_load(self) -> None: @tasks.loop(minutes=10.0) async def completionist_task(self) -> None: """ - Give members who have completed all 50 AoC stars the completionist role. + Give members who have completed all AoC stars for this year the completionist role. Runs on a schedule, as defined in the task.loop decorator. """ @@ -107,8 +107,8 @@ async def completionist_task(self) -> None: placement_leaderboard = json.loads(leaderboard["placement_leaderboard"]) for member_aoc_info in placement_leaderboard.values(): - if member_aoc_info["stars"] != 50: - # Only give the role to people who have completed all 50 stars + if member_aoc_info["stars"] <= _helpers.STARS_THIS_YEAR: + # Only give the role to people who have completed all stars for this year continue aoc_name = member_aoc_info["name"] or f"Anonymous #{member_aoc_info['id']}" diff --git a/bot/exts/advent_of_code/_helpers.py b/bot/exts/advent_of_code/_helpers.py index 75202d7..1db2f26 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -65,7 +65,7 @@ def days_in_year(year:int | None = None) -> int: return 25 if year < 2025 else 12 DAYS_THIS_YEAR = days_in_year() - +STARS_THIS_YEAR = DAYS_THIS_YEAR class UnexpectedRedirect(aiohttp.ClientError): """Raised when an unexpected redirect was detected.""" From 149becf04fb6cc8c156ec04d4a2c4662deda64e1 Mon Sep 17 00:00:00 2001 From: z Date: Sun, 9 Nov 2025 18:01:47 -0500 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: lemonyte <49930425+lemonyte@users.noreply.github.com> --- bot/exts/advent_of_code/_cog.py | 2 +- bot/exts/advent_of_code/_helpers.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/advent_of_code/_cog.py b/bot/exts/advent_of_code/_cog.py index 7f2957b..f44f10c 100644 --- a/bot/exts/advent_of_code/_cog.py +++ b/bot/exts/advent_of_code/_cog.py @@ -107,7 +107,7 @@ async def completionist_task(self) -> None: placement_leaderboard = json.loads(leaderboard["placement_leaderboard"]) for member_aoc_info in placement_leaderboard.values(): - if member_aoc_info["stars"] <= _helpers.STARS_THIS_YEAR: + if member_aoc_info["stars"] < _helpers.STARS_THIS_YEAR: # Only give the role to people who have completed all stars for this year continue diff --git a/bot/exts/advent_of_code/_helpers.py b/bot/exts/advent_of_code/_helpers.py index 1db2f26..2375cb3 100644 --- a/bot/exts/advent_of_code/_helpers.py +++ b/bot/exts/advent_of_code/_helpers.py @@ -58,14 +58,14 @@ # In 2025, AOC was changed to be held from Dec 1 to 12, with 12 days rather than 25. # This implementation is done in such a way that any arbitary number of days can be supported. -def days_in_year(year:int | None = None) -> int: +def days_in_year(year: int | None = None) -> int: """Return the number of days in the current Advent of Code year.""" if year is None: year = AdventOfCode.year return 25 if year < 2025 else 12 DAYS_THIS_YEAR = days_in_year() -STARS_THIS_YEAR = DAYS_THIS_YEAR +STARS_THIS_YEAR = DAYS_THIS_YEAR * 2 class UnexpectedRedirect(aiohttp.ClientError): """Raised when an unexpected redirect was detected."""