diff options
| author | 2019-10-02 20:58:00 +0100 | |
|---|---|---|
| committer | 2019-10-02 20:58:00 +0100 | |
| commit | 1da22f8a9bd64f13883a4bc8011c9f5069b4dac9 (patch) | |
| tree | deaed1b2caf7f650f05e12613cdff5b8a12629c9 /bot/seasons/christmas/adventofcode.py | |
| parent | Removed unused json (diff) | |
| parent | Merge pull request #285 from Numerlor/hacktober-date-channel-fixes (diff) | |
Merge pull request #3 from python-discord/master
Update
Diffstat (limited to 'bot/seasons/christmas/adventofcode.py')
| -rw-r--r-- | bot/seasons/christmas/adventofcode.py | 66 |
1 files changed, 18 insertions, 48 deletions
diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index 440484b4..513c1020 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -14,6 +14,7 @@ from discord.ext import commands from pytz import timezone from bot.constants import AdventOfCode as AocConfig, Channels, Colours, Emojis, Tokens +from bot.decorators import override_in_channel log = logging.getLogger(__name__) @@ -26,14 +27,12 @@ COUNTDOWN_STEP = 60 * 5 def is_in_advent() -> bool: """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]: """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, @@ -47,9 +46,8 @@ def time_left_to_aoc_midnight() -> Tuple[datetime, timedelta]: return tomorrow, tomorrow - datetime.now(EST) -async def countdown_status(bot: commands.Bot): +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 is_in_advent(): _, time_left = time_left_to_aoc_midnight() @@ -75,14 +73,13 @@ async def countdown_status(bot: commands.Bot): await asyncio.sleep(delay) -async def day_countdown(bot: commands.Bot): +async def day_countdown(bot: commands.Bot) -> None: """ - Calculate the number of seconds left until the next day of advent. + 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() @@ -104,7 +101,7 @@ 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 @@ -129,9 +126,9 @@ class AdventOfCode(commands.Cog): self.status_task = asyncio.ensure_future(self.bot.loop.create_task(status_coro)) @commands.group(name="adventofcode", aliases=("aoc",), invoke_without_command=True) - async def adventofcode_group(self, ctx: commands.Context): + @override_in_channel() + async def adventofcode_group(self, ctx: commands.Context) -> None: """All of the Advent of Code commands.""" - await ctx.send_help(ctx.command) @adventofcode_group.command( @@ -139,9 +136,8 @@ class AdventOfCode(commands.Cog): aliases=("sub", "notifications", "notify", "notifs"), brief="Notifications for new days" ) - async def aoc_subscribe(self, ctx: commands.Context): + async def aoc_subscribe(self, ctx: commands.Context) -> None: """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" @@ -154,9 +150,8 @@ class AdventOfCode(commands.Cog): f"If you don't want them any more, run `{unsubscribe_command}` instead.") @adventofcode_group.command(name="unsubscribe", aliases=("unsub",), brief="Notifications for new days") - async def aoc_unsubscribe(self, ctx: commands.Context): + async def aoc_unsubscribe(self, ctx: commands.Context) -> None: """Remove the role for notifications about new days being ready.""" - role = ctx.guild.get_role(AocConfig.role_id) if role in ctx.author.roles: @@ -166,9 +161,8 @@ class AdventOfCode(commands.Cog): await ctx.send("Hey, you don't even get any notifications about new Advent of Code tasks currently anyway.") @adventofcode_group.command(name="countdown", aliases=("count", "c"), brief="Return time left until next day") - async def aoc_countdown(self, ctx: commands.Context): + async def aoc_countdown(self, ctx: commands.Context) -> None: """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) @@ -184,15 +178,13 @@ class AdventOfCode(commands.Cog): await ctx.send(f"There are {hours} hours and {minutes} minutes left until day {tomorrow.day}.") @adventofcode_group.command(name="about", aliases=("ab", "info"), brief="Learn about Advent of Code") - async def about_aoc(self, ctx: commands.Context): + 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) @adventofcode_group.command(name="join", aliases=("j",), brief="Learn how to join PyDis' private AoC leaderboard") - async def join_leaderboard(self, ctx: commands.Context): + async def join_leaderboard(self, ctx: commands.Context) -> None: """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") @@ -211,7 +203,7 @@ class AdventOfCode(commands.Cog): aliases=("board", "lb"), brief="Get a snapshot of the PyDis private AoC leaderboard", ) - async def aoc_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10): + async def aoc_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10) -> None: """ Pull the top number_of_people_to_display members from the PyDis leaderboard and post an embed. @@ -219,7 +211,6 @@ class AdventOfCode(commands.Cog): Advent of Code section of the bot constants. number_of_people_to_display values greater than this limit will default to this maximum and provide feedback to the user. """ - async with ctx.typing(): await self._check_leaderboard_cache(ctx) @@ -253,13 +244,12 @@ class AdventOfCode(commands.Cog): aliases=("dailystats", "ds"), brief="Get daily statistics for the PyDis private leaderboard" ) - async def private_leaderboard_daily_stats(self, ctx: commands.Context): + async def private_leaderboard_daily_stats(self, ctx: commands.Context) -> None: """ 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 """ - async with ctx.typing(): await self._check_leaderboard_cache(ctx) @@ -297,7 +287,7 @@ class AdventOfCode(commands.Cog): aliases=("globalboard", "gb"), brief="Get a snapshot of the global AoC leaderboard", ) - async def global_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10): + async def global_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10) -> None: """ Pull the top number_of_people_to_display members from the global AoC leaderboard and post an embed. @@ -305,7 +295,6 @@ class AdventOfCode(commands.Cog): Advent of Code section of the bot constants. number_of_people_to_display values greater than this limit will default to this maximum and provide feedback to the user. """ - async with ctx.typing(): await self._check_leaderboard_cache(ctx, global_board=True) @@ -330,13 +319,12 @@ class AdventOfCode(commands.Cog): embed=aoc_embed, ) - async def _check_leaderboard_cache(self, ctx, global_board: bool = False): + async def _check_leaderboard_cache(self, ctx: commands.Context, global_board: bool = False) -> None: """ 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 """ - # Toggle between global & private leaderboards if global_board: log.debug("Checking global leaderboard cache") @@ -371,7 +359,7 @@ class AdventOfCode(commands.Cog): ) async def _check_n_entries(self, ctx: commands.Context, number_of_people_to_display: int) -> int: - # Check for n > max_entries and n <= 0 + """Check for n > max_entries and n <= 0.""" max_entries = AocConfig.leaderboard_max_displayed_members author = ctx.message.author if not 0 <= number_of_people_to_display <= max_entries: @@ -390,7 +378,6 @@ class AdventOfCode(commands.Cog): 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") as f: embed_fields = json.load(f) @@ -403,9 +390,8 @@ class AdventOfCode(commands.Cog): return about_embed - async def _boardgetter(self, global_board: bool): + async def _boardgetter(self, global_board: bool) -> None: """Invoke the proper leaderboard getter based on the global_board boolean.""" - if global_board: self.cached_global_leaderboard = await AocGlobalLeaderboard.from_url() else: @@ -426,7 +412,6 @@ class AocMember: def __repr__(self): """Generate a user-friendly representation of the AocMember & their score.""" - return f"<{self.name} ({self.aoc_id}): {self.local_score}>" @classmethod @@ -440,7 +425,6 @@ class AocMember: Returns an AocMember object """ - return cls( name=injson["name"] if injson["name"] else "Anonymous User", aoc_id=int(injson["id"]), @@ -462,7 +446,6 @@ class AocMember: Returns a list of 25 lists, where each nested list contains a pair of booleans representing the code challenge completion status for that day """ - # Basic input validation if not isinstance(injson, dict): raise ValueError @@ -487,7 +470,6 @@ class AocMember: @staticmethod def _completions_from_starboard(starboard: list) -> tuple: """Return days completed, as a (1 star, 2 star) tuple, from starboard.""" - completions = [0, 0] for day in starboard: if day[0]: @@ -515,7 +497,6 @@ class AocPrivateLeaderboard: If n is not specified, default to the top 10 """ - return self.members[:n] def calculate_daily_completion(self) -> List[tuple]: @@ -525,7 +506,6 @@ class AocPrivateLeaderboard: Return a list of tuples for each day containing the number of users who completed each part of the challenge """ - daily_member_completions = [] for day in range(25): one_star_count = 0 @@ -550,7 +530,6 @@ class AocPrivateLeaderboard: If no year is input, year defaults to the current year """ - api_url = f"https://adventofcode.com/{year}/leaderboard/private/view/{leaderboard_id}.json" log.debug("Querying Advent of Code Private Leaderboard API") @@ -567,7 +546,6 @@ class AocPrivateLeaderboard: @classmethod def from_json(cls, injson: dict) -> "AocPrivateLeaderboard": """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"] ) @@ -575,7 +553,6 @@ class AocPrivateLeaderboard: @classmethod async def from_url(cls) -> "AocPrivateLeaderboard": """Helper wrapping of AocPrivateLeaderboard.json_from_url and AocPrivateLeaderboard.from_json.""" - api_json = await cls.json_from_url() return cls.from_json(api_json) @@ -586,7 +563,6 @@ class AocPrivateLeaderboard: Output list is sorted based on the AocMember.local_score """ - members = [AocMember.member_from_json(injson[member]) for member in injson] members.sort(key=lambda x: x.local_score, reverse=True) @@ -599,7 +575,6 @@ class AocPrivateLeaderboard: Returns a string to be used as the content of the bot's leaderboard response """ - stargroup = f"{Emojis.star}, {Emojis.star*2}" header = f"{' '*3}{'Score'} {'Name':^25} {stargroup:^7}\n{'-'*44}" table = "" @@ -632,7 +607,6 @@ class AocGlobalLeaderboard: If n is not specified, default to the top 10 """ - return self.members[:n] @classmethod @@ -642,7 +616,6 @@ class AocGlobalLeaderboard: Because there is no API for this, web scraping needs to be used """ - aoc_url = f"https://adventofcode.com/{AocConfig.year}/leaderboard" async with aiohttp.ClientSession(headers=AOC_REQUEST_HEADER) as session: @@ -688,7 +661,6 @@ class AocGlobalLeaderboard: Returns a string to be used as the content of the bot's leaderboard response """ - header = f"{' '*4}{'Score'} {'Name':^25}\n{'-'*36}" table = "" for member in members_to_print: @@ -706,12 +678,10 @@ class AocGlobalLeaderboard: def _error_embed_helper(title: str, description: str) -> discord.Embed: """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") |