From ae14b32ed349b000b5715b14f109e13c806e68e4 Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:31:13 -0700 Subject: [PATCH 1/5] Improve error msgs --- bot/libs/utils/embeds.py | 20 ++++++++++ bot/libs/utils/modal.py | 6 +-- bot/libs/utils/tree.py | 82 ++++++++++------------------------------ bot/libs/utils/view.py | 32 ++-------------- 4 files changed, 46 insertions(+), 94 deletions(-) diff --git a/bot/libs/utils/embeds.py b/bot/libs/utils/embeds.py index 6203dc6..d31dade 100644 --- a/bot/libs/utils/embeds.py +++ b/bot/libs/utils/embeds.py @@ -1,3 +1,5 @@ +import traceback + import discord @@ -30,6 +32,24 @@ def __init__(self, **kwargs): "Uh oh! It seems like the command ran into an issue! For support, please visit [Catherine-Chan's Support Server](https://discord.gg/ns3e74frqn) to get help!", ) super().__init__(**kwargs) + self.set_footer(text="Happened At") + self.timestamp = discord.utils.utcnow() + + +class FullErrorEmbed(ErrorEmbed): + def __init__(self, error: Exception, **kwargs): + kwargs.setdefault("description", self._format_description(error)) + super().__init__(**kwargs) + + def _format_description(self, error: Exception) -> str: + error_traceback = "\n".join(traceback.format_exception_only(type(error), error)) + desc = f""" + Uh oh! It seems like the command ran into an issue! For support, please visit [Catherine-Chan's Support Server](https://discord.gg/ns3e74frqn) to get help! + + **Error**: + ```{error_traceback}``` + """ + return desc class ConfirmEmbed(discord.Embed): diff --git a/bot/libs/utils/modal.py b/bot/libs/utils/modal.py index 0ffc213..d0d3345 100644 --- a/bot/libs/utils/modal.py +++ b/bot/libs/utils/modal.py @@ -3,7 +3,7 @@ import discord from discord.utils import utcnow -from .embeds import ErrorEmbed +from .embeds import ErrorEmbed, FullErrorEmbed NO_CONTROL_MSG = "This modal cannot be controlled by you, sorry!" @@ -39,7 +39,5 @@ async def interaction_check(self, interaction: discord.Interaction, /) -> bool: async def on_error( self, interaction: discord.Interaction, error: Exception, / ) -> None: - await interaction.response.send_message( - embed=produce_error_embed(error), ephemeral=True - ) + await interaction.response.send_message(embed=FullErrorEmbed(error)) self.stop() diff --git a/bot/libs/utils/tree.py b/bot/libs/utils/tree.py index 112fed3..578784d 100644 --- a/bot/libs/utils/tree.py +++ b/bot/libs/utils/tree.py @@ -1,63 +1,21 @@ from __future__ import annotations -import traceback -from typing import TYPE_CHECKING, List, Union +from typing import TYPE_CHECKING import discord -from discord.app_commands import ( - AppCommandError, - BotMissingPermissions, - CommandTree, - MissingPermissions, - NoPrivateMessage, -) -from discord.utils import utcnow +from discord import app_commands -from .embeds import ErrorEmbed +from .embeds import FullErrorEmbed if TYPE_CHECKING: from bot.catherinecore import Catherine -def _build_error_embed(error: AppCommandError) -> ErrorEmbed: - error_traceback = "\n".join(traceback.format_exception_only(type(error), error)) - embed = ErrorEmbed() - embed.description = f""" - Uh oh! It seems like the command ran into an issue! For support, please visit [Catherine-Chan's Support Server](https://discord.gg/ns3e74frqn) to get help! - - **Error**: - ```{error_traceback}``` - """ - embed.set_footer(text="Happened At") - embed.timestamp = utcnow() - return embed - - -def _build_premade_embed(title: str, description: str) -> ErrorEmbed: - embed = ErrorEmbed() - embed.title = title - embed.description = description - return embed - - -def _build_missing_perm_embed( - missing_perms: List[str], user: Union[discord.User, discord.Member] -) -> ErrorEmbed: - str_user = "Catherine-Chan is" if user.bot is True else "You are" - perms = ",".join(missing_perms).rstrip(",") - desc = f""" - {str_user} missing the following permissions in order to run the command: - - {perms} - """ - return _build_premade_embed("Missing Permissions", desc) - - # This is needed for the blacklisting system # Yes a custom CommandTree lol. # At the very least better than Jade's on_interaction checks # https://github.com/LilbabxJJ-1/PrideBot/blob/master/main.py#L19-L36 -class CatherineCommandTree(CommandTree): +class CatherineCommandTree(app_commands.CommandTree): async def interaction_check(self, interaction: discord.Interaction) -> bool: bot: Catherine = interaction.client # type: ignore # Pretty much returns the subclass anyways. I checked - Noelle @@ -93,22 +51,24 @@ async def interaction_check(self, interaction: discord.Interaction) -> bool: return True async def on_error( - self, interaction: discord.Interaction, error: AppCommandError + self, interaction: discord.Interaction, error: app_commands.AppCommandError ) -> None: - if isinstance(error, MissingPermissions) or isinstance( - error, BotMissingPermissions - ): - await interaction.response.send_message( - embed=_build_missing_perm_embed( - error.missing_permissions, interaction.user - ) + bot: Catherine = interaction.client # type: ignore + + if bot._dev_mode: + bot.logger.exception("Ignoring exception:", exc_info=error) + return + + if isinstance(error, app_commands.NoPrivateMessage): + await interaction.user.send( + "This command cannot be used in private messages" ) - elif isinstance(error, NoPrivateMessage): - await interaction.response.send_message( - embed=_build_premade_embed( - "DMs don't work", - "The command you are trying to run does not work in DMs.", + elif isinstance(error, app_commands.CommandInvokeError): + original = error.original + if not isinstance(original, discord.HTTPException): + bot.logger.exception("In %s: ", interaction.command.qualified_name, exc_info=original) # type: ignore + await interaction.response.send_message( + embed=FullErrorEmbed(error), ephemeral=True ) - ) else: - await interaction.response.send_message(embed=_build_error_embed(error)) + await interaction.response.send_message(embed=FullErrorEmbed(error)) diff --git a/bot/libs/utils/view.py b/bot/libs/utils/view.py index dc23f72..c7a6455 100644 --- a/bot/libs/utils/view.py +++ b/bot/libs/utils/view.py @@ -1,31 +1,13 @@ import asyncio -import traceback from typing import Any, Optional import discord -from discord.utils import utcnow -from .embeds import ErrorEmbed +from .embeds import FullErrorEmbed, TimeoutEmbed NO_CONTROL_MSG = "This view cannot be controlled by you, sorry!" -def make_error_embed(error: Exception, item: discord.ui.Item[Any]) -> ErrorEmbed: - error_traceback = "\n".join(traceback.format_exception_only(type(error), error)) - embed = ErrorEmbed() - embed.description = f""" - Uh oh! It seems like the view ran into an issue! For support, please visit [Catherine-Chan's Support Server](https://discord.gg/ns3e74frqn) to get help! - - **Item**: `{item.__class__.__name__}` - - **Error**: - ```{error_traceback}``` - """ - embed.set_footer(text="Happened At") - embed.timestamp = utcnow() - return embed - - class CatherineView(discord.ui.View): def __init__( self, @@ -38,12 +20,6 @@ def __init__( self.original_response: Optional[discord.InteractionMessage] self.triggered = asyncio.Event() - def build_timeout_embed(self) -> ErrorEmbed: - embed = ErrorEmbed() - embed.title = "\U00002757 Timed Out" - embed.description = "Timed out waiting for a response. Cancelling action." - return embed - async def interaction_check(self, interaction: discord.Interaction, /) -> bool: if interaction.user and interaction.user.id in ( self.interaction.client.application.owner.id, # type: ignore @@ -56,7 +32,7 @@ async def interaction_check(self, interaction: discord.Interaction, /) -> bool: async def on_timeout(self) -> None: if self.original_response: await self.original_response.edit( - embed=self.build_timeout_embed(), view=None, delete_after=15.0 + embed=TimeoutEmbed(), view=None, delete_after=15.0 ) async def on_error( @@ -66,7 +42,5 @@ async def on_error( item: discord.ui.Item[Any], /, ) -> None: - await interaction.response.send_message( - embed=make_error_embed(error, item), ephemeral=True - ) + await interaction.response.send_message(embed=FullErrorEmbed(error)) self.stop() From dbfc4746978dfc1a3f7954acc2c0ce9415bacd20 Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:32:54 -0700 Subject: [PATCH 2/5] update embeds listing --- bot/libs/utils/embeds.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bot/libs/utils/embeds.py b/bot/libs/utils/embeds.py index d31dade..d115890 100644 --- a/bot/libs/utils/embeds.py +++ b/bot/libs/utils/embeds.py @@ -52,15 +52,6 @@ def _format_description(self, error: Exception) -> str: return desc -class ConfirmEmbed(discord.Embed): - """Kumiko's custom confirm embed""" - - def __init__(self, **kwargs): - kwargs.setdefault("color", discord.Color.from_rgb(255, 191, 0)) - kwargs.setdefault("title", "Are you sure?") - super().__init__(**kwargs) - - class TimeoutEmbed(discord.Embed): """Timed out embed""" @@ -71,3 +62,12 @@ def __init__(self, **kwargs): "description", "Timed out waiting for a response. Cancelling action." ) super().__init__(**kwargs) + + +class ConfirmEmbed(discord.Embed): + """Kumiko's custom confirm embed""" + + def __init__(self, **kwargs): + kwargs.setdefault("color", discord.Color.from_rgb(255, 191, 0)) + kwargs.setdefault("title", "Are you sure?") + super().__init__(**kwargs) From ffa56de05766e8ccdf5eefe98603245f9f76f404 Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:35:23 -0700 Subject: [PATCH 3/5] revert changes --- bot/libs/utils/modal.py | 4 +++- bot/libs/utils/view.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/libs/utils/modal.py b/bot/libs/utils/modal.py index d0d3345..6aec05a 100644 --- a/bot/libs/utils/modal.py +++ b/bot/libs/utils/modal.py @@ -39,5 +39,7 @@ async def interaction_check(self, interaction: discord.Interaction, /) -> bool: async def on_error( self, interaction: discord.Interaction, error: Exception, / ) -> None: - await interaction.response.send_message(embed=FullErrorEmbed(error)) + await interaction.response.send_message( + embed=FullErrorEmbed(error), ephemeral=True + ) self.stop() diff --git a/bot/libs/utils/view.py b/bot/libs/utils/view.py index c7a6455..74645c0 100644 --- a/bot/libs/utils/view.py +++ b/bot/libs/utils/view.py @@ -42,5 +42,7 @@ async def on_error( item: discord.ui.Item[Any], /, ) -> None: - await interaction.response.send_message(embed=FullErrorEmbed(error)) + await interaction.response.send_message( + embed=FullErrorEmbed(error), ephemeral=True + ) self.stop() From 01cd6c9c5b41672642a7d49271631422f7be3e2e Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:40:32 -0700 Subject: [PATCH 4/5] fix some stuff --- bot/libs/ui/pride_profiles/views.py | 4 ++-- bot/libs/ui/tonetags/views.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/libs/ui/pride_profiles/views.py b/bot/libs/ui/pride_profiles/views.py index 280a870..9c5c434 100644 --- a/bot/libs/ui/pride_profiles/views.py +++ b/bot/libs/ui/pride_profiles/views.py @@ -1,7 +1,7 @@ import asyncpg import discord from libs.utils import CatherineView -from libs.utils.embeds import ErrorEmbed, SuccessEmbed +from libs.utils.embeds import ErrorEmbed, SuccessEmbed, TimeoutEmbed from .selects import SelectPrideCategory @@ -43,7 +43,7 @@ def build_register_embed(self, status: str) -> discord.Embed: async def on_timeout(self) -> None: if self.original_response and not self.triggered.is_set(): await self.original_response.edit( - embed=self.build_timeout_embed(), view=None, delete_after=15.0 + embed=TimeoutEmbed(), view=None, delete_after=15.0 ) @discord.ui.button( diff --git a/bot/libs/ui/tonetags/views.py b/bot/libs/ui/tonetags/views.py index 4485257..6157cee 100644 --- a/bot/libs/ui/tonetags/views.py +++ b/bot/libs/ui/tonetags/views.py @@ -5,7 +5,7 @@ import asyncpg import discord from libs.cog_utils.tonetags import parse_tonetag -from libs.utils import CatherineView, ErrorEmbed, SuccessEmbed +from libs.utils import CatherineView, ErrorEmbed, SuccessEmbed, TimeoutEmbed NO_CONTROL_MSG = "This menu cannot be controlled by you, sorry!" @@ -27,7 +27,7 @@ def __init__( async def on_timeout(self) -> None: if self.original_response and not self.triggered.is_set(): await self.original_response.edit( - embed=self.build_timeout_embed(), view=None, delete_after=15.0 + embed=TimeoutEmbed(), view=None, delete_after=15.0 ) @discord.ui.button( From be80ee333c88022b1f74de64ffa4bd39800f56fb Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:09:37 -0700 Subject: [PATCH 5/5] Include logging view and modal errors into stdlib --- bot/libs/utils/modal.py | 30 +++++++++++++----------------- bot/libs/utils/view.py | 11 ++++++++++- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/bot/libs/utils/modal.py b/bot/libs/utils/modal.py index 6aec05a..ad1c3e9 100644 --- a/bot/libs/utils/modal.py +++ b/bot/libs/utils/modal.py @@ -1,31 +1,22 @@ -import traceback +from __future__ import annotations -import discord -from discord.utils import utcnow +from typing import TYPE_CHECKING -from .embeds import ErrorEmbed, FullErrorEmbed +import discord -NO_CONTROL_MSG = "This modal cannot be controlled by you, sorry!" +from .embeds import FullErrorEmbed +if TYPE_CHECKING: + from catherinecore import Catherine -def produce_error_embed(error: Exception) -> ErrorEmbed: - error_traceback = "\n".join(traceback.format_exception_only(type(error), error)) - embed = ErrorEmbed() - embed.description = f""" - Uh oh! It seems like the modal ran into an issue! For support, please visit [Catherine-Chan's Support Server](https://discord.gg/ns3e74frqn) to get help! - - **Error**: - ```{error_traceback}``` - """ - embed.set_footer(text="Happened At") - embed.timestamp = utcnow() - return embed +NO_CONTROL_MSG = "This modal cannot be controlled by you, sorry!" class CatherineModal(discord.ui.Modal): def __init__(self, interaction: discord.Interaction, *args, **kwargs): super().__init__(*args, **kwargs) self.interaction = interaction + self.bot: Catherine = interaction.client # type: ignore async def interaction_check(self, interaction: discord.Interaction, /) -> bool: if interaction.user and interaction.user.id in ( @@ -39,6 +30,11 @@ async def interaction_check(self, interaction: discord.Interaction, /) -> bool: async def on_error( self, interaction: discord.Interaction, error: Exception, / ) -> None: + self.bot.logger.exception( + "Ignoring modal exception from %s: ", + self.__class__.__name__, + exc_info=error, + ) await interaction.response.send_message( embed=FullErrorEmbed(error), ephemeral=True ) diff --git a/bot/libs/utils/view.py b/bot/libs/utils/view.py index 74645c0..44a03c0 100644 --- a/bot/libs/utils/view.py +++ b/bot/libs/utils/view.py @@ -1,10 +1,15 @@ +from __future__ import annotations + import asyncio -from typing import Any, Optional +from typing import TYPE_CHECKING, Any, Optional import discord from .embeds import FullErrorEmbed, TimeoutEmbed +if TYPE_CHECKING: + from catherinecore import Catherine + NO_CONTROL_MSG = "This view cannot be controlled by you, sorry!" @@ -19,6 +24,7 @@ def __init__( self.interaction = interaction self.original_response: Optional[discord.InteractionMessage] self.triggered = asyncio.Event() + self.bot: Catherine = interaction.client # type: ignore async def interaction_check(self, interaction: discord.Interaction, /) -> bool: if interaction.user and interaction.user.id in ( @@ -42,6 +48,9 @@ async def on_error( item: discord.ui.Item[Any], /, ) -> None: + self.bot.logger.exception( + "Ignoring view exception from %s: ", self.__class__.__name__, exc_info=error + ) await interaction.response.send_message( embed=FullErrorEmbed(error), ephemeral=True )