aboutsummaryrefslogtreecommitdiffstats
path: root/bot/exts/christmas
diff options
context:
space:
mode:
Diffstat (limited to 'bot/exts/christmas')
-rw-r--r--bot/exts/christmas/advent_of_code/_cog.py44
-rw-r--r--bot/exts/christmas/advent_of_code/_helpers.py40
-rw-r--r--bot/exts/christmas/hanukkah_embed.py85
3 files changed, 88 insertions, 81 deletions
diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py
index 466edd48..3d61753b 100644
--- a/bot/exts/christmas/advent_of_code/_cog.py
+++ b/bot/exts/christmas/advent_of_code/_cog.py
@@ -3,6 +3,7 @@ import logging
from datetime import datetime, timedelta
from pathlib import Path
+import arrow
import discord
from discord.ext import commands
@@ -12,6 +13,7 @@ from bot.constants import (
)
from bot.exts.christmas.advent_of_code import _helpers
from bot.utils.decorators import InChannelCheckFailure, in_month, whitelist_override, with_role
+from bot.utils.extensions import invoke_help_command
log = logging.getLogger(__name__)
@@ -36,9 +38,6 @@ class AdventOfCode(commands.Cog):
self.about_aoc_filepath = Path("./bot/resources/advent_of_code/about.json")
self.cached_about_aoc = self._build_about_embed()
- self.countdown_task = None
- self.status_task = None
-
notification_coro = _helpers.new_puzzle_notification(self.bot)
self.notification_task = self.bot.loop.create_task(notification_coro)
self.notification_task.set_name("Daily AoC Notification")
@@ -54,7 +53,7 @@ class AdventOfCode(commands.Cog):
async def adventofcode_group(self, ctx: commands.Context) -> None:
"""All of the Advent of Code commands."""
if not ctx.invoked_subcommand:
- await ctx.send_help(ctx.command)
+ await invoke_help_command(ctx)
@adventofcode_group.command(
name="subscribe",
@@ -74,11 +73,15 @@ class AdventOfCode(commands.Cog):
if role not in ctx.author.roles:
await ctx.author.add_roles(role)
- await ctx.send("Okay! You have been __subscribed__ to notifications about new Advent of Code tasks. "
- f"You can run `{unsubscribe_command}` to disable them again for you.")
+ await ctx.send(
+ "Okay! You have been __subscribed__ to notifications about new Advent of Code tasks. "
+ f"You can run `{unsubscribe_command}` to disable them again for you."
+ )
else:
- await ctx.send("Hey, you already are receiving notifications about new Advent of Code tasks. "
- f"If you don't want them any more, run `{unsubscribe_command}` instead.")
+ await ctx.send(
+ "Hey, you already are receiving notifications about new Advent of Code tasks. "
+ f"If you don't want them any more, run `{unsubscribe_command}` instead."
+ )
@in_month(Month.DECEMBER)
@adventofcode_group.command(name="unsubscribe", aliases=("unsub",), brief="Notifications for new days")
@@ -98,11 +101,11 @@ class AdventOfCode(commands.Cog):
async def aoc_countdown(self, ctx: commands.Context) -> None:
"""Return time left until next day."""
if not _helpers.is_in_advent():
- datetime_now = datetime.now(_helpers.EST)
+ datetime_now = arrow.now(_helpers.EST)
# Calculate the delta to this & next year's December 1st to see which one is closest and not in the past
- this_year = datetime(datetime_now.year, 12, 1, tzinfo=_helpers.EST)
- next_year = datetime(datetime_now.year + 1, 12, 1, tzinfo=_helpers.EST)
+ this_year = arrow.get(datetime(datetime_now.year, 12, 1), _helpers.EST)
+ next_year = arrow.get(datetime(datetime_now.year + 1, 12, 1), _helpers.EST)
deltas = (dec_first - datetime_now for dec_first in (this_year, next_year))
delta = min(delta for delta in deltas if delta >= timedelta()) # timedelta() gives 0 duration delta
@@ -112,8 +115,10 @@ class AdventOfCode(commands.Cog):
else:
delta_str = f"{delta.days} days"
- await ctx.send(f"The Advent of Code event is not currently running. "
- f"The next event will start in {delta_str}.")
+ await ctx.send(
+ "The Advent of Code event is not currently running. "
+ f"The next event will start in {delta_str}."
+ )
return
tomorrow, time_left = _helpers.time_left_to_est_midnight()
@@ -126,7 +131,7 @@ class AdventOfCode(commands.Cog):
@whitelist_override(channels=AOC_WHITELIST)
async def about_aoc(self, ctx: commands.Context) -> None:
"""Respond with an explanation of all things Advent of Code."""
- await ctx.send("", embed=self.cached_about_aoc)
+ await ctx.send(embed=self.cached_about_aoc)
@adventofcode_group.command(name="join", aliases=("j",), brief="Learn how to join the leaderboard (via DM)")
@whitelist_override(channels=AOC_WHITELIST)
@@ -137,7 +142,7 @@ class AdventOfCode(commands.Cog):
await ctx.send(f"The Python Discord leaderboard for {current_year} is not yet available!")
return
- author = ctx.message.author
+ author = ctx.author
log.info(f"{author.name} ({author.id}) has requested a PyDis AoC leaderboard code")
if AocConfig.staff_leaderboard_id and any(r.id == Roles.helpers for r in author.roles):
@@ -173,6 +178,7 @@ class AdventOfCode(commands.Cog):
else:
await ctx.message.add_reaction(Emojis.envelope)
+ @in_month(Month.DECEMBER)
@adventofcode_group.command(
name="leaderboard",
aliases=("board", "lb"),
@@ -198,6 +204,7 @@ class AdventOfCode(commands.Cog):
await ctx.send(content=f"{header}\n\n{table}", embed=info_embed)
+ @in_month(Month.DECEMBER)
@adventofcode_group.command(
name="global",
aliases=("globalboard", "gb"),
@@ -244,7 +251,7 @@ class AdventOfCode(commands.Cog):
info_embed = _helpers.get_summary_embed(leaderboard)
await ctx.send(f"```\n{table}\n```", embed=info_embed)
- @with_role(Roles.admin, Roles.events_lead)
+ @with_role(Roles.admin)
@adventofcode_group.command(
name="refresh",
aliases=("fetch",),
@@ -268,13 +275,12 @@ class AdventOfCode(commands.Cog):
def cog_unload(self) -> None:
"""Cancel season-related tasks on cog unload."""
log.debug("Unloading the cog and canceling the background task.")
- self.countdown_task.cancel()
+ self.notification_task.cancel()
self.status_task.cancel()
def _build_about_embed(self) -> discord.Embed:
"""Build and return the informational "About AoC" embed from the resources file."""
- with self.about_aoc_filepath.open("r", encoding="utf8") as f:
- embed_fields = json.load(f)
+ embed_fields = json.loads(self.about_aoc_filepath.read_text("utf8"))
about_embed = discord.Embed(
title=self._base_url,
diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py
index b7adc895..e26a17ca 100644
--- a/bot/exts/christmas/advent_of_code/_helpers.py
+++ b/bot/exts/christmas/advent_of_code/_helpers.py
@@ -9,8 +9,8 @@ import typing
from typing import Tuple
import aiohttp
+import arrow
import discord
-import pytz
from bot.bot import Bot
from bot.constants import AdventOfCode, Channels, Colours
@@ -44,11 +44,11 @@ REQUIRED_CACHE_KEYS = (
AOC_EMBED_THUMBNAIL = (
"https://raw.githubusercontent.com/python-discord"
- "/branding/master/seasonal/christmas/server_icons/festive_256.gif"
+ "/branding/main/seasonal/christmas/server_icons/festive_256.gif"
)
# Create an easy constant for the EST timezone
-EST = pytz.timezone("EST")
+EST = "America/New_York"
# Step size for the challenge countdown status
COUNTDOWN_STEP = 60 * 5
@@ -67,7 +67,7 @@ class UnexpectedResponseStatus(aiohttp.ClientError):
"""Raised when an unexpected redirect was detected."""
-class FetchingLeaderboardFailed(Exception):
+class FetchingLeaderboardFailedError(Exception):
"""Raised when one or more leaderboards could not be fetched at all."""
@@ -108,7 +108,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict:
# star view. We need that per star view to compute rank scores per star.
for member in raw_leaderboard_data.values():
name = member["name"] if member["name"] else f"Anonymous #{member['id']}"
- member_id = member['id']
+ member_id = member["id"]
leaderboard[member_id] = {"name": name, "score": 0, "star_1": 0, "star_2": 0}
# Iterate over all days for this participant
@@ -119,7 +119,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict:
leaderboard[member_id][f"star_{star}"] += 1
# Record completion datetime for this participant for this day/star
- completion_time = datetime.datetime.fromtimestamp(int(data['get_star_ts']))
+ completion_time = datetime.datetime.fromtimestamp(int(data["get_star_ts"]))
star_results[(day, star)].append(
StarResult(member_id=member_id, completion_time=completion_time)
)
@@ -133,7 +133,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict:
if day in AdventOfCode.ignored_days:
continue
- sorted_result = sorted(results, key=operator.attrgetter('completion_time'))
+ sorted_result = sorted(results, key=operator.attrgetter("completion_time"))
for rank, star_result in enumerate(sorted_result):
leaderboard[star_result.member_id]["score"] += max_score - rank
@@ -210,7 +210,7 @@ async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]:
except UnexpectedRedirect:
if cookies["session"] == AdventOfCode.fallback_session:
log.error("It seems like the fallback cookie has expired!")
- raise FetchingLeaderboardFailed from None
+ raise FetchingLeaderboardFailedError from None
# If we're here, it means that the original session did not
# work. Let's fall back to the fallback session.
@@ -218,7 +218,7 @@ async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]:
continue
except aiohttp.ClientError:
# Don't retry, something unexpected is wrong and it may not be the session.
- raise FetchingLeaderboardFailed from None
+ raise FetchingLeaderboardFailedError from None
else:
# Get the participants and store their current count.
board_participants = raw_data["members"]
@@ -227,7 +227,7 @@ async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]:
break
else:
log.error(f"reached 'unreachable' state while fetching board `{leaderboard.id}`.")
- raise FetchingLeaderboardFailed
+ raise FetchingLeaderboardFailedError
log.info(f"Fetched leaderboard information for {len(participants)} participants")
return participants
@@ -307,7 +307,7 @@ async def fetch_leaderboard(invalidate_cache: bool = False) -> dict:
def get_summary_embed(leaderboard: dict) -> discord.Embed:
"""Get an embed with the current summary stats of the leaderboard."""
- leaderboard_url = leaderboard['full_leaderboard_url']
+ leaderboard_url = leaderboard["full_leaderboard_url"]
refresh_minutes = AdventOfCode.leaderboard_cache_expiry_seconds // 60
aoc_embed = discord.Embed(
@@ -395,13 +395,13 @@ def is_in_advent() -> bool:
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.
"""
- return datetime.datetime.now(EST).day in range(1, 25) and datetime.datetime.now(EST).month == 12
+ return arrow.now(EST).day in range(1, 25) and arrow.now(EST).month == 12
def time_left_to_est_midnight() -> Tuple[datetime.datetime, datetime.timedelta]:
"""Calculate the amount of time left until midnight EST/UTC-5."""
# Change all time properties back to 00:00
- todays_midnight = datetime.datetime.now(EST).replace(
+ todays_midnight = arrow.now(EST).replace(
microsecond=0,
second=0,
minute=0,
@@ -412,7 +412,7 @@ def time_left_to_est_midnight() -> Tuple[datetime.datetime, datetime.timedelta]:
tomorrow = todays_midnight + datetime.timedelta(days=1)
# Calculate the timedelta between the current time and midnight
- return tomorrow, tomorrow - datetime.datetime.now(EST)
+ return tomorrow, tomorrow - arrow.now(EST)
async def wait_for_advent_of_code(*, hours_before: int = 1) -> None:
@@ -430,9 +430,9 @@ async def wait_for_advent_of_code(*, hours_before: int = 1) -> None:
if we're already past the Advent of Code edition the bot is currently
configured for.
"""
- start = datetime.datetime(AdventOfCode.year, 12, 1, 0, 0, 0, tzinfo=EST)
+ start = arrow.get(datetime.datetime(AdventOfCode.year, 12, 1), EST)
target = start - datetime.timedelta(hours=hours_before)
- now = datetime.datetime.now(EST)
+ now = arrow.now(EST)
# If we've already reached or passed to target, we
# simply return immediately.
@@ -474,10 +474,10 @@ async def countdown_status(bot: Bot) -> 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 = datetime.datetime(AdventOfCode.year, 12, 25, 0, 0, 0, tzinfo=EST)
+ last_challenge = arrow.get(datetime.datetime(AdventOfCode.year, 12, 25), EST)
end = last_challenge + datetime.timedelta(hours=1)
- while datetime.datetime.now(EST) < end:
+ while arrow.now(EST) < end:
_, time_left = time_left_to_est_midnight()
aligned_seconds = int(math.ceil(time_left.seconds / COUNTDOWN_STEP)) * COUNTDOWN_STEP
@@ -534,8 +534,8 @@ async def new_puzzle_notification(bot: Bot) -> None:
# The last event day is 25 December, so we only have to schedule
# a reminder if the current day is before 25 December.
- end = datetime.datetime(AdventOfCode.year, 12, 25, tzinfo=EST)
- while datetime.datetime.now(EST) < end:
+ end = arrow.get(datetime.datetime(AdventOfCode.year, 12, 25), 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/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py
index 4f470a34..119f2446 100644
--- a/bot/exts/christmas/hanukkah_embed.py
+++ b/bot/exts/christmas/hanukkah_embed.py
@@ -5,19 +5,23 @@ from typing import List
from discord import Embed
from discord.ext import commands
+from bot.bot import Bot
from bot.constants import Colours, Month
from bot.utils.decorators import in_month
log = logging.getLogger(__name__)
+HEBCAL_URL = (
+ "https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&"
+ "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on"
+)
+
class HanukkahEmbed(commands.Cog):
"""A cog that returns information about Hanukkah festival."""
- def __init__(self, bot: commands.Bot):
+ def __init__(self, bot: Bot):
self.bot = bot
- self.url = ("https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&"
- "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on")
self.hanukkah_days = []
self.hanukkah_months = []
self.hanukkah_years = []
@@ -25,17 +29,17 @@ class HanukkahEmbed(commands.Cog):
async def get_hanukkah_dates(self) -> List[str]:
"""Gets the dates for hanukkah festival."""
hanukkah_dates = []
- async with self.bot.http_session.get(self.url) as response:
+ async with self.bot.http_session.get(HEBCAL_URL) as response:
json_data = await response.json()
- festivals = json_data['items']
+ festivals = json_data["items"]
for festival in festivals:
- if festival['title'].startswith('Chanukah'):
- date = festival['date']
+ if festival["title"].startswith("Chanukah"):
+ date = festival["date"]
hanukkah_dates.append(date)
return hanukkah_dates
@in_month(Month.DECEMBER)
- @commands.command(name='hanukkah', aliases=['chanukah'])
+ @commands.command(name="hanukkah", aliases=("chanukah",))
async def hanukkah_festival(self, ctx: commands.Context) -> None:
"""Tells you about the Hanukkah Festivaltime of festival, festival day, etc)."""
hanukkah_dates = await self.get_hanukkah_dates()
@@ -54,49 +58,46 @@ class HanukkahEmbed(commands.Cog):
day = str(today.day)
month = str(today.month)
year = str(today.year)
- embed = Embed()
- embed.title = 'Hanukkah'
- embed.colour = Colours.blue
+ embed = Embed(title="Hanukkah", colour=Colours.blue)
if day in self.hanukkah_days and month in self.hanukkah_months and year in self.hanukkah_years:
if int(day) == hanukkah_start_day:
now = datetime.datetime.utcnow()
- now = str(now)
- hours = int(now[11:13]) + 4 # using only hours
+ hours = now.hour + 4 # using only hours
hanukkah_start_hour = 18
if hours < hanukkah_start_hour:
- embed.description = (f"Hanukkah hasnt started yet, "
- f"it will start in about {hanukkah_start_hour-hours} hour/s.")
- return await ctx.send(embed=embed)
+ embed.description = (
+ "Hanukkah hasnt started yet, "
+ f"it will start in about {hanukkah_start_hour - hours} hour/s."
+ )
+ await ctx.send(embed=embed)
+ return
elif hours > hanukkah_start_hour:
- embed.description = (f'It is the starting day of Hanukkah ! '
- f'Its been {hours-hanukkah_start_hour} hours hanukkah started !')
- return await ctx.send(embed=embed)
+ embed.description = (
+ "It is the starting day of Hanukkah! "
+ f"Its been {hours - hanukkah_start_hour} hours hanukkah started!"
+ )
+ await ctx.send(embed=embed)
+ return
festival_day = self.hanukkah_days.index(day)
- number_suffixes = ['st', 'nd', 'rd', 'th']
- suffix = ''
- if int(festival_day) == 1:
- suffix = number_suffixes[0]
- if int(festival_day) == 2:
- suffix = number_suffixes[1]
- if int(festival_day) == 3:
- suffix = number_suffixes[2]
- if int(festival_day) > 3:
- suffix = number_suffixes[3]
- message = ''
- for _ in range(1, festival_day + 1):
- message += ':menorah:'
- embed.description = f'It is the {festival_day}{suffix} day of Hanukkah ! \n {message}'
+ number_suffixes = ["st", "nd", "rd", "th"]
+ suffix = number_suffixes[festival_day - 1 if festival_day <= 3 else 3]
+ message = ":menorah:" * festival_day
+ embed.description = f"It is the {festival_day}{suffix} day of Hanukkah!\n{message}"
await ctx.send(embed=embed)
else:
if today < hanukkah_start:
- festival_starting_month = hanukkah_start.strftime('%B')
- embed.description = (f"Hanukkah has not started yet. "
- f"Hanukkah will start at sundown on {hanukkah_start_day}th "
- f"of {festival_starting_month}.")
+ festival_starting_month = hanukkah_start.strftime("%B")
+ embed.description = (
+ f"Hanukkah has not started yet. "
+ f"Hanukkah will start at sundown on {hanukkah_start_day}th "
+ f"of {festival_starting_month}."
+ )
else:
- festival_end_month = hanukkah_end.strftime('%B')
- embed.description = (f"Looks like you missed Hanukkah !"
- f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}.")
+ festival_end_month = hanukkah_end.strftime("%B")
+ embed.description = (
+ f"Looks like you missed Hanukkah!"
+ f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}."
+ )
await ctx.send(embed=embed)
@@ -108,6 +109,6 @@ class HanukkahEmbed(commands.Cog):
self.hanukkah_years.append(date[0:4])
-def setup(bot: commands.Bot) -> None:
- """Cog load."""
+def setup(bot: Bot) -> None:
+ """Load the Hanukkah Embed Cog."""
bot.add_cog(HanukkahEmbed(bot))