mirror of
				https://gitlab.com/ddorn/tfjm-discord-bot.git
				synced 2025-11-04 09:42:11 +01:00 
			
		
		
		
	♻️ more flexible error handling
This commit is contained in:
		@@ -0,0 +1,94 @@
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
 | 
			
		||||
import discord
 | 
			
		||||
from discord.ext.commands import *
 | 
			
		||||
from discord.utils import maybe_coroutine
 | 
			
		||||
 | 
			
		||||
from src.errors import UnwantedCommand
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Global variable and function because I'm too lazy to make a metaclass
 | 
			
		||||
handlers = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def handles(error_type):
 | 
			
		||||
    """
 | 
			
		||||
    This registers an error handler.
 | 
			
		||||
 | 
			
		||||
    Error handlers can be coroutines or functions.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def decorator(f):
 | 
			
		||||
        handlers[error_type] = f
 | 
			
		||||
        return f
 | 
			
		||||
 | 
			
		||||
    return decorator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ErrorsCog(Cog):
 | 
			
		||||
    """This cog defines all the handles for errors."""
 | 
			
		||||
 | 
			
		||||
    @Cog.listener()
 | 
			
		||||
    async def on_command_error(self, ctx: Context, error: CommandError):
 | 
			
		||||
        print(repr(error), file=sys.stderr)
 | 
			
		||||
 | 
			
		||||
        # We take the first superclass with an handler defined
 | 
			
		||||
        handler = None
 | 
			
		||||
        for type_ in error.__class__.__mro__:
 | 
			
		||||
            handler = handlers.get(type_)
 | 
			
		||||
            if handler:
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        if handler is None:
 | 
			
		||||
            # Default handling
 | 
			
		||||
            msg = repr(error)
 | 
			
		||||
        else:
 | 
			
		||||
            msg = await maybe_coroutine(handler, self, ctx, error)
 | 
			
		||||
 | 
			
		||||
        if msg:
 | 
			
		||||
            await ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
    @handles(UnwantedCommand)
 | 
			
		||||
    async def on_unwanted_command(self, ctx, error):
 | 
			
		||||
        await ctx.message.delete()
 | 
			
		||||
        author: discord.Message
 | 
			
		||||
        await ctx.author.send(
 | 
			
		||||
            "J'ai supprimé ton message:\n> "
 | 
			
		||||
            + ctx.message.clean_content
 | 
			
		||||
            + "\nC'est pas grave, c'est juste pour ne pas encombrer "
 | 
			
		||||
            "le chat lors du tirage."
 | 
			
		||||
        )
 | 
			
		||||
        await ctx.author.send("Raison: " + error.original.msg)
 | 
			
		||||
 | 
			
		||||
    @handles(CommandInvokeError)
 | 
			
		||||
    async def on_command_invoke_error(self, ctx, error):
 | 
			
		||||
        specific_handler = handlers.get(type(error.original))
 | 
			
		||||
 | 
			
		||||
        if specific_handler:
 | 
			
		||||
            return await specific_handler(ctx, error)
 | 
			
		||||
 | 
			
		||||
        traceback.print_tb(error.original.__traceback__, file=sys.stderr)
 | 
			
		||||
        return (
 | 
			
		||||
            error.original.__class__.__name__
 | 
			
		||||
            + ": "
 | 
			
		||||
            + (str(error.original) or str(error))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @handles(CommandNotFound)
 | 
			
		||||
    def on_command_not_found(self, ctx, error):
 | 
			
		||||
 | 
			
		||||
        # Here we just take advantage that the error is formatted this way:
 | 
			
		||||
        # 'Command "NAME" is not found'
 | 
			
		||||
        name = str(error).partition('"')[2].rpartition('"')[0]
 | 
			
		||||
        return f"La commande {name} n'éxiste pas. Pour une liste des commandes, envoie `!help`."
 | 
			
		||||
 | 
			
		||||
    @handles(MissingRole)
 | 
			
		||||
    def on_missing_role(self, ctx, error):
 | 
			
		||||
        return (
 | 
			
		||||
            f"Il te faut le role de {error.missing_role} pour utiliser cette commande."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup(bot):
 | 
			
		||||
    bot.add_cog(ErrorsCog())
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ from src.errors import TfjmError, UnwantedCommand
 | 
			
		||||
bot = commands.Bot(("! ", "!"), help_command=TfjmHelpCommand())
 | 
			
		||||
 | 
			
		||||
# Variable globale qui contient les tirages.
 | 
			
		||||
# We *want* it to be global so we can reload the tirages cog without
 | 
			
		||||
# removing all the running tirages
 | 
			
		||||
tirages = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -26,46 +28,12 @@ async def on_ready():
 | 
			
		||||
    print(f"{bot.user} has connected to Discord!")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bot.event
 | 
			
		||||
async def on_command_error(ctx: Context, error, *args, **kwargs):
 | 
			
		||||
    if isinstance(error, commands.CommandInvokeError):
 | 
			
		||||
        if isinstance(error.original, UnwantedCommand):
 | 
			
		||||
            await ctx.message.delete()
 | 
			
		||||
            author: discord.Message
 | 
			
		||||
            await ctx.author.send(
 | 
			
		||||
                "J'ai supprimé ton message:\n> "
 | 
			
		||||
                + ctx.message.clean_content
 | 
			
		||||
                + "\nC'est pas grave, c'est juste pour ne pas encombrer "
 | 
			
		||||
                "le chat lors du tirage."
 | 
			
		||||
            )
 | 
			
		||||
            await ctx.author.send("Raison: " + error.original.msg)
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
            msg = (
 | 
			
		||||
                error.original.__class__.__name__
 | 
			
		||||
                + ": "
 | 
			
		||||
                + (str(error.original) or str(error))
 | 
			
		||||
            )
 | 
			
		||||
            traceback.print_tb(error.original.__traceback__, file=sys.stderr)
 | 
			
		||||
    elif isinstance(error, commands.CommandNotFound):
 | 
			
		||||
        # Here we just take adventage that the error is formatted this way:
 | 
			
		||||
        # 'Command "NAME" is not found'
 | 
			
		||||
        name = str(error).partition('"')[2].rpartition('"')[0]
 | 
			
		||||
        msg = f"La commande {name} n'éxiste pas. Pour un liste des commandes, envoie `!help`."
 | 
			
		||||
    elif isinstance(error, commands.MissingRole):
 | 
			
		||||
        msg = f"Il te faut le role de {error.missing_role} pour utiliser cette commande"
 | 
			
		||||
    else:
 | 
			
		||||
        msg = repr(error)
 | 
			
		||||
 | 
			
		||||
    print(repr(error), dir(error), file=sys.stderr)
 | 
			
		||||
    await ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bot.remove_command("help")
 | 
			
		||||
bot.load_extension("src.cogs.tirages")
 | 
			
		||||
bot.load_extension("src.cogs.teams")
 | 
			
		||||
bot.load_extension("src.cogs.dev")
 | 
			
		||||
bot.load_extension("src.cogs.errors")
 | 
			
		||||
bot.load_extension("src.cogs.misc")
 | 
			
		||||
bot.load_extension("src.cogs.teams")
 | 
			
		||||
bot.load_extension("src.cogs.tirages")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user