diff options
author | 2022-11-02 02:07:29 -0700 | |
---|---|---|
committer | 2022-11-02 02:07:29 -0700 | |
commit | 43a2acf5ee4eb354ce3dfaeef9504eee9b9b46b4 (patch) | |
tree | cbdfeb08f8d582aa98acec6a529f0fa3dcd7933c /bot/exts | |
parent | Appeased the formatter (diff) | |
parent | Merge pull request #1137 from DivyanshuBist/bug-issue1122-message-of-type-None (diff) |
Merge branch 'main' into main
Diffstat (limited to 'bot/exts')
96 files changed, 413 insertions, 353 deletions
diff --git a/bot/exts/avatar_modification/avatar_modify.py b/bot/exts/avatar_modification/avatar_modify.py index 3ee70cfd..6d1f26f6 100644 --- a/bot/exts/avatar_modification/avatar_modify.py +++ b/bot/exts/avatar_modification/avatar_modify.py @@ -14,7 +14,6 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Colours, Emojis from bot.exts.avatar_modification._effects import PfpEffects -from bot.utils.extensions import invoke_help_command from bot.utils.halloween import spookifications log = logging.getLogger(__name__) @@ -89,7 +88,7 @@ class AvatarModify(commands.Cog): async def avatar_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifying commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @avatar_modify.command(name="8bitify", root_aliases=("8bitify",)) async def eightbit_command(self, ctx: commands.Context) -> None: @@ -367,6 +366,6 @@ class AvatarModify(commands.Cog): await ctx.send(file=file, embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the AvatarModify cog.""" - bot.add_cog(AvatarModify(bot)) + await bot.add_cog(AvatarModify(bot)) diff --git a/bot/exts/core/error_handler.py b/bot/exts/core/error_handler.py index 4578f734..f62b3d4e 100644 --- a/bot/exts/core/error_handler.py +++ b/bot/exts/core/error_handler.py @@ -171,9 +171,8 @@ class CommandErrorHandler(commands.Cog): if not await similar_command.can_run(ctx): log.debug(log_msg) continue - except commands.errors.CommandError as cmd_error: + except commands.errors.CommandError: log.debug(log_msg) - await self.on_command_error(ctx, cmd_error) continue command_suggestions.append(similar_command_name) @@ -187,6 +186,6 @@ class CommandErrorHandler(commands.Cog): await ctx.send(embed=e, delete_after=RedirectOutput.delete_delay) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the ErrorHandler cog.""" - bot.add_cog(CommandErrorHandler(bot)) + await bot.add_cog(CommandErrorHandler(bot)) diff --git a/bot/exts/core/extensions.py b/bot/exts/core/extensions.py index d809d2b9..5b958c02 100644 --- a/bot/exts/core/extensions.py +++ b/bot/exts/core/extensions.py @@ -4,6 +4,7 @@ from collections.abc import Mapping from enum import Enum from typing import Optional +from botcore.utils._extensions import unqualify from discord import Colour, Embed from discord.ext import commands from discord.ext.commands import Context, group @@ -12,7 +13,6 @@ from bot import exts from bot.bot import Bot from bot.constants import Client, Emojis, MODERATION_ROLES, Roles from bot.utils.checks import with_role_check -from bot.utils.extensions import EXTENSIONS, invoke_help_command, unqualify from bot.utils.pagination import LinePaginator log = logging.getLogger(__name__) @@ -46,13 +46,13 @@ class Extension(commands.Converter): argument = argument.lower() - if argument in EXTENSIONS: + if argument in ctx.bot.all_extensions: return argument - elif (qualified_arg := f"{exts.__name__}.{argument}") in EXTENSIONS: + elif (qualified_arg := f"{exts.__name__}.{argument}") in ctx.bot.all_extensions: return qualified_arg matches = [] - for ext in EXTENSIONS: + for ext in ctx.bot.all_extensions: if argument == unqualify(ext): matches.append(ext) @@ -78,7 +78,7 @@ class Extensions(commands.Cog): @group(name="extensions", aliases=("ext", "exts", "c", "cogs"), invoke_without_command=True) async def extensions_group(self, ctx: Context) -> None: """Load, unload, reload, and list loaded extensions.""" - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @extensions_group.command(name="load", aliases=("l",)) async def load_command(self, ctx: Context, *extensions: Extension) -> None: @@ -88,13 +88,13 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all unloaded extensions will be loaded. """ # noqa: W605 if not extensions: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return if "*" in extensions or "**" in extensions: - extensions = set(EXTENSIONS) - set(self.bot.extensions.keys()) + extensions = set(self.bot.all_extensions) - set(self.bot.extensions.keys()) - msg = self.batch_manage(Action.LOAD, *extensions) + msg = await self.batch_manage(Action.LOAD, *extensions) await ctx.send(msg) @extensions_group.command(name="unload", aliases=("ul",)) @@ -105,7 +105,7 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all loaded extensions will be unloaded. """ # noqa: W605 if not extensions: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return blacklisted = "\n".join(UNLOAD_BLACKLIST & set(extensions)) @@ -116,7 +116,7 @@ class Extensions(commands.Cog): if "*" in extensions or "**" in extensions: extensions = set(self.bot.extensions.keys()) - UNLOAD_BLACKLIST - msg = self.batch_manage(Action.UNLOAD, *extensions) + msg = await self.batch_manage(Action.UNLOAD, *extensions) await ctx.send(msg) @@ -131,16 +131,16 @@ class Extensions(commands.Cog): If '\*\*' is given as the name, all extensions, including unloaded ones, will be reloaded. """ # noqa: W605 if not extensions: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return if "**" in extensions: - extensions = EXTENSIONS + extensions = self.bot.all_extensions elif "*" in extensions: extensions = set(self.bot.extensions.keys()) | set(extensions) extensions.remove("*") - msg = self.batch_manage(Action.RELOAD, *extensions) + msg = await self.batch_manage(Action.RELOAD, *extensions) await ctx.send(msg) @@ -175,7 +175,7 @@ class Extensions(commands.Cog): """Return a mapping of extension names and statuses to their categories.""" categories = {} - for ext in EXTENSIONS: + for ext in self.bot.all_extensions: if ext in self.bot.extensions: status = Emojis.status_online else: @@ -191,21 +191,21 @@ class Extensions(commands.Cog): return categories - def batch_manage(self, action: Action, *extensions: str) -> str: + async def batch_manage(self, action: Action, *extensions: str) -> str: """ Apply an action to multiple extensions and return a message with the results. If only one extension is given, it is deferred to `manage()`. """ if len(extensions) == 1: - msg, _ = self.manage(action, extensions[0]) + msg, _ = await self.manage(action, extensions[0]) return msg verb = action.name.lower() failures = {} for extension in extensions: - _, error = self.manage(action, extension) + _, error = await self.manage(action, extension) if error: failures[extension] = error @@ -220,17 +220,17 @@ class Extensions(commands.Cog): return msg - def manage(self, action: Action, ext: str) -> tuple[str, Optional[str]]: + async def manage(self, action: Action, ext: str) -> tuple[str, Optional[str]]: """Apply an action to an extension and return the status message and any error message.""" verb = action.name.lower() error_msg = None try: - action.value(self.bot, ext) + await action.value(self.bot, ext) except (commands.ExtensionAlreadyLoaded, commands.ExtensionNotLoaded): if action is Action.RELOAD: # When reloading, just load the extension if it was not loaded. - return self.manage(Action.LOAD, ext) + return await self.manage(Action.LOAD, ext) msg = f":x: Extension `{ext}` is already {verb}ed." log.debug(msg[4:]) @@ -261,6 +261,6 @@ class Extensions(commands.Cog): error.handled = True -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Extensions cog.""" - bot.add_cog(Extensions(bot)) + await bot.add_cog(Extensions(bot)) diff --git a/bot/exts/core/help.py b/bot/exts/core/help.py index eb7a9762..30deaff4 100644 --- a/bot/exts/core/help.py +++ b/bot/exts/core/help.py @@ -220,11 +220,11 @@ class HelpSession: async def prepare(self) -> None: """Sets up the help session pages, events, message and reactions.""" await self.build_pages() + await self.update_page() self._bot.add_listener(self.on_reaction_add) self._bot.add_listener(self.on_message_delete) - await self.update_page() self.add_reactions() def add_reactions(self) -> None: @@ -547,7 +547,7 @@ def unload(bot: Bot) -> None: bot.add_command(bot._old_help) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """ The setup for the help extension. @@ -562,7 +562,7 @@ def setup(bot: Bot) -> None: bot.remove_command("help") try: - bot.add_cog(Help()) + await bot.add_cog(Help()) except Exception: unload(bot) raise diff --git a/bot/exts/core/internal_eval/__init__.py b/bot/exts/core/internal_eval/__init__.py index 695fa74d..258f7fd8 100644 --- a/bot/exts/core/internal_eval/__init__.py +++ b/bot/exts/core/internal_eval/__init__.py @@ -1,10 +1,10 @@ from bot.bot import Bot -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Set up the Internal Eval extension.""" # Import the Cog at runtime to prevent side effects like defining # RedisCache instances too early. from ._internal_eval import InternalEval - bot.add_cog(InternalEval(bot)) + await bot.add_cog(InternalEval(bot)) diff --git a/bot/exts/core/internal_eval/_internal_eval.py b/bot/exts/core/internal_eval/_internal_eval.py index 190a15ec..2daf8ef9 100644 --- a/bot/exts/core/internal_eval/_internal_eval.py +++ b/bot/exts/core/internal_eval/_internal_eval.py @@ -9,7 +9,6 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Client, Roles from bot.utils.decorators import with_role -from bot.utils.extensions import invoke_help_command from ._helpers import EvalContext @@ -154,7 +153,7 @@ class InternalEval(commands.Cog): async def internal_group(self, ctx: commands.Context) -> None: """Internal commands. Top secret!""" if not ctx.invoked_subcommand: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @internal_group.command(name="eval", aliases=("e",)) @with_role(Roles.admins) diff --git a/bot/exts/core/ping.py b/bot/exts/core/ping.py index 6be78117..cb32398e 100644 --- a/bot/exts/core/ping.py +++ b/bot/exts/core/ping.py @@ -40,6 +40,6 @@ class Ping(commands.Cog): await ctx.send(f"I started up {uptime_string}.") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Ping cog.""" - bot.add_cog(Ping(bot)) + await bot.add_cog(Ping(bot)) diff --git a/bot/exts/core/source.py b/bot/exts/core/source.py index 2801be0f..f771eaca 100644 --- a/bot/exts/core/source.py +++ b/bot/exts/core/source.py @@ -82,6 +82,6 @@ class BotSource(commands.Cog): return embed -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the BotSource cog.""" - bot.add_cog(BotSource()) + await bot.add_cog(BotSource()) diff --git a/bot/exts/events/advent_of_code/__init__.py b/bot/exts/events/advent_of_code/__init__.py index 3c521168..33c3971a 100644 --- a/bot/exts/events/advent_of_code/__init__.py +++ b/bot/exts/events/advent_of_code/__init__.py @@ -1,10 +1,10 @@ from bot.bot import Bot -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Set up the Advent of Code extension.""" # Import the Cog at runtime to prevent side effects like defining # RedisCache instances too early. from ._cog import AdventOfCode - bot.add_cog(AdventOfCode(bot)) + await bot.add_cog(AdventOfCode(bot)) diff --git a/bot/exts/events/advent_of_code/_cog.py b/bot/exts/events/advent_of_code/_cog.py index 518841d4..49140a3f 100644 --- a/bot/exts/events/advent_of_code/_cog.py +++ b/bot/exts/events/advent_of_code/_cog.py @@ -18,7 +18,6 @@ from bot.exts.events.advent_of_code.views.dayandstarview import AoCDropdownView from bot.utils import members from bot.utils.decorators import InChannelCheckFailure, in_month, whitelist_override, with_role from bot.utils.exceptions import MovedCommandError -from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -71,7 +70,6 @@ class AdventOfCode(commands.Cog): Runs on a schedule, as defined in the task.loop decorator. """ - await self.bot.wait_until_guild_available() guild = self.bot.get_guild(Client.guild) completionist_role = guild.get_role(Roles.aoc_completionist) if completionist_role is None: @@ -87,7 +85,7 @@ class AdventOfCode(commands.Cog): try: leaderboard = await _helpers.fetch_leaderboard() except _helpers.FetchingLeaderboardFailedError: - await self.bot.send_log("Unable to fetch AoC leaderboard during role sync.") + await self.bot.log_to_dev_log("Unable to fetch AoC leaderboard during role sync.") return placement_leaderboard = json.loads(leaderboard["placement_leaderboard"]) @@ -122,7 +120,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 invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @with_role(Roles.admins) @adventofcode_group.command( diff --git a/bot/exts/events/advent_of_code/_helpers.py b/bot/exts/events/advent_of_code/_helpers.py index 6c004901..abd80b77 100644 --- a/bot/exts/events/advent_of_code/_helpers.py +++ b/bot/exts/events/advent_of_code/_helpers.py @@ -523,13 +523,6 @@ async def countdown_status(bot: Bot) -> None: # 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.") - # Trying to change status too early in the bot's startup sequence will fail - # the task because the websocket instance has not yet been created. Waiting - # for this event means that both the websocket instance has been initialized - # and that the connection to Discord is mature enough to change the presence - # of the bot. - await bot.wait_until_guild_available() - # 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 @@ -578,9 +571,6 @@ async def new_puzzle_notification(bot: Bot) -> None: log.info("The Advent of Code has started or will start soon, waking up notification task.") - # Ensure that the guild cache is loaded so we can get the Advent of Code - # channel and role. - await bot.wait_until_guild_available() aoc_channel = bot.get_channel(Channels.advent_of_code) aoc_role = aoc_channel.guild.get_role(AdventOfCode.role_id) diff --git a/bot/exts/events/advent_of_code/views/dayandstarview.py b/bot/exts/events/advent_of_code/views/dayandstarview.py index 5529c12b..f0ebc803 100644 --- a/bot/exts/events/advent_of_code/views/dayandstarview.py +++ b/bot/exts/events/advent_of_code/views/dayandstarview.py @@ -55,7 +55,7 @@ class AoCDropdownView(discord.ui.View): options=[discord.SelectOption(label=str(i)) for i in range(1, 26)], custom_id="day_select" ) - async def day_select(self, select: discord.ui.Select, interaction: discord.Interaction) -> None: + async def day_select(self, _: discord.Interaction, select: discord.ui.Select) -> None: """Dropdown to choose a Day of the AoC.""" self.day = select.values[0] @@ -64,12 +64,12 @@ class AoCDropdownView(discord.ui.View): options=[discord.SelectOption(label=str(i)) for i in range(1, 3)], custom_id="star_select" ) - async def star_select(self, select: discord.ui.Select, interaction: discord.Interaction) -> None: + async def star_select(self, _: discord.Interaction, select: discord.ui.Select) -> None: """Dropdown to choose either the first or the second star.""" self.star = select.values[0] @discord.ui.button(label="Fetch", style=discord.ButtonStyle.blurple) - async def fetch(self, button: discord.ui.Button, interaction: discord.Interaction) -> None: + async def fetch(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: """Button that fetches the statistics based on the dropdown values.""" if self.day == 0 or self.star == 0: await interaction.response.send_message( diff --git a/bot/exts/events/hacktoberfest/hacktober-issue-finder.py b/bot/exts/events/hacktoberfest/hacktober-issue-finder.py index 1774564b..aeffc8d7 100644 --- a/bot/exts/events/hacktoberfest/hacktober-issue-finder.py +++ b/bot/exts/events/hacktoberfest/hacktober-issue-finder.py @@ -100,8 +100,9 @@ class HacktoberIssues(commands.Cog): """Format the issue data into a embed.""" title = issue["title"] issue_url = issue["url"].replace("api.", "").replace("/repos/", "/") - # issues can have empty bodies, which in that case GitHub doesn't include the key in the API response - body = issue.get("body", "") + # Issues can have empty bodies, resulting in the value being a literal `null` (parsed as `None`). + # For this reason, we can't use the default arg of `dict.get`, and so instead use `or` logic. + body = issue.get("body") or "" labels = [label["name"] for label in issue["labels"]] embed = discord.Embed(title=title) @@ -113,6 +114,6 @@ class HacktoberIssues(commands.Cog): return embed -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the HacktoberIssue finder.""" - bot.add_cog(HacktoberIssues(bot)) + await bot.add_cog(HacktoberIssues(bot)) diff --git a/bot/exts/events/hacktoberfest/hacktoberstats.py b/bot/exts/events/hacktoberfest/hacktoberstats.py index 72067dbe..c29e24b0 100644 --- a/bot/exts/events/hacktoberfest/hacktoberstats.py +++ b/bot/exts/events/hacktoberfest/hacktoberstats.py @@ -432,6 +432,6 @@ class HacktoberStats(commands.Cog): return author_id, author_mention -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Hacktober Stats Cog.""" - bot.add_cog(HacktoberStats(bot)) + await bot.add_cog(HacktoberStats(bot)) diff --git a/bot/exts/events/hacktoberfest/timeleft.py b/bot/exts/events/hacktoberfest/timeleft.py index 55109599..f470e932 100644 --- a/bot/exts/events/hacktoberfest/timeleft.py +++ b/bot/exts/events/hacktoberfest/timeleft.py @@ -62,6 +62,6 @@ class TimeLeft(commands.Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Time Left Cog.""" - bot.add_cog(TimeLeft()) + await bot.add_cog(TimeLeft()) diff --git a/bot/exts/events/trivianight/_questions.py b/bot/exts/events/trivianight/_questions.py index d6beced9..5f1046dc 100644 --- a/bot/exts/events/trivianight/_questions.py +++ b/bot/exts/events/trivianight/_questions.py @@ -127,7 +127,7 @@ class QuestionView(View): if len(guesses) != 0: answers_chosen = { answer_choice: len( - tuple(filter(lambda x: x[0] == answer_choice, guesses.values())) + tuple(filter(lambda x: x[0] == answer_choice, guesses.values())) # noqa: B023 ) for answer_choice in labels } diff --git a/bot/exts/events/trivianight/_scoreboard.py b/bot/exts/events/trivianight/_scoreboard.py index a5a5fcac..bd61be3d 100644 --- a/bot/exts/events/trivianight/_scoreboard.py +++ b/bot/exts/events/trivianight/_scoreboard.py @@ -123,26 +123,26 @@ class ScoreboardView(View): return rank_embed @discord.ui.button(label="Scoreboard for Speed", style=ButtonStyle.green) - async def speed_leaderboard(self, button: Button, interaction: Interaction) -> None: + async def speed_leaderboard(self, interaction: Interaction, _: Button) -> None: """ Send an ephemeral message with the speed leaderboard embed. Parameters: - - button: The discord.ui.Button instance representing the `Speed Leaderboard` button. - interaction: The discord.Interaction instance containing information on the interaction between the user and the button. + - button: The discord.ui.Button instance representing the `Speed Leaderboard` button. """ await interaction.response.send_message(embed=await self._create_speed_embed(), ephemeral=True) @discord.ui.button(label="What's my rank?", style=ButtonStyle.blurple) - async def rank_button(self, button: Button, interaction: Interaction) -> None: + async def rank_button(self, interaction: Interaction, _: Button) -> None: """ Send an ephemeral message with the user's rank for the overall points/average speed. Parameters: - - button: The discord.ui.Button instance representing the `What's my rank?` button. - interaction: The discord.Interaction instance containing information on the interaction between the user and the button. + - button: The discord.ui.Button instance representing the `What's my rank?` button. """ await interaction.response.send_message(embed=self._get_rank(interaction.user), ephemeral=True) diff --git a/bot/exts/events/trivianight/trivianight.py b/bot/exts/events/trivianight/trivianight.py index 18d8327a..10435551 100644 --- a/bot/exts/events/trivianight/trivianight.py +++ b/bot/exts/events/trivianight/trivianight.py @@ -323,6 +323,6 @@ class TriviaNightCog(commands.Cog): )) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the TriviaNight cog.""" - bot.add_cog(TriviaNightCog(bot)) + await bot.add_cog(TriviaNightCog(bot)) diff --git a/bot/exts/fun/anagram.py b/bot/exts/fun/anagram.py index 79280fa9..d8ea6a55 100644 --- a/bot/exts/fun/anagram.py +++ b/bot/exts/fun/anagram.py @@ -104,6 +104,6 @@ class Anagram(commands.Cog): await game.message_creation(message) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Anagram cog.""" - bot.add_cog(Anagram(bot)) + await bot.add_cog(Anagram(bot)) diff --git a/bot/exts/fun/battleship.py b/bot/exts/fun/battleship.py index 77e38427..a8039cf2 100644 --- a/bot/exts/fun/battleship.py +++ b/bot/exts/fun/battleship.py @@ -442,6 +442,6 @@ class Battleship(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Battleship Cog.""" - bot.add_cog(Battleship(bot)) + await bot.add_cog(Battleship(bot)) diff --git a/bot/exts/fun/catify.py b/bot/exts/fun/catify.py index 32dfae09..6e8c75ba 100644 --- a/bot/exts/fun/catify.py +++ b/bot/exts/fun/catify.py @@ -81,6 +81,6 @@ class Catify(commands.Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Loads the catify cog.""" - bot.add_cog(Catify()) + await bot.add_cog(Catify()) diff --git a/bot/exts/fun/coinflip.py b/bot/exts/fun/coinflip.py index 804306bd..b7dee44d 100644 --- a/bot/exts/fun/coinflip.py +++ b/bot/exts/fun/coinflip.py @@ -48,6 +48,6 @@ class CoinFlip(commands.Cog): await ctx.send(message) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Loads the coinflip cog.""" - bot.add_cog(CoinFlip()) + await bot.add_cog(CoinFlip()) diff --git a/bot/exts/fun/connect_four.py b/bot/exts/fun/connect_four.py index 1b88d065..0d870a6e 100644 --- a/bot/exts/fun/connect_four.py +++ b/bot/exts/fun/connect_four.py @@ -447,6 +447,6 @@ class ConnectFour(commands.Cog): await self._play_game(ctx, None, board_size, str(emoji1), str(emoji2)) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load ConnectFour Cog.""" - bot.add_cog(ConnectFour(bot)) + await bot.add_cog(ConnectFour(bot)) diff --git a/bot/exts/fun/duck_game.py b/bot/exts/fun/duck_game.py index 10b03a49..a2612e51 100644 --- a/bot/exts/fun/duck_game.py +++ b/bot/exts/fun/duck_game.py @@ -341,6 +341,6 @@ class DuckGamesDirector(commands.Cog): return await ctx.send(file=file, embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the DuckGamesDirector cog.""" - bot.add_cog(DuckGamesDirector(bot)) + await bot.add_cog(DuckGamesDirector(bot)) diff --git a/bot/exts/fun/fun.py b/bot/exts/fun/fun.py index e7337cb6..8056b033 100644 --- a/bot/exts/fun/fun.py +++ b/bot/exts/fun/fun.py @@ -6,9 +6,10 @@ from pathlib import Path from typing import Literal import pyjokes +from botcore.utils.commands import clean_text_or_reply from discord import Embed from discord.ext import commands -from discord.ext.commands import BadArgument, Cog, Context, clean_content +from discord.ext.commands import BadArgument, Cog, Context from bot.bot import Bot from bot.constants import Client, Colours, Emojis @@ -60,13 +61,14 @@ class Fun(Cog): raise BadArgument(f"`{Client.prefix}roll` only supports between 1 and 6 rolls.") @commands.command(name="randomcase", aliases=("rcase", "randomcaps", "rcaps",)) - async def randomcase_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: - """Randomly converts the casing of a given `text`.""" + async def randomcase_command(self, ctx: Context, *, text: str | None) -> None: + """Randomly converts the casing of a given `text`, or the replied message.""" def conversion_func(text: str) -> str: """Randomly converts the casing of a given string.""" return "".join( char.upper() if round(random.random()) else char.lower() for char in text ) + text = await clean_text_or_reply(ctx, text) text, embed = await messages.get_text_and_embed(ctx, text) # Convert embed if it exists if embed is not None: @@ -157,6 +159,6 @@ class Fun(Cog): await ctx.send(joke) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Fun cog.""" - bot.add_cog(Fun(bot)) + await bot.add_cog(Fun(bot)) diff --git a/bot/exts/fun/game.py b/bot/exts/fun/game.py index 5f56bef7..4e01444e 100644 --- a/bot/exts/fun/game.py +++ b/bot/exts/fun/game.py @@ -2,12 +2,12 @@ import difflib import logging import random import re -from asyncio import sleep from datetime import datetime as dt, timedelta from enum import IntEnum from typing import Any, Optional from aiohttp import ClientSession +from botcore.utils import scheduling from discord import Embed from discord.ext import tasks from discord.ext.commands import Cog, Context, group @@ -15,7 +15,6 @@ from discord.ext.commands import Cog, Context, group from bot.bot import Bot from bot.constants import STAFF_ROLES, Tokens from bot.utils.decorators import with_role -from bot.utils.extensions import invoke_help_command from bot.utils.pagination import ImagePaginator, LinePaginator # Base URL of IGDB API @@ -185,46 +184,45 @@ class Games(Cog): self.genres: dict[str, int] = {} self.headers = BASE_HEADERS + self.token_refresh_scheduler = scheduling.Scheduler(__name__) - self.bot.loop.create_task(self.renew_access_token()) - - async def renew_access_token(self) -> None: - """Refeshes V4 access token a number of seconds before expiry. See `ACCESS_TOKEN_RENEWAL_WINDOW`.""" - while True: - async with self.http_session.post(OAUTH_URL, params=OAUTH_PARAMS) as resp: - result = await resp.json() - if resp.status != 200: - # If there is a valid access token continue to use that, - # otherwise unload cog. - if "Authorization" in self.headers: - time_delta = timedelta(seconds=ACCESS_TOKEN_RENEWAL_WINDOW) - logger.error( - "Failed to renew IGDB access token. " - f"Current token will last for {time_delta} " - f"OAuth response message: {result['message']}" - ) - else: - logger.warning( - "Invalid OAuth credentials. Unloading Games cog. " - f"OAuth response message: {result['message']}" - ) - self.bot.remove_cog("Games") - - return - - self.headers["Authorization"] = f"Bearer {result['access_token']}" - - # Attempt to renew before the token expires - next_renewal = result["expires_in"] - ACCESS_TOKEN_RENEWAL_WINDOW - - time_delta = timedelta(seconds=next_renewal) - logger.info(f"Successfully renewed access token. Refreshing again in {time_delta}") - - # This will be true the first time this loop runs. - # Since we now have an access token, its safe to start this task. - if self.genres == {}: - self.refresh_genres_task.start() - await sleep(next_renewal) + async def cog_load(self) -> None: + """Get an auth token and start the refresh task on cog load.""" + await self.refresh_token() + self.refresh_genres_task.start() + + async def refresh_token(self) -> None: + """ + Refresh the IGDB V4 access token. + + Once a new token has been created, schedule another refresh `ACCESS_TOKEN_RENEWAL_WINDOW` seconds before expiry. + """ + async with self.http_session.post(OAUTH_URL, params=OAUTH_PARAMS) as resp: + result = await resp.json() + if resp.status != 200: + # If there is a valid access token continue to use that, + # otherwise unload cog. + if "Authorization" in self.headers: + time_delta = timedelta(seconds=ACCESS_TOKEN_RENEWAL_WINDOW) + logger.error( + "Failed to renew IGDB access token. " + f"Current token will last for {time_delta} " + f"OAuth response message: {result['message']}" + ) + else: + logger.warning( + "Invalid OAuth credentials. Unloading Games cog. " + f"OAuth response message: {result['message']}" + ) + self.bot.remove_cog("Games") + return + + self.headers["Authorization"] = f"Bearer {result['access_token']}" + + # Attempt to renew before the token expires + seconds_until_next_renewal = result["expires_in"] - ACCESS_TOKEN_RENEWAL_WINDOW + logger.info(f"Successfully renewed access token. Refreshing again in {seconds_until_next_renewal} seconds") + self.token_refresh_scheduler.schedule_later(seconds_until_next_renewal, __name__, self.refresh_token()) @tasks.loop(hours=24.0) async def refresh_genres_task(self) -> None: @@ -267,7 +265,7 @@ class Games(Cog): """ # When user didn't specified genre, send help message if genre is None: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return # Capitalize genre for check @@ -505,7 +503,7 @@ class Games(Cog): return sorted((item for item in results if item[0] >= 0.60), reverse=True)[:4] -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Games cog.""" # Check does IGDB API key exist, if not, log warning and don't load cog if not Tokens.igdb_client_id: @@ -514,4 +512,4 @@ def setup(bot: Bot) -> None: if not Tokens.igdb_client_secret: logger.warning("No IGDB client secret. Not loading Games cog.") return - bot.add_cog(Games(bot)) + await bot.add_cog(Games(bot)) diff --git a/bot/exts/fun/hangman.py b/bot/exts/fun/hangman.py index a2c8c735..f385a955 100644 --- a/bot/exts/fun/hangman.py +++ b/bot/exts/fun/hangman.py @@ -110,7 +110,7 @@ class Hangman(commands.Cog): try: message = await self.bot.wait_for( - event="message", + "message", timeout=60.0, check=check ) @@ -177,6 +177,6 @@ class Hangman(commands.Cog): await ctx.send(embed=win_embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Hangman cog.""" - bot.add_cog(Hangman(bot)) + await bot.add_cog(Hangman(bot)) diff --git a/bot/exts/fun/latex.py b/bot/exts/fun/latex.py index aeabcd20..b5dada1c 100644 --- a/bot/exts/fun/latex.py +++ b/bot/exts/fun/latex.py @@ -133,6 +133,6 @@ class Latex(commands.Cog): await ctx.send(file=discord.File(image_path, "latex.png")) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Latex Cog.""" - bot.add_cog(Latex(bot)) + await bot.add_cog(Latex(bot)) diff --git a/bot/exts/fun/madlibs.py b/bot/exts/fun/madlibs.py index 21708e53..075dde75 100644 --- a/bot/exts/fun/madlibs.py +++ b/bot/exts/fun/madlibs.py @@ -93,7 +93,7 @@ class Madlibs(commands.Cog): await original_message.edit(embed=madlibs_embed) try: - message = await self.bot.wait_for(event="message", check=author_check, timeout=TIMEOUT) + message = await self.bot.wait_for("message", check=author_check, timeout=TIMEOUT) except TimeoutError: timeout_embed = discord.Embed( title=choice(NEGATIVE_REPLIES), @@ -143,6 +143,6 @@ class Madlibs(commands.Cog): error.handled = True -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Madlibs cog.""" - bot.add_cog(Madlibs(bot)) + await bot.add_cog(Madlibs(bot)) diff --git a/bot/exts/fun/magic_8ball.py b/bot/exts/fun/magic_8ball.py index a7b682ca..95d711c4 100644 --- a/bot/exts/fun/magic_8ball.py +++ b/bot/exts/fun/magic_8ball.py @@ -25,6 +25,6 @@ class Magic8ball(commands.Cog): await ctx.send("Usage: .8ball <question> (minimum length of 3 eg: `will I win?`)") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Magic8Ball Cog.""" - bot.add_cog(Magic8ball()) + await bot.add_cog(Magic8ball()) diff --git a/bot/exts/fun/minesweeper.py b/bot/exts/fun/minesweeper.py index a48b5051..f16b1db2 100644 --- a/bot/exts/fun/minesweeper.py +++ b/bot/exts/fun/minesweeper.py @@ -11,7 +11,6 @@ from bot.bot import Bot from bot.constants import Client from bot.utils.converters import CoordinateConverter from bot.utils.exceptions import UserNotPlayingError -from bot.utils.extensions import invoke_help_command MESSAGE_MAPPING = { 0: ":stop_button:", @@ -51,13 +50,14 @@ class Game: class Minesweeper(commands.Cog): """Play a game of Minesweeper.""" - def __init__(self): + def __init__(self, bot: Bot): + self.bot = bot self.games: dict[int, Game] = {} @commands.group(name="minesweeper", aliases=("ms",), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context) -> None: """Commands for Playing Minesweeper.""" - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @staticmethod def get_neighbours(x: int, y: int) -> Iterator[tuple[int, int]]: @@ -265,6 +265,6 @@ class Minesweeper(commands.Cog): del self.games[ctx.author.id] -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Minesweeper cog.""" - bot.add_cog(Minesweeper()) + await bot.add_cog(Minesweeper(bot)) diff --git a/bot/exts/fun/movie.py b/bot/exts/fun/movie.py index a04eeb41..422a83ac 100644 --- a/bot/exts/fun/movie.py +++ b/bot/exts/fun/movie.py @@ -9,13 +9,16 @@ from discord.ext.commands import Cog, Context, group from bot.bot import Bot from bot.constants import Tokens -from bot.utils.extensions import invoke_help_command +from bot.utils.exceptions import APIError from bot.utils.pagination import ImagePaginator +logger = logging.getLogger(__name__) + # Define base URL of TMDB BASE_URL = "https://api.themoviedb.org/3/" -logger = logging.getLogger(__name__) +# Logo of TMDB +THUMBNAIL_URL = "https://i.imgur.com/LtFtC8H.png" # Define movie params, that will be used for every movie request MOVIE_PARAMS = { @@ -23,6 +26,10 @@ MOVIE_PARAMS = { "language": "en-US" } +# Maximum value for `pages` API parameter. The maximum is documented as 1000 but +# anything over 500 returns an error. +MAX_PAGES = 500 + class MovieGenres(Enum): """Movies Genre names and IDs.""" @@ -50,12 +57,14 @@ class Movie(Cog): """Movie Cog contains movies command that grab random movies from TMDB.""" def __init__(self, bot: Bot): + self.bot = bot self.http_session: ClientSession = bot.http_session @group(name="movies", aliases=("movie",), invoke_without_command=True) async def movies(self, ctx: Context, genre: str = "", amount: int = 5) -> None: """ - Get random movies by specifying genre. Also support amount parameter, that define how much movies will be shown. + Get random movies by specifying genre. Also support amount parameter,\ + that define how much movies will be shown. Default 5. Use .movies genres to get all available genres. """ @@ -73,28 +82,14 @@ class Movie(Cog): try: result = await self.get_movies_data(self.http_session, MovieGenres[genre].value, 1) except KeyError: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return - # Check if "results" is in result. If not, throw error. - if "results" not in result: - err_msg = ( - f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " - f"{result['status_message']}." - ) - await ctx.send(err_msg) - logger.warning(err_msg) - # Get random page. Max page is last page where is movies with this genre. - page = random.randint(1, result["total_pages"]) + page = random.randint(1, min(result["total_pages"], MAX_PAGES)) # Get movies list from TMDB, check if results key in result. When not, raise error. movies = await self.get_movies_data(self.http_session, MovieGenres[genre].value, page) - if "results" not in movies: - err_msg = f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " \ - f"{result['status_message']}." - await ctx.send(err_msg) - logger.warning(err_msg) # Get all pages and embed pages = await self.get_pages(self.http_session, movies, amount) @@ -124,7 +119,18 @@ class Movie(Cog): # Make discover request to TMDB, return result async with client.get(url, params=params) as resp: - return await resp.json() + result, status = await resp.json(), resp.status + # Check if "results" is in result. If not, throw error. + if "results" not in result: + err_msg = ( + f"There was a problem making the TMDB API request. Response Code: {status}, " + f"TMDB: Status Code: {result.get('status_code', None)} " + f"TMDB: Status Message: {result.get('status_message', None)}, " + f"TMDB: Errors: {result.get('errors', None)}, " + ) + logger.error(err_msg) + raise APIError("TMDB API", status, err_msg) + return result async def get_pages(self, client: ClientSession, movies: dict[str, Any], amount: int) -> list[tuple[str, str]]: """Fetch all movie pages from movies dictionary. Return list of pages.""" @@ -196,10 +202,10 @@ class Movie(Cog): """Return embed of random movies. Uses name in title.""" embed = Embed(title=f"Random {name} Movies") embed.set_footer(text="This product uses the TMDb API but is not endorsed or certified by TMDb.") - embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png") + embed.set_thumbnail(url=THUMBNAIL_URL) return embed -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Movie Cog.""" - bot.add_cog(Movie(bot)) + await bot.add_cog(Movie(bot)) diff --git a/bot/exts/fun/quack.py b/bot/exts/fun/quack.py index 0c228aed..bb0cd731 100644 --- a/bot/exts/fun/quack.py +++ b/bot/exts/fun/quack.py @@ -50,13 +50,12 @@ class Quackstack(commands.Cog): description="The request failed. Please try again later.", color=Colours.soft_red, ) - if response.status != 200: + if response.status != 201: log.error(f"Response to Quackstack returned code {response.status}") await ctx.send(embed=error_embed) return - data = await response.json() - file = data["file"] + file = response.headers["Location"] embed = discord.Embed( title=f"Quack! Here's a {ducktype} for you.", @@ -65,11 +64,11 @@ class Quackstack(commands.Cog): url=f"{API_URL}/docs" ) - embed.set_image(url=API_URL + file) + embed.set_image(url=file) await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Loads the Quack cog.""" - bot.add_cog(Quackstack(bot)) + await bot.add_cog(Quackstack(bot)) diff --git a/bot/exts/fun/recommend_game.py b/bot/exts/fun/recommend_game.py index 42c9f7c2..e972b9a5 100644 --- a/bot/exts/fun/recommend_game.py +++ b/bot/exts/fun/recommend_game.py @@ -46,6 +46,6 @@ class RecommendGame(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Loads the RecommendGame cog.""" - bot.add_cog(RecommendGame(bot)) + await bot.add_cog(RecommendGame(bot)) diff --git a/bot/exts/fun/rps.py b/bot/exts/fun/rps.py index c6bbff46..50129835 100644 --- a/bot/exts/fun/rps.py +++ b/bot/exts/fun/rps.py @@ -52,6 +52,6 @@ class RPS(commands.Cog): await ctx.send(f"Sir Lancebot played {bot_move}! {player_mention} lost!") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the RPS Cog.""" - bot.add_cog(RPS(bot)) + await bot.add_cog(RPS(bot)) diff --git a/bot/exts/fun/snakes/__init__.py b/bot/exts/fun/snakes/__init__.py index ba8333fd..8aa39fb5 100644 --- a/bot/exts/fun/snakes/__init__.py +++ b/bot/exts/fun/snakes/__init__.py @@ -6,6 +6,6 @@ from bot.exts.fun.snakes._snakes_cog import Snakes log = logging.getLogger(__name__) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Snakes Cog.""" - bot.add_cog(Snakes(bot)) + await bot.add_cog(Snakes(bot)) diff --git a/bot/exts/fun/snakes/_snakes_cog.py b/bot/exts/fun/snakes/_snakes_cog.py index 59e57199..96718200 100644 --- a/bot/exts/fun/snakes/_snakes_cog.py +++ b/bot/exts/fun/snakes/_snakes_cog.py @@ -22,7 +22,6 @@ from bot.constants import ERROR_REPLIES, Tokens from bot.exts.fun.snakes import _utils as utils from bot.exts.fun.snakes._converter import Snake from bot.utils.decorators import locked -from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -440,7 +439,7 @@ class Snakes(Cog): @group(name="snakes", aliases=("snake",), invoke_without_command=True) async def snakes_group(self, ctx: Context) -> None: """Commands from our first code jam.""" - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @bot_has_permissions(manage_messages=True) @snakes_group.command(name="antidote") diff --git a/bot/exts/fun/space.py b/bot/exts/fun/space.py index 48ad0f96..22a89050 100644 --- a/bot/exts/fun/space.py +++ b/bot/exts/fun/space.py @@ -11,7 +11,6 @@ from discord.ext.commands import Cog, Context, group from bot.bot import Bot from bot.constants import Tokens from bot.utils.converters import DateConverter -from bot.utils.extensions import invoke_help_command logger = logging.getLogger(__name__) @@ -27,6 +26,7 @@ class Space(Cog): def __init__(self, bot: Bot): self.http_session = bot.http_session + self.bot = bot self.rovers = {} self.get_rovers.start() @@ -50,7 +50,7 @@ class Space(Cog): @group(name="space", invoke_without_command=True) async def space(self, ctx: Context) -> None: """Head command that contains commands about space.""" - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @space.command(name="apod") async def apod(self, ctx: Context, date: Optional[str]) -> None: @@ -227,10 +227,10 @@ class Space(Cog): ).set_image(url=image).set_footer(text="Powered by NASA API" + footer) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Space cog.""" if not Tokens.nasa: logger.warning("Can't find NASA API key. Not loading Space Cog.") return - bot.add_cog(Space(bot)) + await bot.add_cog(Space(bot)) diff --git a/bot/exts/fun/speedrun.py b/bot/exts/fun/speedrun.py index c2966ce1..43e570a2 100644 --- a/bot/exts/fun/speedrun.py +++ b/bot/exts/fun/speedrun.py @@ -21,6 +21,6 @@ class Speedrun(commands.Cog): await ctx.send(choice(LINKS)) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Speedrun cog.""" - bot.add_cog(Speedrun()) + await bot.add_cog(Speedrun()) diff --git a/bot/exts/fun/status_codes.py b/bot/exts/fun/status_codes.py index 501cbe0a..cf544a19 100644 --- a/bot/exts/fun/status_codes.py +++ b/bot/exts/fun/status_codes.py @@ -82,6 +82,6 @@ class HTTPStatusCodes(commands.Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the HTTPStatusCodes cog.""" - bot.add_cog(HTTPStatusCodes(bot)) + await bot.add_cog(HTTPStatusCodes(bot)) diff --git a/bot/exts/fun/tic_tac_toe.py b/bot/exts/fun/tic_tac_toe.py index 5dd38a81..fa2a7531 100644 --- a/bot/exts/fun/tic_tac_toe.py +++ b/bot/exts/fun/tic_tac_toe.py @@ -333,6 +333,6 @@ class TicTacToe(Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the TicTacToe cog.""" - bot.add_cog(TicTacToe()) + await bot.add_cog(TicTacToe()) diff --git a/bot/exts/fun/trivia_quiz.py b/bot/exts/fun/trivia_quiz.py index 4a1cec5b..31652374 100644 --- a/bot/exts/fun/trivia_quiz.py +++ b/bot/exts/fun/trivia_quiz.py @@ -435,7 +435,7 @@ class TriviaQuiz(commands.Cog): def contains_correct_answer(m: discord.Message) -> bool: return m.channel == ctx.channel and any( fuzz.ratio(answer.lower(), m.content.lower()) > variation_tolerance - for answer in quiz_entry.answers + for answer in quiz_entry.answers # noqa: B023 ) return contains_correct_answer @@ -670,6 +670,6 @@ class TriviaQuiz(commands.Cog): await channel.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the TriviaQuiz cog.""" - bot.add_cog(TriviaQuiz(bot)) + await bot.add_cog(TriviaQuiz(bot)) diff --git a/bot/exts/fun/uwu.py b/bot/exts/fun/uwu.py index 83497893..7a9d55d0 100644 --- a/bot/exts/fun/uwu.py +++ b/bot/exts/fun/uwu.py @@ -199,6 +199,6 @@ class Uwu(Cog): await ctx.send(content=converted_text, embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the uwu cog.""" - bot.add_cog(Uwu(bot)) + await bot.add_cog(Uwu(bot)) diff --git a/bot/exts/fun/wonder_twins.py b/bot/exts/fun/wonder_twins.py index 79d6b6d9..0c5b0a76 100644 --- a/bot/exts/fun/wonder_twins.py +++ b/bot/exts/fun/wonder_twins.py @@ -44,6 +44,6 @@ class WonderTwins(Cog): await ctx.send(f"Form of {self.format_phrase()}!") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the WonderTwins cog.""" - bot.add_cog(WonderTwins()) + await bot.add_cog(WonderTwins()) diff --git a/bot/exts/fun/xkcd.py b/bot/exts/fun/xkcd.py index b56c53d9..380c3c80 100644 --- a/bot/exts/fun/xkcd.py +++ b/bot/exts/fun/xkcd.py @@ -86,6 +86,6 @@ class XKCD(Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the XKCD cog.""" - bot.add_cog(XKCD(bot)) + await bot.add_cog(XKCD(bot)) diff --git a/bot/exts/holidays/earth_day/save_the_planet.py b/bot/exts/holidays/earth_day/save_the_planet.py index 13c84886..63836faf 100644 --- a/bot/exts/holidays/earth_day/save_the_planet.py +++ b/bot/exts/holidays/earth_day/save_the_planet.py @@ -20,6 +20,6 @@ class SaveThePlanet(commands.Cog): await ctx.send(embed=return_embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Save the Planet Cog.""" - bot.add_cog(SaveThePlanet()) + await bot.add_cog(SaveThePlanet()) diff --git a/bot/exts/holidays/easter/april_fools_vids.py b/bot/exts/holidays/easter/april_fools_vids.py index ae22f751..7f46a569 100644 --- a/bot/exts/holidays/easter/april_fools_vids.py +++ b/bot/exts/holidays/easter/april_fools_vids.py @@ -25,6 +25,6 @@ class AprilFoolVideos(commands.Cog): await ctx.send(f"Check out this April Fools' video by {channel}.\n\n{url}") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the April Fools' Cog.""" - bot.add_cog(AprilFoolVideos()) + await bot.add_cog(AprilFoolVideos()) diff --git a/bot/exts/holidays/easter/bunny_name_generator.py b/bot/exts/holidays/easter/bunny_name_generator.py index f767f7c5..50872ebc 100644 --- a/bot/exts/holidays/easter/bunny_name_generator.py +++ b/bot/exts/holidays/easter/bunny_name_generator.py @@ -89,6 +89,6 @@ class BunnyNameGenerator(commands.Cog): await ctx.send(bunnified_name) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Bunny Name Generator Cog.""" - bot.add_cog(BunnyNameGenerator()) + await bot.add_cog(BunnyNameGenerator()) diff --git a/bot/exts/holidays/easter/earth_photos.py b/bot/exts/holidays/easter/earth_photos.py index 27442f1c..e60e2626 100644 --- a/bot/exts/holidays/easter/earth_photos.py +++ b/bot/exts/holidays/easter/earth_photos.py @@ -57,9 +57,9 @@ class EarthPhotos(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Earth Photos cog.""" if not Tokens.unsplash_access_key: log.warning("No Unsplash access key found. Cog not loading.") return - bot.add_cog(EarthPhotos(bot)) + await bot.add_cog(EarthPhotos(bot)) diff --git a/bot/exts/holidays/easter/easter_riddle.py b/bot/exts/holidays/easter/easter_riddle.py index c9b7fc53..c5d7b164 100644 --- a/bot/exts/holidays/easter/easter_riddle.py +++ b/bot/exts/holidays/easter/easter_riddle.py @@ -107,6 +107,6 @@ class EasterRiddle(commands.Cog): self.winners.add(message.author.mention) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Easter Riddle Cog load.""" - bot.add_cog(EasterRiddle(bot)) + await bot.add_cog(EasterRiddle(bot)) diff --git a/bot/exts/holidays/easter/egg_decorating.py b/bot/exts/holidays/easter/egg_decorating.py index 1db9b347..a9334820 100644 --- a/bot/exts/holidays/easter/egg_decorating.py +++ b/bot/exts/holidays/easter/egg_decorating.py @@ -114,6 +114,6 @@ class EggDecorating(commands.Cog): return new_im -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Egg decorating Cog.""" - bot.add_cog(EggDecorating()) + await bot.add_cog(EggDecorating()) diff --git a/bot/exts/holidays/easter/egg_facts.py b/bot/exts/holidays/easter/egg_facts.py index 152af6a4..43b31c7b 100644 --- a/bot/exts/holidays/easter/egg_facts.py +++ b/bot/exts/holidays/easter/egg_facts.py @@ -29,8 +29,6 @@ class EasterFacts(commands.Cog): @seasonal_task(Month.APRIL) async def send_egg_fact_daily(self) -> None: """A background task that sends an easter egg fact in the event channel everyday.""" - await self.bot.wait_until_guild_available() - channel = self.bot.get_channel(Channels.sir_lancebot_playground) await channel.send(embed=self.make_embed()) @@ -50,6 +48,6 @@ class EasterFacts(commands.Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Easter Egg facts Cog.""" - bot.add_cog(EasterFacts(bot)) + await bot.add_cog(EasterFacts(bot)) diff --git a/bot/exts/holidays/easter/egghead_quiz.py b/bot/exts/holidays/easter/egghead_quiz.py index 06229537..2e4d1931 100644 --- a/bot/exts/holidays/easter/egghead_quiz.py +++ b/bot/exts/holidays/easter/egghead_quiz.py @@ -65,23 +65,47 @@ class EggheadQuiz(commands.Cog): msg = await ctx.fetch_message(msg.id) # Refreshes message - total_no = sum([len(await r.users().flatten()) for r in msg.reactions]) - len(valid_emojis) # - bot's reactions + users = [] + for reaction in msg.reactions: + async for user in reaction.users(): + users.append(user) + + total_no = len(users) - len(valid_emojis) # - bot's reactions if total_no == 0: return await msg.delete() # To avoid ZeroDivisionError if nobody reacts results = ["**VOTES:**"] for emoji, _ in answers: - num = [len(await r.users().flatten()) for r in msg.reactions if str(r.emoji) == emoji][0] - 1 + users = [] + for reaction in msg.reactions: + if str(reaction.emoji) != emoji: + continue + + async for user in reaction.users(): + users.append(user) + break + + num = len(users) - 1 percent = round(100 * num / total_no) s = "" if num == 1 else "s" string = f"{emoji} - {num} vote{s} ({percent}%)" results.append(string) + users = [] + for reaction in msg.reactions: + if str(reaction.emoji) != correct: + continue + + async for user in reaction.users(): + users.append(user) + + # At this point we've added everyone who reacted + # with the correct answer, so stop looping over reactions. + break + mentions = " ".join([ - u.mention for u in [ - await r.users().flatten() for r in msg.reactions if str(r.emoji) == correct - ][0] if not u.bot + u.mention for u in users if not u.bot ]) content = f"Well done {mentions} for getting it correct!" if mentions else "Nobody got it right..." @@ -95,10 +119,16 @@ class EggheadQuiz(commands.Cog): await ctx.send(content, embed=a_embed) @staticmethod - async def already_reacted(message: discord.Message, user: Union[discord.Member, discord.User]) -> bool: + async def already_reacted(new_reaction: discord.Reaction, user: Union[discord.Member, discord.User]) -> bool: """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 + message = new_reaction.message + for reaction in message.reactions: + if reaction.emoji == new_reaction.emoji: + continue # can't react with same emoji twice so skip 2nd reaction check + async for usr in reaction.users(): + if usr.id == user.id: # user also reacted with the emoji, i.e. has already reacted + return True + return False @commands.Cog.listener() async def on_reaction_add(self, reaction: discord.Reaction, user: Union[discord.Member, discord.User]) -> None: @@ -109,10 +139,10 @@ class EggheadQuiz(commands.Cog): return if str(reaction.emoji) not in self.quiz_messages[reaction.message.id]: return await reaction.message.remove_reaction(reaction, user) - if await self.already_reacted(reaction.message, user): + if await self.already_reacted(reaction, user): return await reaction.message.remove_reaction(reaction, user) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Egghead Quiz Cog.""" - bot.add_cog(EggheadQuiz()) + await bot.add_cog(EggheadQuiz()) diff --git a/bot/exts/holidays/easter/traditions.py b/bot/exts/holidays/easter/traditions.py index f54ab5c4..3ac5617c 100644 --- a/bot/exts/holidays/easter/traditions.py +++ b/bot/exts/holidays/easter/traditions.py @@ -23,6 +23,6 @@ class Traditions(commands.Cog): await ctx.send(f"{random_country}:\n{traditions[random_country]}") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Traditions Cog.""" - bot.add_cog(Traditions()) + await bot.add_cog(Traditions()) diff --git a/bot/exts/holidays/halloween/8ball.py b/bot/exts/holidays/halloween/8ball.py index 4fec8463..21b55a01 100644 --- a/bot/exts/holidays/halloween/8ball.py +++ b/bot/exts/holidays/halloween/8ball.py @@ -26,6 +26,6 @@ class SpookyEightBall(commands.Cog): await msg.edit(content=f"{choice[0]} \n{choice[1]}") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Spooky Eight Ball Cog.""" - bot.add_cog(SpookyEightBall()) + await bot.add_cog(SpookyEightBall()) diff --git a/bot/exts/holidays/halloween/candy_collection.py b/bot/exts/holidays/halloween/candy_collection.py index 220ba8e5..683114f9 100644 --- a/bot/exts/holidays/halloween/candy_collection.py +++ b/bot/exts/holidays/halloween/candy_collection.py @@ -88,10 +88,7 @@ class CandyCollection(commands.Cog): if message.author.bot: return - recent_message_ids = map( - lambda m: m.id, - await self.hacktober_channel.history(limit=10).flatten() - ) + recent_message_ids = [message.id async for message in self.hacktober_channel.history(limit=10)] if message.id in recent_message_ids: await self.reacted_msg_chance(message) return @@ -214,6 +211,6 @@ class CandyCollection(commands.Cog): await ctx.send(embed=e) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Candy Collection Cog.""" - bot.add_cog(CandyCollection(bot)) + await bot.add_cog(CandyCollection(bot)) diff --git a/bot/exts/holidays/halloween/halloween_facts.py b/bot/exts/holidays/halloween/halloween_facts.py index adde2310..a0d63f64 100644 --- a/bot/exts/holidays/halloween/halloween_facts.py +++ b/bot/exts/holidays/halloween/halloween_facts.py @@ -50,6 +50,6 @@ class HalloweenFacts(commands.Cog): return discord.Embed(title=title, description=fact, color=PUMPKIN_ORANGE) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Halloween Facts Cog.""" - bot.add_cog(HalloweenFacts()) + await bot.add_cog(HalloweenFacts()) diff --git a/bot/exts/holidays/halloween/halloweenify.py b/bot/exts/holidays/halloween/halloweenify.py index 03b52589..a16ea6cc 100644 --- a/bot/exts/holidays/halloween/halloweenify.py +++ b/bot/exts/holidays/halloween/halloweenify.py @@ -59,6 +59,6 @@ class Halloweenify(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Halloweenify Cog.""" - bot.add_cog(Halloweenify()) + await bot.add_cog(Halloweenify()) diff --git a/bot/exts/holidays/halloween/monsterbio.py b/bot/exts/holidays/halloween/monsterbio.py index 0556a193..8e83e9d1 100644 --- a/bot/exts/holidays/halloween/monsterbio.py +++ b/bot/exts/holidays/halloween/monsterbio.py @@ -49,6 +49,6 @@ class MonsterBio(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Monster Bio Cog.""" - bot.add_cog(MonsterBio()) + await bot.add_cog(MonsterBio()) diff --git a/bot/exts/holidays/halloween/monstersurvey.py b/bot/exts/holidays/halloween/monstersurvey.py index f3433886..517f1bcb 100644 --- a/bot/exts/holidays/halloween/monstersurvey.py +++ b/bot/exts/holidays/halloween/monstersurvey.py @@ -200,6 +200,6 @@ class MonsterSurvey(Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Monster Survey Cog.""" - bot.add_cog(MonsterSurvey()) + await bot.add_cog(MonsterSurvey()) diff --git a/bot/exts/holidays/halloween/scarymovie.py b/bot/exts/holidays/halloween/scarymovie.py index 89310b97..74bcef90 100644 --- a/bot/exts/holidays/halloween/scarymovie.py +++ b/bot/exts/holidays/halloween/scarymovie.py @@ -120,6 +120,6 @@ class ScaryMovie(commands.Cog): return embed -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Scary Movie Cog.""" - bot.add_cog(ScaryMovie(bot)) + await bot.add_cog(ScaryMovie(bot)) diff --git a/bot/exts/holidays/halloween/spookygif.py b/bot/exts/holidays/halloween/spookygif.py index 9511d407..750e86ca 100644 --- a/bot/exts/holidays/halloween/spookygif.py +++ b/bot/exts/holidays/halloween/spookygif.py @@ -25,7 +25,7 @@ class SpookyGif(commands.Cog): # Make a GET request to the Giphy API to get a random halloween gif. async with self.bot.http_session.get(API_URL, params=params) as resp: data = await resp.json() - url = data["data"]["image_url"] + url = data["data"]["images"]["downsized"]["url"] embed = discord.Embed(title="A spooooky gif!", colour=Colours.purple) embed.set_image(url=url) @@ -33,6 +33,6 @@ class SpookyGif(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Spooky GIF Cog load.""" - bot.add_cog(SpookyGif(bot)) + await bot.add_cog(SpookyGif(bot)) diff --git a/bot/exts/holidays/halloween/spookynamerate.py b/bot/exts/holidays/halloween/spookynamerate.py index 02fb71c3..a76e5e12 100644 --- a/bot/exts/holidays/halloween/spookynamerate.py +++ b/bot/exts/holidays/halloween/spookynamerate.py @@ -95,8 +95,6 @@ class SpookyNameRate(Cog): self.bot = bot self.name = None - self.bot.loop.create_task(self.load_vars()) - self.first_time = None self.poll = False self.announce_name.start() @@ -104,7 +102,7 @@ class SpookyNameRate(Cog): # Define an asyncio.Lock() to make sure the dictionary isn't changed # when checking the messages for duplicate emojis' - async def load_vars(self) -> None: + async def cog_load(self) -> None: """Loads the variables that couldn't be loaded in __init__.""" self.first_time = await self.data.get("first_time", True) self.name = await self.data.get("name") @@ -357,7 +355,6 @@ class SpookyNameRate(Cog): async def get_channel(self) -> Optional[TextChannel]: """Gets the sir-lancebot-channel after waiting until ready.""" - await self.bot.wait_until_ready() channel = self.bot.get_channel( Channels.sir_lancebot_playground ) or await self.bot.fetch_channel(Channels.sir_lancebot_playground) @@ -386,6 +383,6 @@ class SpookyNameRate(Cog): self.announce_name.cancel() -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the SpookyNameRate Cog.""" - bot.add_cog(SpookyNameRate(bot)) + await bot.add_cog(SpookyNameRate(bot)) diff --git a/bot/exts/holidays/halloween/spookyrating.py b/bot/exts/holidays/halloween/spookyrating.py index ec6e8821..373b6583 100644 --- a/bot/exts/holidays/halloween/spookyrating.py +++ b/bot/exts/holidays/halloween/spookyrating.py @@ -62,6 +62,6 @@ class SpookyRating(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Spooky Rating Cog.""" - bot.add_cog(SpookyRating()) + await bot.add_cog(SpookyRating()) diff --git a/bot/exts/holidays/halloween/spookyreact.py b/bot/exts/holidays/halloween/spookyreact.py index e228b91d..945bde33 100644 --- a/bot/exts/holidays/halloween/spookyreact.py +++ b/bot/exts/holidays/halloween/spookyreact.py @@ -17,7 +17,8 @@ SPOOKY_TRIGGERS = { "pumpkin": (r"\bpumpkin\b", "\U0001F383"), "halloween": (r"\bhalloween\b", "\U0001F383"), "jack-o-lantern": (r"\bjack-o-lantern\b", "\U0001F383"), - "danger": (r"\bdanger\b", "\U00002620") + "danger": (r"\bdanger\b", "\U00002620"), + "bat": (r"\bbat((wo)?m[ae]n|persons?|people|s)?\b", "\U0001F987"), } @@ -65,6 +66,6 @@ class SpookyReact(Cog): return False -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Spooky Reaction Cog.""" - bot.add_cog(SpookyReact(bot)) + await bot.add_cog(SpookyReact(bot)) diff --git a/bot/exts/holidays/hanukkah/hanukkah_embed.py b/bot/exts/holidays/hanukkah/hanukkah_embed.py index 5767f91e..1ebc21e8 100644 --- a/bot/exts/holidays/hanukkah/hanukkah_embed.py +++ b/bot/exts/holidays/hanukkah/hanukkah_embed.py @@ -96,6 +96,6 @@ class HanukkahEmbed(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Hanukkah Embed Cog.""" - bot.add_cog(HanukkahEmbed(bot)) + await bot.add_cog(HanukkahEmbed(bot)) diff --git a/bot/exts/holidays/pride/drag_queen_name.py b/bot/exts/holidays/pride/drag_queen_name.py index bd01a603..0c1ca6fb 100644 --- a/bot/exts/holidays/pride/drag_queen_name.py +++ b/bot/exts/holidays/pride/drag_queen_name.py @@ -21,6 +21,6 @@ class DragNames(commands.Cog): await ctx.send(random.choice(NAMES)) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Drag Names Cog.""" - bot.add_cog(DragNames()) + await bot.add_cog(DragNames()) diff --git a/bot/exts/holidays/pride/pride_anthem.py b/bot/exts/holidays/pride/pride_anthem.py index e8a4563b..6b78cba1 100644 --- a/bot/exts/holidays/pride/pride_anthem.py +++ b/bot/exts/holidays/pride/pride_anthem.py @@ -46,6 +46,6 @@ class PrideAnthem(commands.Cog): await ctx.send("I couldn't find a video, sorry!") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Pride Anthem Cog.""" - bot.add_cog(PrideAnthem()) + await bot.add_cog(PrideAnthem()) diff --git a/bot/exts/holidays/pride/pride_facts.py b/bot/exts/holidays/pride/pride_facts.py index 340f0b43..36a9415e 100644 --- a/bot/exts/holidays/pride/pride_facts.py +++ b/bot/exts/holidays/pride/pride_facts.py @@ -28,8 +28,6 @@ class PrideFacts(commands.Cog): @seasonal_task(Month.JUNE) async def send_pride_fact_daily(self) -> None: """Background task to post the daily pride fact every day.""" - await self.bot.wait_until_guild_available() - channel = self.bot.get_channel(Channels.sir_lancebot_playground) await self.send_select_fact(channel, datetime.utcnow()) @@ -94,6 +92,6 @@ class PrideFacts(commands.Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Pride Facts Cog.""" - bot.add_cog(PrideFacts(bot)) + await bot.add_cog(PrideFacts(bot)) diff --git a/bot/exts/holidays/pride/pride_leader.py b/bot/exts/holidays/pride/pride_leader.py index adf01134..120e9e16 100644 --- a/bot/exts/holidays/pride/pride_leader.py +++ b/bot/exts/holidays/pride/pride_leader.py @@ -112,6 +112,6 @@ class PrideLeader(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Pride Leader Cog.""" - bot.add_cog(PrideLeader(bot)) + await bot.add_cog(PrideLeader(bot)) diff --git a/bot/exts/holidays/valentines/be_my_valentine.py b/bot/exts/holidays/valentines/be_my_valentine.py index cbb95157..5ffd14e6 100644 --- a/bot/exts/holidays/valentines/be_my_valentine.py +++ b/bot/exts/holidays/valentines/be_my_valentine.py @@ -163,6 +163,6 @@ class BeMyValentine(commands.Cog): return random.choice(self.valentines["valentine_compliments"]) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Be my Valentine Cog.""" - bot.add_cog(BeMyValentine(bot)) + await bot.add_cog(BeMyValentine(bot)) diff --git a/bot/exts/holidays/valentines/lovecalculator.py b/bot/exts/holidays/valentines/lovecalculator.py index 10dea9df..c212e833 100644 --- a/bot/exts/holidays/valentines/lovecalculator.py +++ b/bot/exts/holidays/valentines/lovecalculator.py @@ -95,6 +95,6 @@ class LoveCalculator(Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Love calculator Cog.""" - bot.add_cog(LoveCalculator()) + await bot.add_cog(LoveCalculator()) diff --git a/bot/exts/holidays/valentines/movie_generator.py b/bot/exts/holidays/valentines/movie_generator.py index d2dc8213..64b86f1b 100644 --- a/bot/exts/holidays/valentines/movie_generator.py +++ b/bot/exts/holidays/valentines/movie_generator.py @@ -62,6 +62,6 @@ class RomanceMovieFinder(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Romance movie Cog.""" - bot.add_cog(RomanceMovieFinder(bot)) + await bot.add_cog(RomanceMovieFinder(bot)) diff --git a/bot/exts/holidays/valentines/myvalenstate.py b/bot/exts/holidays/valentines/myvalenstate.py index 4b547d9b..8d8772d4 100644 --- a/bot/exts/holidays/valentines/myvalenstate.py +++ b/bot/exts/holidays/valentines/myvalenstate.py @@ -77,6 +77,6 @@ class MyValenstate(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Valenstate Cog.""" - bot.add_cog(MyValenstate()) + await bot.add_cog(MyValenstate()) diff --git a/bot/exts/holidays/valentines/pickuplines.py b/bot/exts/holidays/valentines/pickuplines.py index bc4b88c6..8562a07d 100644 --- a/bot/exts/holidays/valentines/pickuplines.py +++ b/bot/exts/holidays/valentines/pickuplines.py @@ -36,6 +36,6 @@ class PickupLine(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Pickup lines Cog.""" - bot.add_cog(PickupLine()) + await bot.add_cog(PickupLine()) diff --git a/bot/exts/holidays/valentines/savethedate.py b/bot/exts/holidays/valentines/savethedate.py index 3638c1ef..7fd644df 100644 --- a/bot/exts/holidays/valentines/savethedate.py +++ b/bot/exts/holidays/valentines/savethedate.py @@ -33,6 +33,6 @@ class SaveTheDate(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Save the date Cog.""" - bot.add_cog(SaveTheDate()) + await bot.add_cog(SaveTheDate()) diff --git a/bot/exts/holidays/valentines/valentine_zodiac.py b/bot/exts/holidays/valentines/valentine_zodiac.py index d1b3a630..0a28a5c5 100644 --- a/bot/exts/holidays/valentines/valentine_zodiac.py +++ b/bot/exts/holidays/valentines/valentine_zodiac.py @@ -141,6 +141,6 @@ class ValentineZodiac(commands.Cog): log.trace("Embed from date successfully sent.") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Valentine zodiac Cog.""" - bot.add_cog(ValentineZodiac()) + await bot.add_cog(ValentineZodiac()) diff --git a/bot/exts/holidays/valentines/whoisvalentine.py b/bot/exts/holidays/valentines/whoisvalentine.py index 67e46aa4..c652e616 100644 --- a/bot/exts/holidays/valentines/whoisvalentine.py +++ b/bot/exts/holidays/valentines/whoisvalentine.py @@ -44,6 +44,6 @@ class ValentineFacts(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Who is Valentine Cog.""" - bot.add_cog(ValentineFacts()) + await bot.add_cog(ValentineFacts()) diff --git a/bot/exts/utilities/bookmark.py b/bot/exts/utilities/bookmark.py index 9eb23988..50e3038f 100644 --- a/bot/exts/utilities/bookmark.py +++ b/bot/exts/utilities/bookmark.py @@ -189,6 +189,6 @@ class Bookmark(commands.Cog): await target_message.delete() -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Bookmark cog.""" - bot.add_cog(Bookmark(bot)) + await bot.add_cog(Bookmark(bot)) diff --git a/bot/exts/utilities/challenges.py b/bot/exts/utilities/challenges.py index ab7ae442..1a5bf289 100644 --- a/bot/exts/utilities/challenges.py +++ b/bot/exts/utilities/challenges.py @@ -336,6 +336,6 @@ class Challenges(commands.Cog): await original_message.edit(embed=kata_embed, view=None) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Challenges cog.""" - bot.add_cog(Challenges(bot)) + await bot.add_cog(Challenges(bot)) diff --git a/bot/exts/utilities/cheatsheet.py b/bot/exts/utilities/cheatsheet.py index 33d29f67..3141a050 100644 --- a/bot/exts/utilities/cheatsheet.py +++ b/bot/exts/utilities/cheatsheet.py @@ -107,6 +107,6 @@ class CheatSheet(commands.Cog): await ctx.send(content=description) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the CheatSheet cog.""" - bot.add_cog(CheatSheet(bot)) + await bot.add_cog(CheatSheet(bot)) diff --git a/bot/exts/utilities/colour.py b/bot/exts/utilities/colour.py index ee6bad93..20f97e4b 100644 --- a/bot/exts/utilities/colour.py +++ b/bot/exts/utilities/colour.py @@ -13,7 +13,6 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.exts.core.extensions import invoke_help_command from bot.utils.decorators import whitelist_override THUMBNAIL_SIZE = (80, 80) @@ -99,7 +98,7 @@ class Colour(commands.Cog): extra_colour = ImageColor.getrgb(colour_input) await self.send_colour_response(ctx, extra_colour) except ValueError: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @colour.command() async def rgb(self, ctx: commands.Context, red: int, green: int, blue: int) -> None: @@ -261,6 +260,6 @@ class Colour(commands.Cog): return f"#{self.colour_mapping[match]}" -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Colour cog.""" - bot.add_cog(Colour(bot)) + await bot.add_cog(Colour(bot)) diff --git a/bot/exts/utilities/conversationstarters.py b/bot/exts/utilities/conversationstarters.py index 8bf2abfd..410ea884 100644 --- a/bot/exts/utilities/conversationstarters.py +++ b/bot/exts/utilities/conversationstarters.py @@ -121,6 +121,6 @@ class ConvoStarters(commands.Cog): self.bot.loop.create_task(self._listen_for_refresh(ctx.author, message)) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the ConvoStarters cog.""" - bot.add_cog(ConvoStarters(bot)) + await bot.add_cog(ConvoStarters(bot)) diff --git a/bot/exts/utilities/emoji.py b/bot/exts/utilities/emoji.py index fa438d7f..ec40be01 100644 --- a/bot/exts/utilities/emoji.py +++ b/bot/exts/utilities/emoji.py @@ -10,7 +10,6 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Colours, ERROR_REPLIES -from bot.utils.extensions import invoke_help_command from bot.utils.pagination import LinePaginator from bot.utils.time import time_since @@ -20,6 +19,9 @@ log = logging.getLogger(__name__) class Emojis(commands.Cog): """A collection of commands related to emojis in the server.""" + def __init__(self, bot: Bot) -> None: + self.bot = bot + @staticmethod def embed_builder(emoji: dict) -> tuple[Embed, list[str]]: """Generates an embed with the emoji names and count.""" @@ -74,7 +76,7 @@ class Emojis(commands.Cog): if emoji is not None: await ctx.invoke(self.info_command, emoji) else: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @emoji_group.command(name="count", aliases=("c",)) async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None: @@ -118,6 +120,6 @@ class Emojis(commands.Cog): await ctx.send(embed=emoji_information) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Emojis cog.""" - bot.add_cog(Emojis()) + await bot.add_cog(Emojis(bot)) diff --git a/bot/exts/utilities/epoch.py b/bot/exts/utilities/epoch.py index 42312dd1..6f572640 100644 --- a/bot/exts/utilities/epoch.py +++ b/bot/exts/utilities/epoch.py @@ -6,7 +6,6 @@ from dateutil import parser from discord.ext import commands from bot.bot import Bot -from bot.utils.extensions import invoke_help_command # https://discord.com/developers/docs/reference#message-formatting-timestamp-styles STYLES = { @@ -48,6 +47,9 @@ class DateString(commands.Converter): class Epoch(commands.Cog): """Convert an entered time and date to a unix timestamp.""" + def __init__(self, bot: Bot) -> None: + self.bot = bot + @commands.command(name="epoch") async def epoch(self, ctx: commands.Context, *, date_time: DateString = None) -> None: """ @@ -71,7 +73,7 @@ class Epoch(commands.Cog): Times in the dropdown are shown in UTC """ if not date_time: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return if isinstance(date_time, tuple): @@ -117,7 +119,7 @@ class TimestampMenuView(discord.ui.View): self.dropdown.add_option(label=label, description=date_time) @discord.ui.select(placeholder="Select the format of your timestamp") - async def select_format(self, _: discord.ui.Select, interaction: discord.Interaction) -> discord.Message: + async def select_format(self, interaction: discord.Interaction, _: discord.ui.Select) -> discord.Message: """Drop down menu which contains a list of formats which discord timestamps can take.""" selected = interaction.data["values"][0] if selected == "Epoch": @@ -133,6 +135,6 @@ class TimestampMenuView(discord.ui.View): return True -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Epoch cog.""" - bot.add_cog(Epoch()) + await bot.add_cog(Epoch(bot)) diff --git a/bot/exts/utilities/githubinfo.py b/bot/exts/utilities/githubinfo.py index 046f67df..a7979718 100644 --- a/bot/exts/utilities/githubinfo.py +++ b/bot/exts/utilities/githubinfo.py @@ -12,7 +12,6 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Colours, ERROR_REPLIES, Emojis, NEGATIVE_REPLIES, Tokens -from bot.exts.core.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -168,7 +167,7 @@ class GithubInfo(commands.Cog): async def github_group(self, ctx: commands.Context) -> None: """Commands for finding information related to GitHub.""" if ctx.invoked_subcommand is None: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: @@ -363,6 +362,6 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the GithubInfo cog.""" - bot.add_cog(GithubInfo(bot)) + await bot.add_cog(GithubInfo(bot)) diff --git a/bot/exts/utilities/logging.py b/bot/exts/utilities/logging.py new file mode 100644 index 00000000..83b7025f --- /dev/null +++ b/bot/exts/utilities/logging.py @@ -0,0 +1,40 @@ +from botcore.utils.logging import get_logger +from discord.ext.commands import Cog + +from bot import constants +from bot.bot import Bot + +log = get_logger(__name__) + + +class Logging(Cog): + """Debug logging module.""" + + def __init__(self, bot: Bot): + self.bot = bot + + async def cog_load(self) -> None: + """Announce our presence to the configured dev-log channel after checking channel constants.""" + await self.check_channels() + await self.bot.log_to_dev_log( + title=self.bot.name, + details="Connected!", + ) + + async def check_channels(self) -> None: + """Verifies that all channel constants refer to channels which exist.""" + if constants.Client.debug: + log.info("Skipping Channels Check.") + return + + all_channels_ids = [channel.id for channel in self.bot.get_all_channels()] + for name, channel_id in vars(constants.Channels).items(): + if name.startswith("_"): + continue + if channel_id not in all_channels_ids: + log.error(f'Channel "{name}" with ID {channel_id} missing') + + +async def setup(bot: Bot) -> None: + """Load the Logging cog.""" + await bot.add_cog(Logging(bot)) diff --git a/bot/exts/utilities/pythonfacts.py b/bot/exts/utilities/pythonfacts.py index ef190185..a5bfb612 100644 --- a/bot/exts/utilities/pythonfacts.py +++ b/bot/exts/utilities/pythonfacts.py @@ -31,6 +31,6 @@ class PythonFacts(commands.Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the PythonFacts Cog.""" - bot.add_cog(PythonFacts()) + await bot.add_cog(PythonFacts()) diff --git a/bot/exts/utilities/realpython.py b/bot/exts/utilities/realpython.py index 5e9757d0..46b02866 100644 --- a/bot/exts/utilities/realpython.py +++ b/bot/exts/utilities/realpython.py @@ -94,6 +94,6 @@ class RealPython(commands.Cog): await ctx.send(embed=article_embed) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Real Python Cog.""" - bot.add_cog(RealPython(bot)) + await bot.add_cog(RealPython(bot)) diff --git a/bot/exts/utilities/reddit.py b/bot/exts/utilities/reddit.py index 782583d2..028c16bc 100644 --- a/bot/exts/utilities/reddit.py +++ b/bot/exts/utilities/reddit.py @@ -15,7 +15,6 @@ from discord.utils import escape_markdown, sleep_until from bot.bot import Bot from bot.constants import Channels, ERROR_REPLIES, Emojis, Reddit as RedditConfig, STAFF_ROLES from bot.utils.converters import Subreddit -from bot.utils.extensions import invoke_help_command from bot.utils.messages import sub_clyde from bot.utils.pagination import ImagePaginator, LinePaginator @@ -39,20 +38,17 @@ class Reddit(Cog): self.access_token = None self.client_auth = BasicAuth(RedditConfig.client_id, RedditConfig.secret) - bot.loop.create_task(self.init_reddit_ready()) self.auto_poster_loop.start() - def cog_unload(self) -> None: + async def cog_unload(self) -> None: """Stop the loop task and revoke the access token when the cog is unloaded.""" self.auto_poster_loop.cancel() if self.access_token and self.access_token.expires_at > datetime.utcnow(): asyncio.create_task(self.revoke_access_token()) - async def init_reddit_ready(self) -> None: + async def cog_load(self) -> None: """Sets the reddit webhook when the cog is loaded.""" - await self.bot.wait_until_guild_available() - if not self.webhook: - self.webhook = await self.bot.fetch_webhook(RedditConfig.webhook) + self.webhook = await self.bot.fetch_webhook(RedditConfig.webhook) @property def channel(self) -> TextChannel: @@ -258,7 +254,6 @@ class Reddit(Cog): await sleep_until(midnight_tomorrow) - await self.bot.wait_until_guild_available() if not self.webhook: await self.bot.fetch_webhook(RedditConfig.webhook) @@ -302,7 +297,7 @@ class Reddit(Cog): @group(name="reddit", invoke_without_command=True) async def reddit_group(self, ctx: Context) -> None: """View the top posts from various subreddits.""" - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) @reddit_group.command(name="top") async def top_command(self, ctx: Context, subreddit: Subreddit = "r/Python") -> None: @@ -360,9 +355,9 @@ class Reddit(Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Reddit cog.""" if not RedditConfig.secret or not RedditConfig.client_id: log.error("Credentials not provided, cog not loaded.") return - bot.add_cog(Reddit(bot)) + await bot.add_cog(Reddit(bot)) diff --git a/bot/exts/utilities/stackoverflow.py b/bot/exts/utilities/stackoverflow.py index 64455e33..b248e83f 100644 --- a/bot/exts/utilities/stackoverflow.py +++ b/bot/exts/utilities/stackoverflow.py @@ -83,6 +83,6 @@ class Stackoverflow(commands.Cog): await ctx.send(embed=search_query_too_long) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Stackoverflow Cog.""" - bot.add_cog(Stackoverflow(bot)) + await bot.add_cog(Stackoverflow(bot)) diff --git a/bot/exts/utilities/timed.py b/bot/exts/utilities/timed.py index 2ea6b419..d419dd08 100644 --- a/bot/exts/utilities/timed.py +++ b/bot/exts/utilities/timed.py @@ -43,6 +43,6 @@ class TimedCommands(commands.Cog): await ctx.send(f"Command execution for `{new_ctx.command}` finished in {(t_end - t_start):.4f} seconds.") -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Timed cog.""" - bot.add_cog(TimedCommands()) + await bot.add_cog(TimedCommands()) diff --git a/bot/exts/utilities/twemoji.py b/bot/exts/utilities/twemoji.py index c915f05b..25a03d25 100644 --- a/bot/exts/utilities/twemoji.py +++ b/bot/exts/utilities/twemoji.py @@ -4,12 +4,11 @@ from typing import Literal, Optional import discord from discord.ext import commands -from emoji import UNICODE_EMOJI_ENGLISH, is_emoji +from emoji import EMOJI_DATA, is_emoji from bot.bot import Bot from bot.constants import Colours, Roles from bot.utils.decorators import whitelist_override -from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) BASE_URLS = { @@ -50,7 +49,7 @@ class Twemoji(commands.Cog): emoji = "".join(Twemoji.emoji(e) or "" for e in codepoint.split("-")) embed = discord.Embed( - title=Twemoji.alias_to_name(UNICODE_EMOJI_ENGLISH[emoji]), + title=Twemoji.alias_to_name(EMOJI_DATA[emoji]["en"]), description=f"{codepoint.replace('-', ' ')}\n[Download svg]({Twemoji.get_url(codepoint, 'svg')})", colour=Colours.twitter_blue, ) @@ -133,7 +132,7 @@ class Twemoji(commands.Cog): async def twemoji(self, ctx: commands.Context, *raw_emoji: str) -> None: """Sends a preview of a given Twemoji, specified by codepoint or emoji.""" if len(raw_emoji) == 0: - await invoke_help_command(ctx) + await self.bot.invoke_help_command(ctx) return try: codepoint = self.codepoint_from_input(raw_emoji) @@ -145,6 +144,6 @@ class Twemoji(commands.Cog): await ctx.send(embed=self.build_embed(codepoint)) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Twemoji cog.""" - bot.add_cog(Twemoji(bot)) + await bot.add_cog(Twemoji(bot)) diff --git a/bot/exts/utilities/wikipedia.py b/bot/exts/utilities/wikipedia.py index e5e8e289..d87982c9 100644 --- a/bot/exts/utilities/wikipedia.py +++ b/bot/exts/utilities/wikipedia.py @@ -93,6 +93,6 @@ class WikipediaSearch(commands.Cog): ) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the WikipediaSearch cog.""" - bot.add_cog(WikipediaSearch(bot)) + await bot.add_cog(WikipediaSearch(bot)) diff --git a/bot/exts/utilities/wolfram.py b/bot/exts/utilities/wolfram.py index 9a26e545..a2f1228a 100644 --- a/bot/exts/utilities/wolfram.py +++ b/bot/exts/utilities/wolfram.py @@ -202,6 +202,13 @@ class Wolfram(Cog): message = "Wolfram API key is invalid or missing." footer = "" color = Colours.soft_red + elif status != 200: + # Handle all other possible status codes here + message = f"Unexpected status code from Wolfram API: {status}" + footer = "" + color = Colours.soft_red + + log.warning(f"Unexpected status code from Wolfram API: {status}\nInput: {query}") else: message = "" footer = "View original for a bigger picture." @@ -281,6 +288,12 @@ class Wolfram(Cog): elif response_text == "Error 1: Invalid appid.": message = "Wolfram API key is invalid or missing." color = Colours.soft_red + elif status != 200: + # Handle all other possible status codes here + message = f"Unexpected status code from Wolfram API: {status}" + color = Colours.soft_red + + log.warning(f"Unexpected status code from Wolfram API: {status}\nInput: {query}") else: message = response_text color = Colours.soft_orange @@ -288,6 +301,6 @@ class Wolfram(Cog): await send_embed(ctx, message, color) -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the Wolfram cog.""" - bot.add_cog(Wolfram(bot)) + await bot.add_cog(Wolfram(bot)) diff --git a/bot/exts/utilities/wtf_python.py b/bot/exts/utilities/wtf_python.py index 980b3dba..0c0375cb 100644 --- a/bot/exts/utilities/wtf_python.py +++ b/bot/exts/utilities/wtf_python.py @@ -78,7 +78,7 @@ class WTFPython(commands.Cog): match, certainty, _ = rapidfuzz.process.extractOne(query, self.headers.keys()) return match if certainty > MINIMUM_CERTAINTY else None - @commands.command(aliases=("wtf", "WTF")) + @commands.command(aliases=("wtf",)) async def wtf_python(self, ctx: commands.Context, *, query: Optional[str] = None) -> None: """ Search WTF Python repository. @@ -133,6 +133,6 @@ class WTFPython(commands.Cog): self.fetch_readme.cancel() -def setup(bot: Bot) -> None: +async def setup(bot: Bot) -> None: """Load the WTFPython Cog.""" - bot.add_cog(WTFPython(bot)) + await bot.add_cog(WTFPython(bot)) |