diff options
| -rw-r--r-- | bot/exts/christmas/advent_of_code/_cog.py | 32 | ||||
| -rw-r--r-- | bot/exts/christmas/advent_of_code/_helpers.py | 59 | 
2 files changed, 60 insertions, 31 deletions
| diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 2a1a776b..57043454 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -1,7 +1,6 @@  import asyncio  import json  import logging -import math  from datetime import datetime, timedelta  from pathlib import Path @@ -19,38 +18,9 @@ log = logging.getLogger(__name__)  AOC_REQUEST_HEADER = {"user-agent": "PythonDiscord AoC Event Bot"} -COUNTDOWN_STEP = 60 * 5 -  AOC_WHITELIST = WHITELISTED_CHANNELS + (Channels.advent_of_code,) -async def countdown_status(bot: commands.Bot) -> None: -    """Set the playing status of the bot to the minutes & hours left until the next day's challenge.""" -    while _helpers.is_in_advent(): -        _, time_left = _helpers.time_left_to_aoc_midnight() - -        aligned_seconds = int(math.ceil(time_left.seconds / COUNTDOWN_STEP)) * COUNTDOWN_STEP -        hours, minutes = aligned_seconds // 3600, aligned_seconds // 60 % 60 - -        if aligned_seconds == 0: -            playing = "right now!" -        elif aligned_seconds == COUNTDOWN_STEP: -            playing = f"in less than {minutes} minutes" -        elif hours == 0: -            playing = f"in {minutes} minutes" -        elif hours == 23: -            playing = f"since {60 - minutes} minutes ago" -        else: -            playing = f"in {hours} hours and {minutes} minutes" - -        # Status will look like "Playing in 5 hours and 30 minutes" -        await bot.change_presence(activity=discord.Game(playing)) - -        # Sleep until next aligned time or a full step if already aligned -        delay = time_left.seconds % COUNTDOWN_STEP or COUNTDOWN_STEP -        await asyncio.sleep(delay) - -  async def day_countdown(bot: commands.Bot) -> None:      """      Calculate the number of seconds left until the next day of Advent. @@ -124,7 +94,7 @@ class AdventOfCode(commands.Cog):          countdown_coro = day_countdown(self.bot)          self.countdown_task = self.bot.loop.create_task(countdown_coro) -        status_coro = countdown_status(self.bot) +        status_coro = _helpers.countdown_status(self.bot)          self.status_task = self.bot.loop.create_task(status_coro)      @commands.group(name="adventofcode", aliases=("aoc",)) diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index 145fa30a..57ad001a 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -3,6 +3,7 @@ import collections  import datetime  import json  import logging +import math  import operator  import typing  from typing import Tuple @@ -11,6 +12,7 @@ import aiohttp  import discord  import pytz +from bot.bot import Bot  from bot.constants import AdventOfCode, Colours  from bot.exts.christmas.advent_of_code import _caches @@ -48,6 +50,9 @@ AOC_EMBED_THUMBNAIL = (  # Create an easy constant for the EST timezone  EST = pytz.timezone("EST") +# Step size for the challenge countdown status +COUNTDOWN_STEP = 60 * 5 +  # Create namedtuple that combines a participant's name and their completion  # time for a specific star. We're going to use this later to order the results  # for each star to compute the rank score. @@ -384,3 +389,57 @@ async def wait_for_advent_of_code(*, hours_before: int = 1) -> None:      delta = target - now      await asyncio.sleep(delta.total_seconds()) + + +async def countdown_status(bot: Bot) -> None: +    """ +    Add the time until the next challenge is published to the bot's status. + +    This function sleeps until 2 hours before the event and exists one hour +    after the last challenge has been published. It will not start up again +    automatically for next year's event, as it will wait for the environment +    variable AOC_YEAR to be updated. + +    This ensures that the task will only start sleeping again once the next +    event approaches and we're making preparations for that event. +    """ +    log.debug("Initializing status countdown task.") +    # We wait until 2 hours before the event starts. Then we +    # set our first countdown status. +    await wait_for_advent_of_code(hours_before=2) + +    # Log that we're going to start with the countdown status. +    log.info("The Advent of Code has started or will start soon, starting countdown status.") + +    # Calculate when the task needs to stop running. To prevent the task from +    # 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 = datetime.datetime(AdventOfCode.year, 12, 25, 0, 0, 0, tzinfo=EST) +    end = last_challenge + datetime.timedelta(hours=1) + +    while datetime.datetime.now(EST) < end: +        _, time_left = time_left_to_aoc_midnight() + +        aligned_seconds = int(math.ceil(time_left.seconds / COUNTDOWN_STEP)) * COUNTDOWN_STEP +        hours, minutes = aligned_seconds // 3600, aligned_seconds // 60 % 60 + +        if aligned_seconds == 0: +            playing = "right now!" +        elif aligned_seconds == COUNTDOWN_STEP: +            playing = f"in less than {minutes} minutes" +        elif hours == 0: +            playing = f"in {minutes} minutes" +        elif hours == 23: +            playing = f"since {60 - minutes} minutes ago" +        else: +            playing = f"in {hours} hours and {minutes} minutes" + +        log.trace(f"Changing presence to {playing!r}") +        # Status will look like "Playing in 5 hours and 30 minutes" +        await bot.change_presence(activity=discord.Game(playing)) + +        # Sleep until next aligned time or a full step if already aligned +        delay = time_left.seconds % COUNTDOWN_STEP or COUNTDOWN_STEP +        log.trace(f"The countdown status task will sleep for {delay} seconds.") +        await asyncio.sleep(delay) | 
