aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Sebastiaan Zeeff <[email protected]>2020-12-01 22:22:29 +0100
committerGravatar Sebastiaan Zeeff <[email protected]>2020-12-01 22:22:29 +0100
commitcb418139b19aae38d384746ee0d7e149094c05b1 (patch)
tree8ef8859e91ddc14929f7c3551fe3d0e2715850d6
parentAdd function that allows tasks to sleep until AoC (diff)
Let status update task sleep until the AoC starts
I've refactored the rich presence countdown task by making it hibernate until 2 hours before the next Advent of Code starts if the task starts up before the event has started. This ensures that the task will run when the event starts and allows it to countdown to the first challenge. After the event for the configured Advent of Code year has finished, the task will terminate. This also means that the task will terminate immediately in the year following the currently configured Advent of Code; it will only start hibernating again once we configure the bot for the next event. No unnecessary, year-long hibernation.
-rw-r--r--bot/exts/christmas/advent_of_code/_cog.py32
-rw-r--r--bot/exts/christmas/advent_of_code/_helpers.py59
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)