diff options
Diffstat (limited to 'bot/seasons/evergreen/error_handler.py')
-rw-r--r-- | bot/seasons/evergreen/error_handler.py | 225 |
1 files changed, 106 insertions, 119 deletions
diff --git a/bot/seasons/evergreen/error_handler.py b/bot/seasons/evergreen/error_handler.py index 120462ee..cf6dc087 100644 --- a/bot/seasons/evergreen/error_handler.py +++ b/bot/seasons/evergreen/error_handler.py @@ -1,119 +1,106 @@ -import logging
-import math
-import random
-import sys
-import traceback
-
-from discord import Colour, Embed, Message
-from discord.ext import commands
-
-from bot.constants import NEGATIVE_REPLIES
-from bot.decorators import InChannelCheckFailure
-
-log = logging.getLogger(__name__)
-
-
-class CommandErrorHandler(commands.Cog):
- """A error handler for the PythonDiscord server."""
-
- def __init__(self, bot: commands.Bot):
- self.bot = bot
-
- @staticmethod
- def revert_cooldown_counter(command: commands.Command, message: Message) -> None:
- """Undoes the last cooldown counter for user-error cases."""
- if command._buckets.valid:
- bucket = command._buckets.get_bucket(message)
- bucket._tokens = min(bucket.rate, bucket._tokens + 1)
- logging.debug(
- "Cooldown counter reverted as the command was not used correctly."
- )
-
- @commands.Cog.listener()
- async def on_command_error(self, ctx: commands.Context, error: commands.CommandError) -> None:
- """Activates when a command opens an error."""
- if hasattr(ctx.command, 'on_error'):
- return logging.debug(
- "A command error occured but the command had it's own error handler."
- )
-
- error = getattr(error, 'original', error)
-
- if isinstance(error, InChannelCheckFailure):
- logging.debug(
- f"{ctx.author} the command '{ctx.command}', but they did not have "
- f"permissions to run commands in the channel {ctx.channel}!"
- )
- embed = Embed(colour=Colour.red())
- embed.title = random.choice(NEGATIVE_REPLIES)
- embed.description = str(error)
- return await ctx.send(embed=embed)
-
- if isinstance(error, commands.CommandNotFound):
- return logging.debug(
- f"{ctx.author} called '{ctx.message.content}' but no command was found."
- )
-
- if isinstance(error, commands.UserInputError):
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' but entered invalid input!"
- )
-
- self.revert_cooldown_counter(ctx.command, ctx.message)
-
- return await ctx.send(
- ":no_entry: The command you specified failed to run. "
- "This is because the arguments you provided were invalid."
- )
-
- if isinstance(error, commands.CommandOnCooldown):
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' but they were on cooldown!"
- )
- remaining_minutes, remaining_seconds = divmod(error.retry_after, 60)
-
- return await ctx.send(
- "This command is on cooldown, please retry in "
- f"{int(remaining_minutes)} minutes {math.ceil(remaining_seconds)} seconds."
- )
-
- if isinstance(error, commands.DisabledCommand):
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' but the command was disabled!"
- )
- return await ctx.send(":no_entry: This command has been disabled.")
-
- if isinstance(error, commands.NoPrivateMessage):
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "in a private message however the command was guild only!"
- )
- return await ctx.author.send(":no_entry: This command can only be used in the server.")
-
- if isinstance(error, commands.BadArgument):
- self.revert_cooldown_counter(ctx.command, ctx.message)
-
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' but entered a bad argument!"
- )
- return await ctx.send("The argument you provided was invalid.")
-
- if isinstance(error, commands.CheckFailure):
- logging.debug(f"{ctx.author} called the command '{ctx.command}' but the checks failed!")
- return await ctx.send(":no_entry: You are not authorized to use this command.")
-
- print(f"Ignoring exception in command {ctx.command}:", file=sys.stderr)
-
- logging.warning(
- f"{ctx.author} called the command '{ctx.command}' "
- "however the command failed to run with the error:"
- f"-------------\n{error}"
- )
-
- traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
-
-
-def setup(bot: commands.Bot) -> None:
- """Error handler Cog load."""
- bot.add_cog(CommandErrorHandler(bot))
- log.info("CommandErrorHandler cog loaded")
+import logging +import math +import random +from typing import Iterable, Union + +from discord import Embed, Message +from discord.ext import commands + +from bot.constants import Colours, ERROR_REPLIES, NEGATIVE_REPLIES +from bot.decorators import InChannelCheckFailure + +log = logging.getLogger(__name__) + + +class CommandErrorHandler(commands.Cog): + """A error handler for the PythonDiscord server.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @staticmethod + def revert_cooldown_counter(command: commands.Command, message: Message) -> None: + """Undoes the last cooldown counter for user-error cases.""" + if command._buckets.valid: + bucket = command._buckets.get_bucket(message) + bucket._tokens = min(bucket.rate, bucket._tokens + 1) + logging.debug("Cooldown counter reverted as the command was not used correctly.") + + @staticmethod + def error_embed(message: str, title: Union[Iterable, str] = ERROR_REPLIES) -> Embed: + """Build a basic embed with red colour and either a random error title or a title provided.""" + embed = Embed(colour=Colours.soft_red) + if isinstance(title, str): + embed.title = title + else: + embed.title = random.choice(title) + embed.description = message + return embed + + @commands.Cog.listener() + async def on_command_error(self, ctx: commands.Context, error: commands.CommandError) -> None: + """Activates when a command opens an error.""" + if hasattr(ctx.command, 'on_error'): + logging.debug("A command error occured but the command had it's own error handler.") + return + + error = getattr(error, 'original', error) + logging.debug( + f"Error Encountered: {type(error).__name__} - {str(error)}, " + f"Command: {ctx.command}, " + f"Author: {ctx.author}, " + f"Channel: {ctx.channel}" + ) + + if isinstance(error, commands.CommandNotFound): + return + + if isinstance(error, InChannelCheckFailure): + await ctx.send(embed=self.error_embed(str(error), NEGATIVE_REPLIES)) + return + + if isinstance(error, commands.UserInputError): + self.revert_cooldown_counter(ctx.command, ctx.message) + embed = self.error_embed( + f"Your input was invalid: {error}\n\nUsage:\n```{ctx.prefix}{ctx.command} {ctx.command.signature}```" + ) + await ctx.send(embed=embed) + return + + if isinstance(error, commands.CommandOnCooldown): + mins, secs = divmod(math.ceil(error.retry_after), 60) + embed = self.error_embed( + f"This command is on cooldown:\nPlease retry in {mins} minutes {secs} seconds.", + NEGATIVE_REPLIES + ) + await ctx.send(embed=embed) + return + + if isinstance(error, commands.DisabledCommand): + await ctx.send(embed=self.error_embed("This command has been disabled.", NEGATIVE_REPLIES)) + return + + if isinstance(error, commands.NoPrivateMessage): + await ctx.send(embed=self.error_embed("This command can only be used in the server.", NEGATIVE_REPLIES)) + return + + if isinstance(error, commands.BadArgument): + self.revert_cooldown_counter(ctx.command, ctx.message) + embed = self.error_embed( + "The argument you provided was invalid: " + f"{error}\n\nUsage:\n```{ctx.prefix}{ctx.command} {ctx.command.signature}```" + ) + await ctx.send(embed=embed) + return + + if isinstance(error, commands.CheckFailure): + await ctx.send(embed=self.error_embed("You are not authorized to use this command.", NEGATIVE_REPLIES)) + return + + log.exception(f"Unhandled command error: {str(error)}") + + +def setup(bot: commands.Bot) -> None: + """Error handler Cog load.""" + bot.add_cog(CommandErrorHandler(bot)) + log.info("CommandErrorHandler cog loaded") |