async def on_error_notes(self, exc, ctx): cmd_string = message_log_str(ctx.message) if ctx is not None and ctx.command is not None: usage_str = get_usage_str(ctx) else: usage_str = '(Unable to retrieve usage information)' if isinstance(exc, commands.BadArgument): msg = "Bad argument passed in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message( ctx.message.channel, ctx.message.author.mention + (" Invalid parameter(s). Did you mean `.notes add`?" "\n\n**Usage:** `{}`\n\nUse `{}` for help.").format( usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.TooManyArguments): msg = "Too many arguments passed in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message( ctx.message.channel, ctx.message.author.mention + (" Too many parameters. Did you mean `.notes add`?\n\n" "**Usage:** `{}`\n\nUse `{}` for help.").format( usage_str, get_help_str(ctx))) else: await self.on_error_query_user(exc, ctx)
async def spotlight(self, ctx): """ Manages the World Spotlight event. Users: see `.help spotlight join`. """ command_list = list(self.spotlight.commands.keys()) await self.bot.say(('Invalid sub-command. Valid sub-commands are {0!s}. ' 'Use `{1}` or `{1} <subcommand>` for instructions.') .format(command_list, get_help_str(ctx)))
async def queue(self, ctx): """ [MOD ONLY] The `.spotlight queue` sub-command contains sub-sub-commands that let moderators manage a queue of upcoming spotlights. """ command_list = list(self.spotlight.commands.keys()) await self.bot.say(('Invalid sub-command. Valid sub-commands are {0}. ' 'Use `{1}` or `{1} <subcommand>` for instructions.') .format(command_list, get_help_str(ctx)))
async def word_filter(self, ctx): """ [MOD ONLY] Manages the filter lists. This feature is used to notify moderators of keywords and phrases in messages, and optionally auto-delete them. All commands permit single-letter mnemonics for convenience, e.g. `.filter l` is equivalent to `.filter list`. """ command_list = list(self.word_filter.commands.keys()) await self.bot.say( ('Invalid sub-command. Valid sub-commands are {0!s}. ' 'Use `{1}` or `{1} <subcommand>` for instructions.').format( command_list, get_help_str(ctx)))
async def filter_add_error(self, exc, ctx: commands.Context): cmd_string = message_log_str(ctx.message) if isinstance(exc, commands.TooManyArguments): msg = "Too many arguments passed in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, ( "Too many arguments.\n\n**Usage:** `{} <warn|del> \"<filter text>\"`\n\n" "Did you forget quotation marks around the filter text? " "Use `{}` for instructions.").format(get_command_str(ctx), get_help_str(ctx))) else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True ) # Other errors can bubble up
async def on_command_error(self, exc, ctx, force=False): """ Handles all command errors (see the ``discord.ext.commands.errors`` module). This method will do nothing if a command is detected to have an error handler ("on_error"); if you want on_command_error's default behaviour to take over, within a command error handler, you can call this method and pass ``force=True``. If you define custom command error handlers, note that CommandInvokeError is the one you want to handle for arbitrary errors (i.e. any exception raised that isn't derived from CommandError will cause discord.py to raise a CommandInvokeError from it). """ cmd_string = message_log_str(ctx.message) author_mention = ctx.message.author.mention + ' ' if not force and hasattr(ctx.command, "on_error"): return if ctx is not None and ctx.command is not None: usage_str = get_usage_str(ctx) else: usage_str = '(Unable to retrieve usage information)' if isinstance(exc, DeleteMessage): try: await self.bot.delete_message(exc.message) logger.info("on_command_error: Deleted invoking message") except discord.errors.DiscordException: logger.exception("Can't delete invoking message!") exc = exc.cause # and continue on to handle the cause of the DeleteMessage... if isinstance(exc, commands.CommandOnCooldown): await self.bot.send_message(ctx.message.channel, author_mention + "`{}` is on cooldown! Try again in {:.0f} seconds." .format(get_command_str(ctx), max(exc.retry_after, 1.0))) elif isinstance(exc, commands.CommandInvokeError): root_exc = exc.__cause__ if exc.__cause__ is not None else exc if isinstance(root_exc, KeyboardInterrupt): logger.warning("Interrupted by user (SIGINT)") raise root_exc elif isinstance(root_exc, discord.HTTPException): # API errors err_msg = 'While executing {c}\n\nDiscord API error {e!s}' \ .format(c=cmd_string, e=root_exc) logger.error(err_msg + "\n\n{}".format(tb_log_str(root_exc))) await self.send_output( "[ERROR] " + err_msg + "\n\nSee log for details") else: logger.error("An error occurred while processing the command: {}\n\n{}" .format(cmd_string, tb_log_str(root_exc))) await self.send_output( "[ERROR] While executing {}\n\n{}\n\nSee logs for details" .format(cmd_string, exc_log_str(root_exc))) # In all cases (except if return early/re-raise) await self.bot.send_message(ctx.message.channel, author_mention + "An error occurred! Details have been logged. Let a mod know so we can " "investigate.") elif isinstance(exc, commands.DisabledCommand): msg = "Attempt to use disabled command: {}".format(cmd_string) logger.warning(msg) # No need to log this on Discord - not something mods need to be aware of # No need to inform user of this - prevents spam, "disabled" commands could just not # exist elif isinstance(exc, ModOnlyError): err_msg = "Unauthorised user for this command (not a moderator): {!r}".format( cmd_string) logger.warning(err_msg) await self.send_output('[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, author_mention + "Only mods can use that command.") elif isinstance(exc, AdminOnlyError): err_msg = "Unauthorised user for this command (not an admin): {!r}".format( cmd_string) logger.warning(err_msg) await self.send_output('[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, author_mention + "Only admins can use that command.") elif isinstance(exc, (UnauthorizedUserError, commands.CheckFailure)): logger.warning( "Check failed on command: {!r}\n\n{}".format(cmd_string, tb_log_str(exc))) await self.bot.send_message(ctx.message.channel, author_mention + "You're not allowed to use that command. " " *(Dev note: Implement error handler with more precise reason)*") elif isinstance(exc, UnauthorizedChannelError): err_msg = "Unauthorised channel for this command: {!r}".format( cmd_string) logger.warning(err_msg) await self.send_output('[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, author_mention + "You can't use that command here.") elif isinstance(exc, commands.NoPrivateMessage): msg = "Attempt to use non-PM command in PM: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, "Sorry, you can't use that command in PM.") # No need to log this on Discord, spammy and isn't something mods need to be aware of elif isinstance(exc, commands.BadArgument): exc_msg = exc.args[0] if len(exc.args) > 0 else '(No error message).' msg = "Bad argument passed in command: {}\n{}".format(cmd_string, exc_msg) logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + ("Invalid argument(s): {}\n\n**Usage:** `{}`\n\n" "Use `{}` for help.") .format(exc_msg, usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.TooManyArguments): msg = "Too many arguments passed in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + "Too many arguments.\n\n**Usage:** `{}`\n\nUse `{}` for help." .format(usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.MissingRequiredArgument): msg = "Missing required arguments in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + "Missing argument(s).\n\n**Usage:** `{}`\n\nUse `{}` for help." .format(usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, BotNotReady): try: cog_name = exc.args[0] except IndexError: cog_name = 'unknown' logger.warning("Attempted to use command while cog is not ready: {}".format(cmd_string)) await self.bot.send_message( ctx.message.channel, author_mention + "Sorry, I'm still loading the {} module! Try again in a few seconds." .format(cog_name) ) elif isinstance(exc, commands.CommandNotFound): msg = "Unknown command: {}".format(cmd_string) # safe to assume commands usually words - symbolic commands are rare # and we want to avoid emoticons ('._.', etc.), punctuation ('...') and decimal numbers # without leading 0 (.12) being detected if ctx.invoked_with and all(c.isalnum() for c in ctx.invoked_with) \ and not ctx.invoked_with[0].isdigit(): logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + "Sorry, I don't know the command `{}{.invoked_with}`" .format(get_command_prefix(ctx), ctx)) else: logger.exception("Unknown exception occurred") await self.bot.send_message(ctx.message.channel, author_mention + "An unexpected error occurred! Details have been logged. Let a mod know so we can " "investigate.") await self.send_output( ("[ERROR] Unknown error while trying to process command {}\n" "Error: {!s}\n\nSee logs for details").format(cmd_string, exc))