diff options
author | 2019-05-11 04:51:30 +1000 | |
---|---|---|
committer | 2019-05-11 04:51:30 +1000 | |
commit | 0e2c6a1b0daef5b569da7652801b5725bf1ed95b (patch) | |
tree | c3d57e50f2ba07cb2d9e7476d3b9441c1f671bd9 /bot/seasons/christmas/adventofcode.py | |
parent | not importing aiohttp now (diff) | |
parent | Merge pull request #198 from Suhail6inkling/constants_fix (diff) |
Merge branch 'master' into hanukkah_embed_iceman
Diffstat (limited to 'bot/seasons/christmas/adventofcode.py')
-rw-r--r-- | bot/seasons/christmas/adventofcode.py | 122 |
1 files changed, 53 insertions, 69 deletions
diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index 2995c3fd..32858673 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -13,7 +13,7 @@ from bs4 import BeautifulSoup from discord.ext import commands from pytz import timezone -from bot.constants import AdventOfCode as AocConfig, Colours, Emojis, Tokens +from bot.constants import AdventOfCode as AocConfig, Channels, Colours, Emojis, Tokens log = logging.getLogger(__name__) @@ -25,19 +25,15 @@ COUNTDOWN_STEP = 60 * 5 def is_in_advent() -> bool: - """ - Utility function to check if we are between December 1st - and December 25th. - """ + """Utility function to check if we are between December 1st and December 25th.""" + # Run the code from the 1st to the 24th return datetime.now(EST).day in range(1, 25) and datetime.now(EST).month == 12 def time_left_to_aoc_midnight() -> Tuple[datetime, timedelta]: - """ - This calculates the amount of time left until midnight in - UTC-5 (Advent of Code maintainer timezone). - """ + """Calculates the amount of time left until midnight in UTC-5 (Advent of Code maintainer timezone).""" + # Change all time properties back to 00:00 todays_midnight = datetime.now(EST).replace(microsecond=0, second=0, @@ -52,10 +48,8 @@ def time_left_to_aoc_midnight() -> Tuple[datetime, timedelta]: async def countdown_status(bot: commands.Bot): - """ - Every `COUNTDOWN_STEP` seconds set the playing status of the bot to - the number of minutes & hours left until the next day's release. - """ + """Set the playing status of the bot to the minutes & hours left until the next day's challenge.""" + while is_in_advent(): _, time_left = time_left_to_aoc_midnight() @@ -83,17 +77,18 @@ async def countdown_status(bot: commands.Bot): async def day_countdown(bot: commands.Bot): """ - Calculate the number of seconds left until the next day of advent. Once - we have calculated this we should then sleep that number and when the time - is reached ping the advent of code role notifying them that the new task is - ready. + Calculate the number of seconds left until the next day of advent. + + Once we have calculated this we should then sleep that number and when the time is reached, ping + the Advent of Code role notifying them that the new challenge is ready. """ + while is_in_advent(): tomorrow, time_left = time_left_to_aoc_midnight() await asyncio.sleep(time_left.seconds) - channel = bot.get_channel(AocConfig.channel_id) + channel = bot.get_channel(Channels.seasonalbot_chat) if not channel: log.error("Could not find the AoC channel to send notification in") @@ -109,9 +104,8 @@ async def day_countdown(bot: commands.Bot): class AdventOfCode(commands.Cog): - """ - Advent of Code festivities! Ho Ho Ho! - """ + """Advent of Code festivities! Ho Ho Ho.""" + def __init__(self, bot: commands.Bot): self.bot = bot @@ -136,9 +130,7 @@ class AdventOfCode(commands.Cog): @commands.group(name="adventofcode", aliases=("aoc",), invoke_without_command=True) async def adventofcode_group(self, ctx: commands.Context): - """ - All of the Advent of Code commands - """ + """All of the Advent of Code commands.""" await ctx.invoke(self.bot.get_command("help"), "adventofcode") @@ -148,9 +140,8 @@ class AdventOfCode(commands.Cog): brief="Notifications for new days" ) async def aoc_subscribe(self, ctx: commands.Context): - """ - Assign the role for notifications about new days being ready. - """ + """Assign the role for notifications about new days being ready.""" + role = ctx.guild.get_role(AocConfig.role_id) unsubscribe_command = f"{ctx.prefix}{ctx.command.root_parent} unsubscribe" @@ -164,9 +155,8 @@ class AdventOfCode(commands.Cog): @adventofcode_group.command(name="unsubscribe", aliases=("unsub",), brief="Notifications for new days") async def aoc_unsubscribe(self, ctx: commands.Context): - """ - Remove the role for notifications about new days being ready. - """ + """Remove the role for notifications about new days being ready.""" + role = ctx.guild.get_role(AocConfig.role_id) if role in ctx.author.roles: @@ -177,9 +167,8 @@ class AdventOfCode(commands.Cog): @adventofcode_group.command(name="countdown", aliases=("count", "c"), brief="Return time left until next day") async def aoc_countdown(self, ctx: commands.Context): - """ - Return time left until next day - """ + """Return time left until next day.""" + if not is_in_advent(): datetime_now = datetime.now(EST) december_first = datetime(datetime_now.year + 1, 12, 1, tzinfo=EST) @@ -196,17 +185,13 @@ class AdventOfCode(commands.Cog): @adventofcode_group.command(name="about", aliases=("ab", "info"), brief="Learn about Advent of Code") async def about_aoc(self, ctx: commands.Context): - """ - Respond with an explanation of all things Advent of Code - """ + """Respond with an explanation of all things Advent of Code.""" await ctx.send("", embed=self.cached_about_aoc) @adventofcode_group.command(name="join", aliases=("j",), brief="Learn how to join PyDis' private AoC leaderboard") async def join_leaderboard(self, ctx: commands.Context): - """ - DM the user the information for joining the PyDis AoC private leaderboard - """ + """DM the user the information for joining the PyDis AoC private leaderboard.""" author = ctx.message.author log.info(f"{author.name} ({author.id}) has requested the PyDis AoC leaderboard code") @@ -228,7 +213,7 @@ class AdventOfCode(commands.Cog): ) async def aoc_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10): """ - Pull the top number_of_people_to_display members from the PyDis leaderboard and post an embed + Pull the top number_of_people_to_display members from the PyDis leaderboard and post an embed. For readability, number_of_people_to_display defaults to 10. A maximum value is configured in the Advent of Code section of the bot constants. number_of_people_to_display values greater than this @@ -270,7 +255,7 @@ class AdventOfCode(commands.Cog): ) async def private_leaderboard_daily_stats(self, ctx: commands.Context): """ - Respond with a table of the daily completion statistics for the PyDis private leaderboard + Respond with a table of the daily completion statistics for the PyDis private leaderboard. Embed will display the total members and the number of users who have completed each day's puzzle """ @@ -314,7 +299,7 @@ class AdventOfCode(commands.Cog): ) async def global_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10): """ - Pull the top number_of_people_to_display members from the global AoC leaderboard and post an embed + Pull the top number_of_people_to_display members from the global AoC leaderboard and post an embed. For readability, number_of_people_to_display defaults to 10. A maximum value is configured in the Advent of Code section of the bot constants. number_of_people_to_display values greater than this @@ -347,7 +332,7 @@ class AdventOfCode(commands.Cog): async def _check_leaderboard_cache(self, ctx, global_board: bool = False): """ - Check age of current leaderboard & pull a new one if the board is too old + Check age of current leaderboard & pull a new one if the board is too old. global_board is a boolean to toggle between the global board and the Pydis private board """ @@ -404,9 +389,7 @@ class AdventOfCode(commands.Cog): return number_of_people_to_display def _build_about_embed(self) -> discord.Embed: - """ - Build and return the informational "About AoC" embed from the resources file - """ + """Build and return the informational "About AoC" embed from the resources file.""" with self.about_aoc_filepath.open("r") as f: embed_fields = json.load(f) @@ -421,9 +404,8 @@ class AdventOfCode(commands.Cog): return about_embed async def _boardgetter(self, global_board: bool): - """ - Invoke the proper leaderboard getter based on the global_board boolean - """ + """Invoke the proper leaderboard getter based on the global_board boolean.""" + if global_board: self.cached_global_leaderboard = await AocGlobalLeaderboard.from_url() else: @@ -431,6 +413,8 @@ class AdventOfCode(commands.Cog): class AocMember: + """Object representing the Advent of Code user.""" + def __init__(self, name: str, aoc_id: int, stars: int, starboard: list, local_score: int, global_score: int): self.name = name self.aoc_id = aoc_id @@ -441,12 +425,14 @@ class AocMember: self.completions = self._completions_from_starboard(self.starboard) def __repr__(self): + """Generate a user-friendly representation of the AocMember & their score.""" + return f"<{self.name} ({self.aoc_id}): {self.local_score}>" @classmethod def member_from_json(cls, injson: dict) -> "AocMember": """ - Generate an AocMember from AoC's private leaderboard API JSON + Generate an AocMember from AoC's private leaderboard API JSON. injson is expected to be the dict contained in: @@ -467,7 +453,7 @@ class AocMember: @staticmethod def _starboard_from_json(injson: dict) -> list: """ - Generate starboard from AoC's private leaderboard API JSON + Generate starboard from AoC's private leaderboard API JSON. injson is expected to be the dict contained in: @@ -500,9 +486,7 @@ class AocMember: @staticmethod def _completions_from_starboard(starboard: list) -> tuple: - """ - Return days completed, as a (1 star, 2 star) tuple, from starboard - """ + """Return days completed, as a (1 star, 2 star) tuple, from starboard.""" completions = [0, 0] for day in starboard: @@ -515,6 +499,8 @@ class AocMember: class AocPrivateLeaderboard: + """Object representing the Advent of Code private leaderboard.""" + def __init__(self, members: list, owner_id: int, event_year: int): self.members = members self._owner_id = owner_id @@ -534,7 +520,7 @@ class AocPrivateLeaderboard: def calculate_daily_completion(self) -> List[tuple]: """ - Calculate member completion rates by day + Calculate member completion rates by day. Return a list of tuples for each day containing the number of users who completed each part of the challenge @@ -560,7 +546,7 @@ class AocPrivateLeaderboard: leaderboard_id: int = AocConfig.leaderboard_id, year: int = AocConfig.year ) -> "AocPrivateLeaderboard": """ - Request the API JSON from Advent of Code for leaderboard_id for the specified year's event + Request the API JSON from Advent of Code for leaderboard_id for the specified year's event. If no year is input, year defaults to the current year """ @@ -580,9 +566,7 @@ class AocPrivateLeaderboard: @classmethod def from_json(cls, injson: dict) -> "AocPrivateLeaderboard": - """ - Generate an AocPrivateLeaderboard object from AoC's private leaderboard API JSON - """ + """Generate an AocPrivateLeaderboard object from AoC's private leaderboard API JSON.""" return cls( members=cls._sorted_members(injson["members"]), owner_id=injson["owner_id"], event_year=injson["event"] @@ -590,9 +574,7 @@ class AocPrivateLeaderboard: @classmethod async def from_url(cls) -> "AocPrivateLeaderboard": - """ - Helper wrapping of AocPrivateLeaderboard.json_from_url and AocPrivateLeaderboard.from_json - """ + """Helper wrapping of AocPrivateLeaderboard.json_from_url and AocPrivateLeaderboard.from_json.""" api_json = await cls.json_from_url() return cls.from_json(api_json) @@ -600,7 +582,7 @@ class AocPrivateLeaderboard: @staticmethod def _sorted_members(injson: dict) -> list: """ - Generate a sorted list of AocMember objects from AoC's private leaderboard API JSON + Generate a sorted list of AocMember objects from AoC's private leaderboard API JSON. Output list is sorted based on the AocMember.local_score """ @@ -613,7 +595,7 @@ class AocPrivateLeaderboard: @staticmethod def build_leaderboard_embed(members_to_print: List[AocMember]) -> str: """ - Build a text table from members_to_print, a list of AocMember objects + Build a text table from members_to_print, a list of AocMember objects. Returns a string to be used as the content of the bot's leaderboard response """ @@ -638,6 +620,8 @@ class AocPrivateLeaderboard: class AocGlobalLeaderboard: + """Object representing the Advent of Code global leaderboard.""" + def __init__(self, members: List[tuple]): self.members = members self.last_updated = datetime.utcnow() @@ -654,7 +638,7 @@ class AocGlobalLeaderboard: @classmethod async def from_url(cls) -> "AocGlobalLeaderboard": """ - Generate an list of tuples for the entries on AoC's global leaderboard + Generate an list of tuples for the entries on AoC's global leaderboard. Because there is no API for this, web scraping needs to be used """ @@ -700,7 +684,7 @@ class AocGlobalLeaderboard: @staticmethod def build_leaderboard_embed(members_to_print: List[tuple]) -> str: """ - Build a text table from members_to_print, a list of tuples + Build a text table from members_to_print, a list of tuples. Returns a string to be used as the content of the bot's leaderboard response """ @@ -721,13 +705,13 @@ class AocGlobalLeaderboard: def _error_embed_helper(title: str, description: str) -> discord.Embed: - """ - Return a red-colored Embed with the given title and description - """ + """Return a red-colored Embed with the given title and description.""" return discord.Embed(title=title, description=description, colour=discord.Colour.red()) def setup(bot: commands.Bot) -> None: + """Advent of Code Cog load.""" + bot.add_cog(AdventOfCode(bot)) log.info("AdventOfCode cog loaded") |