feat(meta): Improve bugsplat.

This commit is contained in:
2023-09-10 07:14:37 +03:00
parent 6343d47072
commit 57a4e641ba
3 changed files with 105 additions and 9 deletions

View File

@@ -11,6 +11,7 @@ from discord.app_commands.errors import CommandInvokeError as appCommandInvokeEr
from aiohttp import ClientSession
from data import Database
from utils.lib import tabulate
from .config import Conf
from .logger import logging_context, log_context, log_action_stack, log_wrap, set_logging_context
@@ -192,7 +193,7 @@ class LionBot(Bot):
pass
except asyncio.TimeoutError:
pass
except Exception:
except Exception as e:
logger.exception(
f"Caught an unknown CommandInvokeError while executing: {cmd_str}",
extra={'action': 'BotError', 'with_ctx': True}
@@ -201,10 +202,36 @@ class LionBot(Bot):
error_embed = discord.Embed(title="Something went wrong!")
error_embed.description = (
"An unexpected error occurred while processing your command!\n"
"Our development team has been notified, and the issue should be fixed soon.\n"
"If the error persists, please contact our support team and give them the following number: "
f"`{ctx.interaction.id if ctx.interaction else ctx.message.id}`"
)
"Our development team has been notified, and the issue will be addressed soon.\n"
"If the error persists, or you have any questions, please contact our [support team]({link}) "
"and give them the extra details below."
).format(link=self.config.bot.support_guild)
details = {}
details['error'] = f"`{repr(e)}`"
if ctx.interaction:
details['interactionid'] = f"`{ctx.interaction.id}`"
if ctx.command:
details['cmd'] = f"`{ctx.command.qualified_name}`"
if ctx.author:
details['author'] = f"`{ctx.author.id}` -- `{ctx.author}`"
if ctx.guild:
details['guild'] = f"`{ctx.guild.id}` -- `{ctx.guild.name}`"
details['my_guild_perms'] = f"`{ctx.guild.me.guild_permissions.value}`"
if ctx.author:
ownerstr = ' (owner)' if ctx.author == ctx.guild.owner else ''
details['author_guild_perms'] = f"`{ctx.author.guild_permissions.value}{ownerstr}`"
if ctx.channel.type is discord.enums.ChannelType.private:
details['channel'] = "`Direct Message`"
elif ctx.channel:
details['channel'] = f"`{ctx.channel.id}` -- `{ctx.channel.name}`"
details['my_channel_perms'] = f"`{ctx.channel.permissions_for(ctx.guild.me).value}`"
if ctx.author:
details['author_channel_perms'] = f"`{ctx.channel.permissions_for(ctx.author).value}`"
details['shard'] = f"`{self.shardname}`"
details['log_stack'] = f"`{log_action_stack.get()}`"
table = '\n'.join(tabulate(*details.items()))
error_embed.add_field(name='Details', value=table)
try:
await ctx.error_reply(embed=error_embed)

View File

@@ -1,12 +1,15 @@
import logging
import discord
from discord import Interaction
from discord.app_commands import CommandTree
from discord.app_commands.errors import AppCommandError, CommandInvokeError
from discord.enums import InteractionType
from discord.app_commands.namespace import Namespace
from .logger import logging_context, set_logging_context, log_wrap
from utils.lib import tabulate
from .logger import logging_context, set_logging_context, log_wrap, log_action_stack
from .errors import SafeCancellation
logger = logging.getLogger(__name__)
@@ -17,7 +20,7 @@ class LionTree(CommandTree):
super().__init__(*args, **kwargs)
self._call_tasks = set()
async def on_error(self, interaction, error) -> None:
async def on_error(self, interaction: discord.Interaction, error) -> None:
try:
if isinstance(error, CommandInvokeError):
raise error.original
@@ -28,6 +31,51 @@ class LionTree(CommandTree):
pass
except Exception:
logger.exception(f"Unhandled exception in interaction: {interaction}", extra={'action': 'TreeError'})
if not interaction.is_expired():
splat = self.bugsplat(interaction, error)
try:
if interaction.response.is_done():
await interaction.followup.send(embed=splat, ephemeral=True)
else:
await interaction.response.send_message(embed=splat, ephemeral=True)
except discord.HTTPException:
pass
def bugsplat(self, interaction, e):
error_embed = discord.Embed(title="Something went wrong!", colour=discord.Colour.red())
error_embed.description = (
"An unexpected error occurred during this interaction!\n"
"Our development team has been notified, and the issue will be addressed soon.\n"
"If the error persists, or you have any questions, please contact our [support team]({link}) "
"and give them the extra details below."
).format(link=interaction.client.config.bot.support_guild)
details = {}
details['error'] = f"`{repr(e)}`"
details['interactionid'] = f"`{interaction.id}`"
details['interactiontype'] = f"`{interaction.type}`"
if interaction.command:
details['cmd'] = f"`{interaction.command.qualified_name}`"
if interaction.user:
details['user'] = f"`{interaction.user.id}` -- `{interaction.user}`"
if interaction.guild:
details['guild'] = f"`{interaction.guild.id}` -- `{interaction.guild.name}`"
details['my_guild_perms'] = f"`{interaction.guild.me.guild_permissions.value}`"
if interaction.user:
ownerstr = ' (owner)' if interaction.user == interaction.guild.owner else ''
details['user_guild_perms'] = f"`{interaction.user.guild_permissions.value}{ownerstr}`"
if interaction.channel.type is discord.enums.ChannelType.private:
details['channel'] = "`Direct Message`"
elif interaction.channel:
details['channel'] = f"`{interaction.channel.id}` -- `{interaction.channel.name}`"
details['my_channel_perms'] = f"`{interaction.channel.permissions_for(interaction.guild.me).value}`"
if interaction.user:
details['user_channel_perms'] = f"`{interaction.channel.permissions_for(interaction.user).value}`"
details['shard'] = f"`{interaction.client.shardname}`"
details['log_stack'] = f"`{log_action_stack.get()}`"
table = '\n'.join(tabulate(*details.items()))
error_embed.add_field(name='Details', value=table)
return error_embed
def _from_interaction(self, interaction: Interaction) -> None:
@log_wrap(context=f"iid: {interaction.id}", isolate=False)

View File

@@ -230,9 +230,20 @@ class LeoUI(View):
)
except Exception:
logger.exception(
f"Unhandled interaction exception occurred in item {item!r} of LeoUI {self!r}",
f"Unhandled interaction exception occurred in item {item!r} of LeoUI {self!r} from interaction: "
f"{interaction.data}",
extra={'with_ctx': True, 'action': 'UIError'}
)
# Explicitly handle the bugsplat ourselves
if not interaction.is_expired():
splat = interaction.client.tree.bugsplat(interaction, error)
try:
if interaction.response.is_done():
await interaction.followup.send(embed=splat, ephemeral=True)
else:
await interaction.response.send_message(embed=splat, ephemeral=True)
except discord.HTTPException:
pass
class MessageUI(LeoUI):
@@ -461,9 +472,19 @@ class LeoModal(Modal):
raise error
except Exception:
logger.exception(
f"Unhandled interaction exception occurred in {self!r}",
f"Unhandled interaction exception occurred in {self!r}. Interaction: {interaction.data}",
extra={'with_ctx': True, 'action': 'ModalError'}
)
# Explicitly handle the bugsplat ourselves
if not interaction.is_expired():
splat = interaction.client.tree.bugsplat(interaction, error)
try:
if interaction.response.is_done():
await interaction.followup.send(embed=splat, ephemeral=True)
else:
await interaction.response.send_message(embed=splat, ephemeral=True)
except discord.HTTPException:
pass
def error_handler_for(exc):