async def anonsend_fetch(self, ctx): """Get list of pending files""" url_base = self.config.get( "url") + "/api.php?apikey=" + self.config.get("apikey") response = requests.get(url_base + "&action=list") files = response.json() embed = self.embed(ctx=ctx, title=self.text.get("fetch_server")) for i, (file, timestamp) in enumerate(files.items()): uploaded = datetime.datetime.fromtimestamp(timestamp) embed.add_field( name=file, value=uploaded.strftime("%Y-%m-%d %H:%M:%S") + "\n" + self.text.get( "fetch_age", time=utils.seconds2str( (datetime.datetime.now() - uploaded).seconds), ), ) if i % 24 == 0 and i > 0: await ctx.send(embed=embed) await ctx.send(embed=embed)
async def _send_exception_message(self, ctx: commands.Context, error: Exception) -> bool: """Return True if error should be thrown""" # fmt: off if isinstance(error, acl_repo.ACLException): await self.output.error( ctx, self.text.get("acl", type(error).__name__, error=str(error))) return False # cog exceptions are handled in their cogs if isinstance(error, rubbercog.RubbercogException): if type(error) is not rubbercog.RubbercogException: return False await self.output.error(ctx, self.text.get("RubbercogException"), error) return False if type(error) == commands.CommandNotFound: return # Exceptions with parameters if type(error) == commands.MissingRequiredArgument: await self.output.warning( ctx, self.text.get("MissingRequiredArgument", param=error.param.name)) return False if type(error) == commands.CommandOnCooldown: time = utils.seconds2str(error.retry_after) await self.output.warning( ctx, self.text.get("CommandOnCooldown", time=time)) return False if type(error) == commands.MaxConcurrencyReached: await self.output.warning( ctx, self.text.get("MaxConcurrencyReached", num=error.number, per=error.per.name)) return False if type(error) == commands.MissingRole: # TODO Is !r OK, or should we use error.missing_role.name? role = f"`{error.missing_role!r}`" await self.output.warning(ctx, self.text.get("MissingRole", role=role)) return False if type(error) == commands.BotMissingRole: role = f"`{error.missing_role!r}`" await self.output.error(ctx, self.text.get("BotMissingRole", role=role)) return False if type(error) == commands.MissingAnyRole: roles = ", ".join(f"`{r!r}`" for r in error.missing_roles) await self.output.warning( ctx, self.text.get("MissingAnyRole", roles=roles)) return False if type(error) == commands.BotMissingAnyRole: roles = ", ".join(f"`{r!r}`" for r in error.missing_roles) await self.output.error( ctx, self.text.get("BotMissingAnyRole", roles=roles)) return False if type(error) == commands.MissingPermissions: perms = ", ".join(f"`{p}`" for p in error.missing_perms) await self.output.warning( ctx, self.text.get("MissingPermissions", perms=perms)) return False if type(error) == commands.BotMissingPermissions: perms = ", ".join(f"`{p}`" for p in error.missing_perms) await self.output.error( ctx, self.text.get("BotMissingPermissions", perms=perms)) return False if type(error) == commands.BadUnionArgument: await self.output.warning( ctx, self.text.get("BadUnionArgument", param=error.param.name)) return False if type(error) == commands.BadBoolArgument: await self.output.warning( ctx, self.text.get("BadBoolArgument", arg=error.argument)) return False # All cog-related errors if isinstance(error, smtplib.SMTPException): await self.console.error(ctx, "Could not send e-mail", error) await ctx.send( self.text.get("SMTPException", name=type(error).__name__)) return False if type(error) == commands.ExtensionFailed: await self.output.error( ctx, self.text.get( type(error).__name__, extension=f"{error.name!r}", error_name=error.original.__class__.__name__, error=str(error.original), )) return False if isinstance(error, commands.ExtensionError): await self.output.critical( ctx, self.text.get(type(error).__name__, extension=f"{error.name!r}")) return False # The rest of client exceptions if isinstance(error, commands.CommandError) or isinstance( error, discord.ClientException): await self.output.warning(ctx, self.text.get(type(error).__name__)) return False # DiscordException, non-critical errors if type(error) in ( discord.errors.NoMoreItems, discord.errors.HTTPException, discord.errors.Forbidden, discord.errors.NotFound, ): await self.output.error(ctx, self.text.get(type(error).__name__)) await self.console.error(ctx, type(error).__name__, error) return False # DiscordException, critical errors if type(error) in (discord.errors.DiscordException, discord.errors.GatewayNotFound): await self.output.error(ctx, self.text.get(type(error).__name__)) # Database if isinstance(error, sqlalchemy.exc.SQLAlchemyError): error_name = ".".join( [type(error).__module__, type(error).__name__]) await self.output.critical(ctx, error_name) await self.console.critical(ctx, "Database error", error) await self.event.user( ctx, f"Database reported`{error_name}`. The session may be invalidated <@{config.admin_id}>", escape_markdown=False, ) return False # fmt: on return True
async def on_command_error(self, ctx: commands.Context, error): # noqa: C901 """Handle errors""" if hasattr(ctx.command, "on_error") or hasattr(ctx.command, "on_command_error"): return error = getattr(error, "original", error) # TODO Implement all exceptions # https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#exceptions # fmt: off # cog exceptions are handled in their cogs if isinstance(error, rubbercog.RubbercogException): if type(error) is not rubbercog.RubbercogException: return return await self.output.error( ctx, text.get("bot", "RubbercogException"), error) # user interaction if isinstance(error, commands.MissingPermissions): perms = ", ".join(f"_{x}_" for x in error.missing_perms) return await self.output.error( ctx, text.fill("error", "no user permission", permissions=perms)) if isinstance(error, commands.BotMissingPermissions): perms = ", ".join(f"_{x}_" for x in error.missing_perms) await self.output.error( ctx, text.fill("error", "no bot permission", permissions=perms)) await self.console.error(ctx, "I'm missing permissions: " + perms) return if isinstance(error, commands.CommandOnCooldown): time = utils.seconds2str(error.retry_after) return await self.output.warning( ctx, text.fill("error", "cooldown", time=time)) if isinstance(error, commands.MaxConcurrencyReached): return await self.output.warning( ctx, text.fill("error", "concurrency", number=error.number, bucket_type=error.per.name)) if isinstance(error, commands.NSFWChannelRequired): return await self.output.error("error", "nsfw required") if isinstance(error, commands.CheckFailure): # Should we send _which_ checks failed? return await self.output.warning( ctx, text.get("error", "no requirements")) if isinstance(error, commands.BadArgument): return await self.output.warning(ctx, text.get("error", "bad argument")) if isinstance(error, commands.ExpectedClosingQuoteError): return await self.output.warning(ctx, text.get("error", "bad argument")) if isinstance(error, commands.CommandNotFound): return if isinstance(error, commands.MissingRequiredArgument): return await self.output.warning( ctx, text.fill("error", "missing argument", argument=error.param.name)) if isinstance(error, commands.ArgumentParsingError): return await self.output.warning( ctx, text.get("error", "argument parsing")) if isinstance(error, commands.CommandError): return await self.output.warning(ctx, text.get("error" "command"), error) # cog loading elif isinstance(error, commands.ExtensionAlreadyLoaded): return await self.output.error( ctx, text.get("error", "extension loaded")) elif isinstance(error, commands.ExtensionNotLoaded): return await self.output.error( ctx, text.get("error", "extension not loaded")) elif isinstance(error, commands.ExtensionFailed): return await self.output.error( ctx, text.get("error", "extenson failed"), error) elif isinstance(error, commands.ExtensionNotFound): return await self.output.error( ctx, text.get("error", "extension not found")) elif isinstance(error, commands.ExtensionError): return await self.output.error(ctx, text.get("error", "extension"), error) # fmt: on # display error message await self.output.error(ctx, "", error) if isinstance(ctx.channel, discord.TextChannel): location = f"{ctx.guild.name}/{ctx.channel.name} ({ctx.channel.id})" else: location = type(ctx.channel).__name__ output = "{command} by {user} in {location}:\n".format( command=config.prefix + self.sanitise(ctx.invoked_with), user=str(ctx.author), location=location, ) output += "".join( traceback.format_exception(type(error), error, error.__traceback__)) # print traceback to stdout print(output) # send traceback to dedicated channel channel_stdout = self.bot.get_channel(config.get("channels", "stdout")) output = list(output[0 + i:1960 + i] for i in range(0, len(output), 1960)) sent = [] for message in output: m = await channel_stdout.send("```\n{}```".format(message)) sent.append(m) # send notification to botdev channel_botdev = self.bot.get_channel(config.channel_botdev) embed = self.embed(ctx=ctx, color=discord.Color.from_rgb(255, 0, 0)) # fmt: off footer = "{user} in {channel}".format( user=ctx.author, channel=ctx.channel.name if isinstance(ctx.channel, discord.TextChannel) else type( ctx.channel).__name__, ) stack = output[-1] if len(stack) > 255: stack = "…" + stack[-255:] embed.set_footer(text=footer, icon_url=embed.footer.icon_url) embed.add_field( name=type(error).__name__, value=f"```{stack}```", inline=False, ) embed.add_field(name=f"Traceback link", value=sent[0].jump_url, inline=False) await channel_botdev.send(embed=embed)