mirror of
				https://gitlab.com/ddorn/tfjm-discord-bot.git
				synced 2025-10-29 23:39:52 +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()) | bot = commands.Bot(("! ", "!"), help_command=TfjmHelpCommand()) | ||||||
|  |  | ||||||
| # Variable globale qui contient les tirages. | # 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 = {} | tirages = {} | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -26,46 +28,12 @@ async def on_ready(): | |||||||
|     print(f"{bot.user} has connected to Discord!") |     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.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.dev") | ||||||
|  | bot.load_extension("src.cogs.errors") | ||||||
| bot.load_extension("src.cogs.misc") | bot.load_extension("src.cogs.misc") | ||||||
|  | bot.load_extension("src.cogs.teams") | ||||||
|  | bot.load_extension("src.cogs.tirages") | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user