142 lines
5.6 KiB
Python
142 lines
5.6 KiB
Python
from AAA3A_utils import Cog, CogsUtils, Menu # isort:skip
|
|
from redbot.core import commands # isort:skip
|
|
from redbot.core.bot import Red # isort:skip
|
|
from redbot.core.i18n import Translator, cog_i18n # isort:skip
|
|
import discord # isort:skip
|
|
import typing # isort:skip
|
|
|
|
import traceback
|
|
|
|
from redbot.core.utils.chat_formatting import box, pagify
|
|
|
|
from .dashboard_integration import DashboardIntegration
|
|
|
|
# Credits:
|
|
# General repo credits.
|
|
|
|
_: Translator = Translator("AutoTraceback", __file__)
|
|
|
|
IGNORED_ERRORS = (
|
|
commands.UserInputError,
|
|
commands.DisabledCommand,
|
|
commands.CommandNotFound,
|
|
commands.CheckFailure,
|
|
commands.NoPrivateMessage,
|
|
commands.CommandOnCooldown,
|
|
commands.MaxConcurrencyReached,
|
|
commands.BadArgument,
|
|
commands.BadBoolArgument,
|
|
)
|
|
|
|
|
|
@cog_i18n(_)
|
|
class AutoTraceback(DashboardIntegration, Cog):
|
|
"""A cog to display the error traceback of a command automatically after the error!"""
|
|
|
|
def __init__(self, bot: Red) -> None:
|
|
super().__init__(bot=bot)
|
|
|
|
self.tracebacks: typing.List[str] = []
|
|
|
|
@commands.is_owner()
|
|
@commands.hybrid_command()
|
|
async def traceback(
|
|
self, ctx: commands.Context, public: typing.Optional[bool] = True, index: int = 0
|
|
) -> None:
|
|
"""Sends to the owner the last command exception that has occurred.
|
|
|
|
If public (yes is specified), it will be sent to the chat instead.
|
|
|
|
Warning: Sending the traceback publicly can accidentally reveal sensitive information about your computer or configuration.
|
|
|
|
**Examples:**
|
|
- `[p]traceback` - Sends the traceback to your DMs.
|
|
- `[p]traceback True` - Sends the last traceback in the current context.
|
|
|
|
**Arguments:**
|
|
- `[public]` - Whether to send the traceback to the current context. Default is `True`.
|
|
- `[index]` - The error index. `0` is the last one.
|
|
"""
|
|
if not self.tracebacks and not ctx.bot._last_exception:
|
|
raise commands.UserFeedbackCheckFailure(_("No exception has occurred yet."))
|
|
if index == 0: # Last bot exception can be set directly by cogs.
|
|
_last_exception = ctx.bot._last_exception
|
|
else:
|
|
try:
|
|
_last_exception = self.tracebacks[-(index + 1)]
|
|
except IndexError:
|
|
_last_exception = ctx.bot._last_exception
|
|
_last_exception = _last_exception.split("\n")
|
|
_last_exception[0] = _last_exception[0] + (
|
|
"" if _last_exception[0].endswith(":") else ":\n"
|
|
)
|
|
_last_exception = "\n".join(_last_exception)
|
|
_last_exception = CogsUtils.replace_var_paths(_last_exception)
|
|
if public:
|
|
try:
|
|
await Menu(pages=_last_exception, timeout=180, lang="py").start(ctx)
|
|
except discord.HTTPException:
|
|
pass
|
|
else:
|
|
return
|
|
for page in pagify(_last_exception, shorten_by=15):
|
|
try:
|
|
await ctx.author.send(box(page, lang="py"))
|
|
except discord.HTTPException:
|
|
raise commands.UserFeedbackCheckFailure(
|
|
"I couldn't send the traceback message to you in DM. "
|
|
"Either you blocked me or you disabled DMs in this server."
|
|
)
|
|
|
|
@commands.Cog.listener()
|
|
async def on_command_error(
|
|
self, ctx: commands.Context, error: commands.CommandError, unhandled_by_cog: bool = False
|
|
) -> None:
|
|
if await self.bot.cog_disabled_in_guild(cog=self, guild=ctx.guild):
|
|
return
|
|
if isinstance(error, IGNORED_ERRORS):
|
|
return
|
|
traceback_error = "".join(
|
|
traceback.format_exception(type(error), error, error.__traceback__)
|
|
)
|
|
_traceback_error = traceback_error.split("\n")
|
|
_traceback_error[0] = _traceback_error[0] + (
|
|
"" if _traceback_error[0].endswith(":") else ":\n"
|
|
)
|
|
traceback_error = "\n".join(_traceback_error)
|
|
traceback_error = CogsUtils.replace_var_paths(traceback_error)
|
|
self.tracebacks.append(traceback_error)
|
|
if ctx.author.id not in ctx.bot.owner_ids:
|
|
return
|
|
pages = [box(page, lang="py") for page in pagify(traceback_error, shorten_by=10)]
|
|
try:
|
|
await Menu(pages=pages, timeout=180, delete_after_timeout=False).start(ctx)
|
|
except discord.HTTPException:
|
|
pass
|
|
|
|
@commands.Cog.listener()
|
|
async def on_assistant_cog_add(
|
|
self, assistant_cog: typing.Optional[commands.Cog] = None
|
|
) -> None: # Vert's Assistant integration/third party.
|
|
if assistant_cog is None:
|
|
return self.get_last_command_error_traceback_for_assistant
|
|
schema = {
|
|
"name": "get_last_command_error_traceback_for_assistant",
|
|
"description": "Get the traceback of the last command error occured on the bot.",
|
|
"parameters": {"type": "object", "properties": {}, "required": []},
|
|
}
|
|
await assistant_cog.register_function(cog_name=self.qualified_name, schema=schema)
|
|
|
|
async def get_last_command_error_traceback_for_assistant(
|
|
self, user: typing.Union[discord.Member, discord.User], *args, **kwargs
|
|
):
|
|
if user.id not in self.bot.owner_ids:
|
|
return "Only bot owners can view errors tracebacks."
|
|
if not self.bot._last_exception:
|
|
return "No last command error recorded."
|
|
last_traceback = self.bot._last_exception
|
|
last_traceback = CogsUtils.replace_var_paths(last_traceback)
|
|
data = {
|
|
"Last command error traceback": f"\n{last_traceback}",
|
|
}
|
|
return [f"{key}: {value}\n" for key, value in data.items() if value is not None]
|