diff options
author | 2019-09-10 22:34:30 -0600 | |
---|---|---|
committer | 2019-09-10 22:34:30 -0600 | |
commit | 8347c9ea91b5ec05cd57ddf6d40e88c9bac8ddd5 (patch) | |
tree | 82d0f8647b9fbb38e4462e0e26471da1124d506e | |
parent | Update contrib doc for allowing edits from maintainers (#268) (diff) | |
parent | Update bot/seasons/evergreen/snakes/utils.py (diff) |
Merge pull request #269 from python-discord/hacktoberfest-update
Hacktoberfest update & SaL Fix
-rw-r--r-- | bot/seasons/christmas/adventofcode.py | 2 | ||||
-rw-r--r-- | bot/seasons/easter/avatar_easterifier.py | 2 | ||||
-rw-r--r-- | bot/seasons/easter/bunny_name_generator.py | 10 | ||||
-rw-r--r-- | bot/seasons/easter/easter_riddle.py | 2 | ||||
-rw-r--r-- | bot/seasons/easter/egghead_quiz.py | 6 | ||||
-rw-r--r-- | bot/seasons/easter/traditions.py | 2 | ||||
-rw-r--r-- | bot/seasons/evergreen/8bitify.py | 6 | ||||
-rw-r--r-- | bot/seasons/evergreen/minesweeper.py | 18 | ||||
-rw-r--r-- | bot/seasons/evergreen/showprojects.py | 6 | ||||
-rw-r--r-- | bot/seasons/evergreen/snakes/utils.py | 76 | ||||
-rw-r--r-- | bot/seasons/evergreen/speedrun.py | 2 | ||||
-rw-r--r-- | bot/seasons/halloween/candy_collection.py | 11 | ||||
-rw-r--r-- | bot/seasons/halloween/hacktoberstats.py | 49 | ||||
-rw-r--r-- | bot/seasons/halloween/monstersurvey.py | 2 | ||||
-rw-r--r-- | bot/seasons/halloween/spookyrating.py | 2 | ||||
-rw-r--r-- | bot/utils/__init__.py | 2 | ||||
-rw-r--r-- | tox.ini | 5 |
17 files changed, 103 insertions, 100 deletions
diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index a9e72805..d2894ec4 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -359,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: diff --git a/bot/seasons/easter/avatar_easterifier.py b/bot/seasons/easter/avatar_easterifier.py index ad8b5473..98e15982 100644 --- a/bot/seasons/easter/avatar_easterifier.py +++ b/bot/seasons/easter/avatar_easterifier.py @@ -34,7 +34,7 @@ class AvatarEasterifier(commands.Cog): r1, g1, b1 = x def distance(point): - """Finds the difference between a pastel colour and the original pixel colour""" + """Finds the difference between a pastel colour and the original pixel colour.""" r2, g2, b2 = point return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) diff --git a/bot/seasons/easter/bunny_name_generator.py b/bot/seasons/easter/bunny_name_generator.py index 76d5c478..3ceaeb9e 100644 --- a/bot/seasons/easter/bunny_name_generator.py +++ b/bot/seasons/easter/bunny_name_generator.py @@ -28,8 +28,8 @@ class BunnyNameGenerator(commands.Cog): """ Finds vowels in the user's display name. - If the Discord name contains a vowel and the letter y, - it will match one or more of these patterns. + If the Discord name contains a vowel and the letter y, it will match one or more of these patterns. + Only the most recently matched pattern will apply the changes. """ expressions = [ @@ -46,7 +46,7 @@ class BunnyNameGenerator(commands.Cog): return new_name def append_name(self, displayname): - """Adds a suffix to the end of the Discord name""" + """Adds a suffix to the end of the Discord name.""" extensions = ['foot', 'ear', 'nose', 'tail'] suffix = random.choice(extensions) appended_name = displayname + suffix @@ -55,12 +55,12 @@ class BunnyNameGenerator(commands.Cog): @commands.command() async def bunnyname(self, ctx): - """Picks a random bunny name from a JSON file""" + """Picks a random bunny name from a JSON file.""" await ctx.send(random.choice(BUNNY_NAMES["names"])) @commands.command() async def bunnifyme(self, ctx): - """Gets your Discord username and bunnifies it""" + """Gets your Discord username and bunnifies it.""" username = ctx.message.author.display_name # If name contains spaces or other separators, get the individual words to randomly bunnify diff --git a/bot/seasons/easter/easter_riddle.py b/bot/seasons/easter/easter_riddle.py index 56555586..b612f8b9 100644 --- a/bot/seasons/easter/easter_riddle.py +++ b/bot/seasons/easter/easter_riddle.py @@ -84,7 +84,7 @@ class EasterRiddle(commands.Cog): @commands.Cog.listener() async def on_message(self, message): - """If a non-bot user enters a correct answer, their username gets added to self.winners""" + """If a non-bot user enters a correct answer, their username gets added to self.winners.""" if self.current_channel != message.channel: return diff --git a/bot/seasons/easter/egghead_quiz.py b/bot/seasons/easter/egghead_quiz.py index 3e0cc598..b3841993 100644 --- a/bot/seasons/easter/egghead_quiz.py +++ b/bot/seasons/easter/egghead_quiz.py @@ -37,7 +37,7 @@ class EggheadQuiz(commands.Cog): @commands.command(aliases=["eggheadquiz", "easterquiz"]) async def eggquiz(self, ctx): """ - Gives a random quiz question, waits 30 seconds and then outputs the answer + Gives a random quiz question, waits 30 seconds and then outputs the answer. Also informs of the percentages and votes of each option """ @@ -96,13 +96,13 @@ class EggheadQuiz(commands.Cog): @staticmethod async def already_reacted(message, user): - """Returns whether a given user has reacted more than once to a given message""" + """Returns whether a given user has reacted more than once to a given message.""" users = [u.id for reaction in [await r.users().flatten() for r in message.reactions] for u in reaction] return users.count(user.id) > 1 # Old reaction plus new reaction @commands.Cog.listener() async def on_reaction_add(self, reaction, user): - """Listener to listen specifically for reactions of quiz messages""" + """Listener to listen specifically for reactions of quiz messages.""" if user.bot: return if reaction.message.id not in self.quiz_messages: diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index f04b8828..b0bf04d7 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -19,7 +19,7 @@ class Traditions(commands.Cog): @commands.command(aliases=('eastercustoms',)) async def easter_tradition(self, ctx): - """Responds with a random tradition or custom""" + """Responds with a random tradition or custom.""" random_country = random.choice(list(traditions)) await ctx.send(f"{random_country}:\n{traditions[random_country]}") diff --git a/bot/seasons/evergreen/8bitify.py b/bot/seasons/evergreen/8bitify.py index 54db71db..60062fc1 100644 --- a/bot/seasons/evergreen/8bitify.py +++ b/bot/seasons/evergreen/8bitify.py @@ -13,17 +13,17 @@ class EightBitify(commands.Cog): @staticmethod def pixelate(image: Image) -> Image: - """Takes an image and pixelates it""" + """Takes an image and pixelates it.""" return image.resize((32, 32)).resize((1024, 1024)) @staticmethod def quantize(image: Image) -> Image: - """Reduces colour palette to 256 colours""" + """Reduces colour palette to 256 colours.""" return image.quantize(colors=32) @commands.command(name="8bitify") async def eightbit_command(self, ctx: commands.Context) -> None: - """Pixelates your avatar and changes the palette to an 8bit one""" + """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() avatar = Image.open(BytesIO(image_bytes)) diff --git a/bot/seasons/evergreen/minesweeper.py b/bot/seasons/evergreen/minesweeper.py index cb859ea9..3eee92ca 100644 --- a/bot/seasons/evergreen/minesweeper.py +++ b/bot/seasons/evergreen/minesweeper.py @@ -33,7 +33,7 @@ class CoordinateConverter(commands.Converter): """Converter for Coordinates.""" async def convert(self, ctx, coordinate: str) -> typing.Tuple[int, int]: - """Take in a coordinate string and turn it into x, y""" + """Take in a coordinate string and turn it into an (x, y) tuple.""" if not 2 <= len(coordinate) <= 3: raise commands.BadArgument('Invalid co-ordinate provided') @@ -81,7 +81,7 @@ class Minesweeper(commands.Cog): @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context): - """Commands for Playing Minesweeper""" + """Commands for Playing Minesweeper.""" await ctx.send_help(ctx.command) @staticmethod @@ -175,7 +175,7 @@ class Minesweeper(commands.Cog): @commands.dm_only() @minesweeper_group.command(name="flag") async def flag_command(self, ctx: commands.Context, *coordinates: CoordinateConverter) -> None: - """Place multiple flags on the board""" + """Place multiple flags on the board.""" board: GameBoard = self.games[ctx.author.id].revealed for x, y in coordinates: if board[y][x] == "hidden": @@ -185,14 +185,14 @@ class Minesweeper(commands.Cog): @staticmethod def reveal_bombs(revealed: GameBoard, board: GameBoard) -> None: - """Reveals all the bombs""" + """Reveals all the bombs.""" for y, row in enumerate(board): for x, cell in enumerate(row): if cell == "bomb": revealed[y][x] = cell async def lost(self, ctx: commands.Context) -> None: - """The player lost the game""" + """The player lost the game.""" game = self.games[ctx.author.id] self.reveal_bombs(game.revealed, game.board) await ctx.author.send(":fire: You lost! :fire:") @@ -200,7 +200,7 @@ class Minesweeper(commands.Cog): await game.chat_msg.channel.send(f":fire: {ctx.author.mention} just lost Minesweeper! :fire:") async def won(self, ctx: commands.Context) -> None: - """The player won the game""" + """The player won the game.""" game = self.games[ctx.author.id] await ctx.author.send(":tada: You won! :tada:") if game.activated_on_server: @@ -216,7 +216,7 @@ class Minesweeper(commands.Cog): self.reveal_zeros(revealed, board, x_, y_) async def check_if_won(self, ctx, revealed: GameBoard, board: GameBoard) -> bool: - """Checks if a player has won""" + """Checks if a player has won.""" if any( revealed[y][x] in ["hidden", "flag"] and board[y][x] != "bomb" for x in range(10) @@ -252,7 +252,7 @@ class Minesweeper(commands.Cog): @commands.dm_only() @minesweeper_group.command(name="reveal") async def reveal_command(self, ctx: commands.Context, *coordinates: CoordinateConverter) -> None: - """Reveal multiple cells""" + """Reveal multiple cells.""" game = self.games[ctx.author.id] revealed: GameBoard = game.revealed board: GameBoard = game.board @@ -268,7 +268,7 @@ class Minesweeper(commands.Cog): @minesweeper_group.command(name="end") async def end_command(self, ctx: commands.Context): - """End your current game""" + """End your current game.""" game = self.games[ctx.author.id] game.revealed = game.board await self.update_boards(ctx) diff --git a/bot/seasons/evergreen/showprojects.py b/bot/seasons/evergreen/showprojects.py index 37809b33..5dea78a5 100644 --- a/bot/seasons/evergreen/showprojects.py +++ b/bot/seasons/evergreen/showprojects.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) class ShowProjects(commands.Cog): - """Cog that reacts to posts in the #show-your-projects""" + """Cog that reacts to posts in the #show-your-projects.""" def __init__(self, bot): self.bot = bot @@ -16,7 +16,7 @@ class ShowProjects(commands.Cog): @commands.Cog.listener() async def on_message(self, message): - """Adds reactions to posts in #show-your-projects""" + """Adds reactions to posts in #show-your-projects.""" reactions = ["\U0001f44d", "\U00002764", "\U0001f440", "\U0001f389", "\U0001f680", "\U00002b50", "\U0001f6a9"] if (message.channel.id == Channels.show_your_projects and message.author.bot is False @@ -28,6 +28,6 @@ class ShowProjects(commands.Cog): def setup(bot): - """Show Projects Reaction Cog""" + """Show Projects Reaction Cog.""" bot.add_cog(ShowProjects(bot)) log.info("ShowProjects cog loaded") diff --git a/bot/seasons/evergreen/snakes/utils.py b/bot/seasons/evergreen/snakes/utils.py index e8d2ee44..b1d5048a 100644 --- a/bot/seasons/evergreen/snakes/utils.py +++ b/bot/seasons/evergreen/snakes/utils.py @@ -13,6 +13,8 @@ from PIL.ImageDraw import ImageDraw from discord import File, Member, Reaction from discord.ext.commands import Context +from bot.constants import Roles + SNAKE_RESOURCES = Path("bot/resources/snakes").absolute() h1 = r'''``` @@ -412,7 +414,6 @@ class SnakeAndLaddersGame: "**Snakes and Ladders**: A new game is about to start!", file=File( str(SNAKE_RESOURCES / "snakes_and_ladders" / "banner.jpg"), - # os.path.join("bot", "resources", "snakes", "snakes_and_ladders", "banner.jpg"), filename='Snakes and Ladders.jpg' ) ) @@ -435,8 +436,9 @@ class SnakeAndLaddersGame: if reaction.emoji == JOIN_EMOJI: await self.player_join(user) elif reaction.emoji == CANCEL_EMOJI: - if self.ctx.author == user: - await self.cancel_game(user) + if user == self.author or (self._is_moderator(user) and user not in self.players): + # Allow game author or non-playing moderation staff to cancel a waiting game + await self.cancel_game() return else: await self.player_leave(user) @@ -451,7 +453,7 @@ class SnakeAndLaddersGame: except asyncio.TimeoutError: log.debug("Snakes and Ladders timed out waiting for a reaction") - self.cancel_game(self.author) + await self.cancel_game() return # We're done, no reactions for the last 5 minutes async def _add_player(self, user: Member): @@ -488,20 +490,16 @@ class SnakeAndLaddersGame: delete_after=10 ) - async def player_leave(self, user: Member): + async def player_leave(self, user: Member) -> bool: """ Handle players leaving the game. - Leaving is prevented if the user initiated the game or if they weren't part of it in the - first place. + Leaving is prevented if the user wasn't part of the game. + + If the number of players reaches 0, the game is terminated. In this case, a sentinel boolean + is returned True to prevent a game from continuing after it's destroyed. """ - if user == self.author: - await self.channel.send( - user.mention + " You are the author, and cannot leave the game. Execute " - "`sal cancel` to cancel the game.", - delete_after=10 - ) - return + is_surrendered = False # Sentinel value to assist with stopping a surrendered game for p in self.players: if user == p: self.players.remove(p) @@ -512,17 +510,18 @@ class SnakeAndLaddersGame: delete_after=10 ) - if self.state != 'waiting' and len(self.players) == 1: + if self.state != 'waiting' and len(self.players) == 0: await self.channel.send("**Snakes and Ladders**: The game has been surrendered!") + is_surrendered = True self._destruct() - return - await self.channel.send(user.mention + " You are not in the match.", delete_after=10) - async def cancel_game(self, user: Member): - """Allow the game author to cancel the running game.""" - if not user == self.author: - await self.channel.send(user.mention + " Only the author of the game can cancel it.", delete_after=10) - return + return is_surrendered + else: + await self.channel.send(user.mention + " You are not in the match.", delete_after=10) + return is_surrendered + + async def cancel_game(self): + """Cancel the running game.""" await self.channel.send("**Snakes and Ladders**: Game has been canceled.") self._destruct() @@ -530,21 +529,16 @@ class SnakeAndLaddersGame: """ Allow the game author to begin the game. - The game cannot be started if there aren't enough players joined or if the game is in a - waiting state. + The game cannot be started if the game is in a waiting state. """ if not user == self.author: await self.channel.send(user.mention + " Only the author of the game can start it.", delete_after=10) return - if len(self.players) < 1: - await self.channel.send( - user.mention + " A minimum of 2 players is required to start the game.", - delete_after=10 - ) - return + if not self.state == 'waiting': await self.channel.send(user.mention + " The game cannot be started at this time.", delete_after=10) return + self.state = 'starting' player_list = ', '.join(user.mention for user in self.players) await self.channel.send("**Snakes and Ladders**: The game is starting!\nPlayers: " + player_list) @@ -565,8 +559,6 @@ class SnakeAndLaddersGame: self.state = 'roll' for user in self.players: self.round_has_rolled[user.id] = False - # board_img = Image.open(os.path.join( - # "bot", "resources", "snakes", "snakes_and_ladders", "board.jpg")) board_img = Image.open(str(SNAKE_RESOURCES / "snakes_and_ladders" / "board.jpg")) player_row_size = math.ceil(MAX_PLAYERS / 2) @@ -612,6 +604,7 @@ class SnakeAndLaddersGame: for emoji in GAME_SCREEN_EMOJI: await self.positions.add_reaction(emoji) + is_surrendered = False while True: try: reaction, user = await self.ctx.bot.wait_for( @@ -623,11 +616,12 @@ class SnakeAndLaddersGame: if reaction.emoji == ROLL_EMOJI: await self.player_roll(user) elif reaction.emoji == CANCEL_EMOJI: - if self.ctx.author == user: - await self.cancel_game(user) + if self._is_moderator(user) and user not in self.players: + # Only allow non-playing moderation staff to cancel a running game + await self.cancel_game() return else: - await self.player_leave(user) + is_surrendered = await self.player_leave(user) await self.positions.remove_reaction(reaction.emoji, user) @@ -636,11 +630,14 @@ class SnakeAndLaddersGame: except asyncio.TimeoutError: log.debug("Snakes and Ladders timed out waiting for a reaction") - await self.cancel_game(self.author) + await self.cancel_game() return # We're done, no reactions for the last 5 minutes # Round completed - await self._complete_round() + # Check to see if the game was surrendered before completing the round, without this + # sentinel, the game object would be deleted but the next round still posted into purgatory + if not is_surrendered: + await self._complete_round() async def player_roll(self, user: Member): """Handle the player's roll.""" @@ -708,3 +705,8 @@ class SnakeAndLaddersGame: if is_reversed: x_level = 9 - x_level return x_level, y_level + + @staticmethod + def _is_moderator(user: Member) -> bool: + """Return True if the user is a Moderator.""" + return any(Roles.moderator == role.id for role in user.roles) diff --git a/bot/seasons/evergreen/speedrun.py b/bot/seasons/evergreen/speedrun.py index f6a43a63..5e3d38a0 100644 --- a/bot/seasons/evergreen/speedrun.py +++ b/bot/seasons/evergreen/speedrun.py @@ -23,6 +23,6 @@ class Speedrun(commands.Cog): def setup(bot): - """Load the Speedrun cog""" + """Load the Speedrun cog.""" bot.add_cog(Speedrun(bot)) log.info("Speedrun cog loaded") diff --git a/bot/seasons/halloween/candy_collection.py b/bot/seasons/halloween/candy_collection.py index d35cbee5..a7232e0d 100644 --- a/bot/seasons/halloween/candy_collection.py +++ b/bot/seasons/halloween/candy_collection.py @@ -121,15 +121,16 @@ class CandyCollection(commands.Cog): async def ten_recent_msg(self): """Get the last 10 messages sent in the channel.""" ten_recent = [] - recent_msg = max(message.id for message - in self.bot._connection._messages - if message.channel.id == Channels.seasonalbot_chat) + recent_msg_id = max( + message.id for message in self.bot._connection._messages + if message.channel.id == Channels.seasonalbot_chat + ) channel = await self.hacktober_channel() - ten_recent.append(recent_msg.id) + ten_recent.append(recent_msg_id) for i in range(9): - o = discord.Object(id=recent_msg.id + i) + o = discord.Object(id=recent_msg_id + i) msg = await next(channel.history(limit=1, before=o)) ten_recent.append(msg.id) diff --git a/bot/seasons/halloween/hacktoberstats.py b/bot/seasons/halloween/hacktoberstats.py index b6b5a900..5687a5c7 100644 --- a/bot/seasons/halloween/hacktoberstats.py +++ b/bot/seasons/halloween/hacktoberstats.py @@ -1,10 +1,10 @@ import json import logging import re -import typing from collections import Counter from datetime import datetime from pathlib import Path +from typing import List, Tuple import aiohttp import discord @@ -12,6 +12,9 @@ from discord.ext import commands log = logging.getLogger(__name__) +CURRENT_YEAR = datetime.now().year # Used to construct GH API query +PRS_FOR_SHIRT = 4 # Minimum number of PRs before a shirt is awarded + class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" @@ -21,12 +24,8 @@ class HacktoberStats(commands.Cog): self.link_json = Path("bot/resources/github_links.json") self.linked_accounts = self.load_linked_users() - @commands.group( - name='hacktoberstats', - aliases=('hackstats',), - invoke_without_command=True - ) - async def hacktoberstats_group(self, ctx: commands.Context, github_username: str = None): + @commands.group(name="hacktoberstats", aliases=("hackstats",), invoke_without_command=True) + async def hacktoberstats_group(self, ctx: commands.Context, github_username: str = None) -> None: """ Display an embed for a user's Hacktoberfest contributions. @@ -43,8 +42,8 @@ class HacktoberStats(commands.Cog): else: msg = ( f"{author_mention}, you have not linked a GitHub account\n\n" - f"You can link your GitHub account using:\n```{ctx.prefix}stats link github_username```\n" - f"Or query GitHub stats directly using:\n```{ctx.prefix}stats github_username```" + f"You can link your GitHub account using:\n```{ctx.prefix}hackstats link github_username```\n" + f"Or query GitHub stats directly using:\n```{ctx.prefix}hackstats github_username```" ) await ctx.send(msg) return @@ -52,7 +51,7 @@ class HacktoberStats(commands.Cog): await self.get_stats(ctx, github_username) @hacktoberstats_group.command(name="link") - async def link_user(self, ctx: commands.Context, github_username: str = None): + async def link_user(self, ctx: commands.Context, github_username: str = None) -> None: """ Link the invoking user's Github github_username to their Discord ID. @@ -85,7 +84,7 @@ class HacktoberStats(commands.Cog): await ctx.send(f"{author_mention}, a GitHub username is required to link your account") @hacktoberstats_group.command(name="unlink") - async def unlink_user(self, ctx: commands.Context): + async def unlink_user(self, ctx: commands.Context) -> None: """Remove the invoking user's account link from the log.""" author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) @@ -99,7 +98,7 @@ class HacktoberStats(commands.Cog): self.save_linked_users() - def load_linked_users(self) -> typing.Dict: + def load_linked_users(self) -> dict: """ Load list of linked users from local JSON file. @@ -122,7 +121,7 @@ class HacktoberStats(commands.Cog): logging.info(f"Linked account log: '{self.link_json}' does not exist") return {} - def save_linked_users(self): + def save_linked_users(self) -> None: """ Save list of linked users to local JSON file. @@ -139,7 +138,7 @@ class HacktoberStats(commands.Cog): json.dump(self.linked_accounts, fID, default=str) logging.info(f"linked_accounts saved to '{self.link_json}'") - async def get_stats(self, ctx: commands.Context, github_username: str): + async def get_stats(self, ctx: commands.Context, github_username: str) -> None: """ Query GitHub's API for PRs created by a GitHub user during the month of October. @@ -158,18 +157,18 @@ class HacktoberStats(commands.Cog): else: await ctx.send(f"No October GitHub contributions found for '{github_username}'") - def build_embed(self, github_username: str, prs: typing.List[dict]) -> discord.Embed: + def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: """Return a stats embed built from github_username's PRs.""" logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") pr_stats = self._summarize_prs(prs) n = pr_stats['n_prs'] - if n >= 5: + if n >= PRS_FOR_SHIRT: shirtstr = f"**{github_username} has earned a tshirt!**" - elif n == 4: + elif n == PRS_FOR_SHIRT - 1: shirtstr = f"**{github_username} is 1 PR away from a tshirt!**" else: - shirtstr = f"**{github_username} is {5 - n} PRs away from a tshirt!**" + shirtstr = f"**{github_username} is {PRS_FOR_SHIRT - n} PRs away from a tshirt!**" stats_embed = discord.Embed( title=f"{github_username}'s Hacktoberfest", @@ -186,7 +185,7 @@ class HacktoberStats(commands.Cog): stats_embed.set_author( name="Hacktoberfest", url="https://hacktoberfest.digitalocean.com", - icon_url="https://hacktoberfest.digitalocean.com/assets/logo-hacktoberfest.png" + icon_url="https://hacktoberfest.digitalocean.com/pretty_logo.png" ) stats_embed.add_field( name="Top 5 Repositories:", @@ -197,7 +196,7 @@ class HacktoberStats(commands.Cog): return stats_embed @staticmethod - async def get_october_prs(github_username: str) -> typing.List[dict]: + async def get_october_prs(github_username: str) -> List[dict]: """ Query GitHub's API for PRs created during the month of October by github_username. @@ -219,7 +218,7 @@ class HacktoberStats(commands.Cog): not_label = "invalid" action_type = "pr" is_query = f"public+author:{github_username}" - date_range = "2018-10-01..2018-10-31" + date_range = f"{CURRENT_YEAR}-10-01..{CURRENT_YEAR}-10-31" per_page = "300" query_url = ( f"{base_url}" @@ -274,7 +273,7 @@ class HacktoberStats(commands.Cog): return re.findall(exp, in_url)[0] @staticmethod - def _summarize_prs(prs: typing.List[dict]) -> typing.Dict: + def _summarize_prs(prs: List[dict]) -> dict: """ Generate statistics from an input list of PR dictionaries, as output by get_october_prs. @@ -288,7 +287,7 @@ class HacktoberStats(commands.Cog): return {"n_prs": len(prs), "top5": Counter(contributed_repos).most_common(5)} @staticmethod - def _build_top5str(stats: typing.List[tuple]) -> str: + def _build_top5str(stats: List[tuple]) -> str: """ Build a string from the Top 5 contributions that is compatible with a discord.Embed field. @@ -316,7 +315,7 @@ class HacktoberStats(commands.Cog): return "contributions" @staticmethod - def _author_mention_from_context(ctx: commands.Context) -> typing.Tuple: + def _author_mention_from_context(ctx: commands.Context) -> Tuple: """Return stringified Message author ID and mentionable string from commands.Context.""" author_id = str(ctx.message.author.id) author_mention = ctx.message.author.mention @@ -324,7 +323,7 @@ class HacktoberStats(commands.Cog): return author_id, author_mention -def setup(bot): +def setup(bot): # Noqa """Hacktoberstats Cog load.""" bot.add_cog(HacktoberStats(bot)) log.info("HacktoberStats cog loaded") diff --git a/bot/seasons/halloween/monstersurvey.py b/bot/seasons/halloween/monstersurvey.py index 173ce8eb..6e71a007 100644 --- a/bot/seasons/halloween/monstersurvey.py +++ b/bot/seasons/halloween/monstersurvey.py @@ -60,7 +60,7 @@ class MonsterSurvey(Cog): @commands.group( name='monster', - aliases=('ms',) + aliases=('mon',) ) async def monster_group(self, ctx: Context): """The base voting command. If nothing is called, then it will return an embed.""" diff --git a/bot/seasons/halloween/spookyrating.py b/bot/seasons/halloween/spookyrating.py index 08c17a27..a436e39d 100644 --- a/bot/seasons/halloween/spookyrating.py +++ b/bot/seasons/halloween/spookyrating.py @@ -17,7 +17,7 @@ with Path("bot/resources/halloween/spooky_rating.json").open() as file: class SpookyRating(commands.Cog): - """A cog for calculating one's spooky rating""" + """A cog for calculating one's spooky rating.""" def __init__(self, bot): self.bot = bot diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index 3249a9cf..72a681a3 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -110,7 +110,7 @@ def replace_many( regex = re.compile(pattern, re.I if ignore_case else 0) def _repl(match): - """Returns replacement depending on `ignore_case` and `match_case`""" + """Returns replacement depending on `ignore_case` and `match_case`.""" word = match.group(0) replacement = replacements[word.lower() if ignore_case else word] @@ -1,16 +1,17 @@ [flake8] max-line-length=120 application_import_names=bot +docstring-convention=all ignore= P102,B311,W503,E226,S311, # Missing Docstrings - D100,D104,D107, + D100,D104,D105,D107, # Docstring Whitespace D203,D212,D214,D215, # Docstring Quotes D301,D302, # Docstring Content - D400,D401,D402,D405,D406,D407,D408,D409,D410,D411,D412,D413,D414 + D400,D401,D402,D404,D405,D406,D407,D408,D409,D410,D411,D412,D413,D414,D416,D417 exclude= __pycache__,.cache, venv,.venv, |