diff --git a/src/main.py b/src/main.py index 5a33342..75e546a 100644 --- a/src/main.py +++ b/src/main.py @@ -2,6 +2,7 @@ import pathlib import shutil +import traceback import os import asyncio @@ -78,6 +79,22 @@ async def on_command_error(self, await util.discord.reply(context, embed=embed) return + # all other commands + # code from https://stackoverflow.com/a/73706008/15436169 + user = await util.discord.get_bot().fetch_user(util.discord.get_user_id(context)) + full_error = traceback.format_exception(exception) + msg_text = f"User {user} used command {util.discord.get_command_name(context)}:\n" \ + + f"```py\n{''.join(full_error)}\n```" + await util.discord.private_message(util.discord.get_owner_id(), + msg_text) + embed = discord.Embed(title="An error occured!", + color=discord.Color.red()) + message = await util.discord.get_reply(context) + if message is not None: + await message.edit(embed=embed) + else: + await util.discord.reply(context, embed=embed) + return await super().on_command_error(context, exception) def send_to_channel(self, channel_id: int, text: str) -> discord.Message: diff --git a/src/nikobot/util/discord.py b/src/nikobot/util/discord.py index fd98d4b..8307461 100644 --- a/src/nikobot/util/discord.py +++ b/src/nikobot/util/discord.py @@ -12,6 +12,14 @@ from . import error from .storage import VolatileStorage +def get_command_name(ctx: commands.context.Context | discord.interactions.Interaction) -> str: + """Return the full name of the contexts' command""" + + if not is_slash_command(ctx): + return ctx.command.qualified_name + + return ctx.command.qualified_name + def get_bot() -> commands.Bot: """Return the ``DiscordBot`` instance""" @@ -25,6 +33,27 @@ def get_bot() -> commands.Bot: return bot +def get_owner_id() -> int: + """Return the discord bot owners' user_id""" + + return 650587171375284226 + +async def get_reply(ctx: commands.context.Context | discord.interactions.Interaction) \ + -> discord.Message | discord.interactions.InteractionMessage | None: + """Return the reply message for the given context, or None if the bot didn't reply yet""" + + if not is_slash_command(ctx): + ctx: commands.context.Context = ctx + async for message in ctx.channel.history(limit=100): + if message.reference is not None and message.reference.message_id == ctx.message.id: + return message + return None + + if not ctx.response.is_done(): + return None + + return await ctx.original_response() + def get_user_id(ctx: commands.context.Context | discord.interactions.Interaction) -> int: """Get discords user id from the message's sender""" @@ -193,7 +222,7 @@ async def wrapper(*args, **kwargs): return wrapper return decorator -def is_slash_command(ctx: commands.context.Context | discord.interactions.Interaction): +def is_slash_command(ctx: commands.context.Context | discord.interactions.Interaction) -> bool: """Checks whether the ``ctx`` was received from a 'normal' text command or a slash command""" # for slash commands