[Core] Don't publicly show errors when a command fails. [p]exception

Fixes #668
The exception command sends the last occurred exception to the owner, or even publicly if requested.
The error that used to show up publicly when a command failed has been hidden for security reasons.
All references to "modules" have been changed to "cogs"
This commit is contained in:
Twentysix 2017-03-24 00:45:41 +01:00
parent 613d2fe35a
commit bc68ad21c5
2 changed files with 51 additions and 30 deletions

View File

@ -54,46 +54,44 @@ class Owner:
@commands.command() @commands.command()
@checks.is_owner() @checks.is_owner()
async def load(self, *, module: str): async def load(self, *, cog_name: str):
"""Loads a module """Loads a cog
Example: load mod""" Example: load mod"""
module = module.strip() module = cog_name.strip()
if "cogs." not in module: if "cogs." not in module:
module = "cogs." + module module = "cogs." + module
try: try:
self._load_cog(module) self._load_cog(module)
except CogNotFoundError: except CogNotFoundError:
await self.bot.say("That module could not be found.") await self.bot.say("That cog could not be found.")
except CogLoadError as e: except CogLoadError as e:
log.exception(e) log.exception(e)
traceback.print_exc() traceback.print_exc()
await self.bot.say("There was an issue loading the module. Check" await self.bot.say("There was an issue loading the cog. Check"
" your console or logs for more information.\n" " your console or logs for more information.")
"\nError: `{}`".format(e.args[0]))
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
traceback.print_exc() traceback.print_exc()
await self.bot.say('Module was found and possibly loaded but ' await self.bot.say('Cog was found and possibly loaded but '
'something went wrong. Check your console ' 'something went wrong. Check your console '
'or logs for more information.\n\n' 'or logs for more information.')
'Error: `{}`'.format(e.args[0]))
else: else:
set_cog(module, True) set_cog(module, True)
await self.disable_commands() await self.disable_commands()
await self.bot.say("Module enabled.") await self.bot.say("The cog has been loaded.")
@commands.group(invoke_without_command=True) @commands.group(invoke_without_command=True)
@checks.is_owner() @checks.is_owner()
async def unload(self, *, module: str): async def unload(self, *, cog_name: str):
"""Unloads a module """Unloads a cog
Example: unload mod""" Example: unload mod"""
module = module.strip() module = cog_name.strip()
if "cogs." not in module: if "cogs." not in module:
module = "cogs." + module module = "cogs." + module
if not self._does_cogfile_exist(module): if not self._does_cogfile_exist(module):
await self.bot.say("That module file doesn't exist. I will not" await self.bot.say("That cog file doesn't exist. I will not"
" turn off autoloading at start just in case" " turn off autoloading at start just in case"
" this isn't supposed to happen.") " this isn't supposed to happen.")
else: else:
@ -106,14 +104,14 @@ class Owner:
except CogUnloadError as e: except CogUnloadError as e:
log.exception(e) log.exception(e)
traceback.print_exc() traceback.print_exc()
await self.bot.say('Unable to safely disable that module.') await self.bot.say('Unable to safely unload that cog.')
else: else:
await self.bot.say("Module disabled.") await self.bot.say("The cog has been unloaded.")
@unload.command(name="all") @unload.command(name="all")
@checks.is_owner() @checks.is_owner()
async def unload_all(self): async def unload_all(self):
"""Unloads all modules""" """Unloads all cogs"""
cogs = self._list_cogs() cogs = self._list_cogs()
still_loaded = [] still_loaded = []
for cog in cogs: for cog in cogs:
@ -135,10 +133,11 @@ class Owner:
@checks.is_owner() @checks.is_owner()
@commands.command(name="reload") @commands.command(name="reload")
async def _reload(self, module): async def _reload(self, *, cog_name: str):
"""Reloads a module """Reloads a cog
Example: reload audio""" Example: reload audio"""
module = cog_name.strip()
if "cogs." not in module: if "cogs." not in module:
module = "cogs." + module module = "cogs." + module
@ -150,19 +149,18 @@ class Owner:
try: try:
self._load_cog(module) self._load_cog(module)
except CogNotFoundError: except CogNotFoundError:
await self.bot.say("That module cannot be found.") await self.bot.say("That cog cannot be found.")
except NoSetupError: except NoSetupError:
await self.bot.say("That module does not have a setup function.") await self.bot.say("That cog does not have a setup function.")
except CogLoadError as e: except CogLoadError as e:
log.exception(e) log.exception(e)
traceback.print_exc() traceback.print_exc()
await self.bot.say("That module could not be loaded. Check your" await self.bot.say("That cog could not be loaded. Check your"
" console or logs for more information.\n\n" " console or logs for more information.")
"Error: `{}`".format(e.args[0]))
else: else:
set_cog(module, True) set_cog(module, True)
await self.disable_commands() await self.disable_commands()
await self.bot.say("Module reloaded.") await self.bot.say("The cog has been reloaded.")
@commands.command(name="cogs") @commands.command(name="cogs")
@checks.is_owner() @checks.is_owner()
@ -790,6 +788,23 @@ class Owner:
await self.bot.say("I need the `Embed links` permission " await self.bot.say("I need the `Embed links` permission "
"to send this") "to send this")
@commands.command(pass_context=True)
@checks.is_owner()
async def traceback(self, ctx, public: bool=False):
"""Sends to the owner the last command exception that has occurred
If public (yes is specified), it will be sent to the chat instead"""
if not public:
destination = ctx.message.author
else:
destination = ctx.message.channel
if self.bot._last_exception:
for page in pagify(self.bot._last_exception):
await self.bot.send_message(destination, box(page, lang="py"))
else:
await self.bot.say("No exception has occurred yet.")
def _load_cog(self, cogname): def _load_cog(self, cogname):
if not self._does_cogfile_exist(cogname): if not self._does_cogfile_exist(cogname):
raise CogNotFoundError(cogname) raise CogNotFoundError(cogname)

14
red.py
View File

@ -68,6 +68,7 @@ class Bot(commands.Bot):
self._intro_displayed = False self._intro_displayed = False
self._shutdown_mode = None self._shutdown_mode = None
self.logger = set_logger(self) self.logger = set_logger(self)
self._last_exception = None
if 'self_bot' in kwargs: if 'self_bot' in kwargs:
self.settings.self_bot = kwargs['self_bot'] self.settings.self_bot = kwargs['self_bot']
else: else:
@ -361,10 +362,15 @@ def initialize(bot_class=Bot, formatter_class=Formatter):
elif isinstance(error, commands.CommandInvokeError): elif isinstance(error, commands.CommandInvokeError):
bot.logger.exception("Exception in command '{}'".format( bot.logger.exception("Exception in command '{}'".format(
ctx.command.qualified_name), exc_info=error.original) ctx.command.qualified_name), exc_info=error.original)
oneliner = "Error in command '{}' - {}: {}".format( message = ("Error in command '{}'. Check your console or "
ctx.command.qualified_name, type(error.original).__name__, "logs for details."
str(error.original)) "".format(ctx.command.qualified_name))
await ctx.bot.send_message(channel, inline(oneliner)) log = ("Exception in command '{}'\n"
"".format(ctx.command.qualified_name))
log += "".join(traceback.format_exception(type(error), error,
error.__traceback__))
bot._last_exception = log
await ctx.bot.send_message(channel, inline(message))
elif isinstance(error, commands.CommandNotFound): elif isinstance(error, commands.CommandNotFound):
pass pass
elif isinstance(error, commands.CheckFailure): elif isinstance(error, commands.CheckFailure):