async def spotlight_on_error(self, exc, ctx): cmd_string = message_log_str(ctx.message) if isinstance(exc, commands.CommandInvokeError): root_exc = exc.__cause__ if exc.__cause__ is not None else exc # noinspection PyUnresolvedReferences if isinstance(root_exc, gsheets.Error): # any Google API errors logger.error("Google API error while processing command: {}\n\n{}" .format(cmd_string, tb_log_str(root_exc))) await self.bot.send_message(ctx.message.channel, "An error occurred while communicating with the Google API. " "See bot output for details.") await self.bot.send_message(self.dest_output, ("[ERROR] An error occurred while communicating with the Google API.\n" "Original command: {}\n{}\n\nSee logs for details") .format(cmd_string, exc_log_str(root_exc))) elif isinstance(root_exc, (gsheets.UnknownClientSecretsFlowError, gsheets.InvalidClientSecretsError) ): # Auth credentials file errors logger.error("Problem with Google API credentials file: {}\n\n{}" .format(cmd_string, tb_log_str(root_exc))) await self.bot.send_message(ctx.message.channel, "Problem with the stored Google API credentials. " "See bot output for details.") await self.bot.send_message(self.dest_output, ("[ERROR] Problem with Google API credentials file.\n" "Original command: {}\n{}\n\nSee logs for details") .format(cmd_string, exc_log_str(root_exc))) elif isinstance(root_exc, discord.HTTPException): cmd_string = str(ctx.message.content)[11:] logger.error("Error sending spotlight info ({}): {!s}" .format(cmd_string, root_exc)) await self.bot.send_message(ctx.message.channel, ("Error sending spotlight info, " "maybe the message is too long but Discord is stupid and might not " "give a useful error message here: {!s}").format(root_exc)) await self.bot.send_message(self.dest_output, ("[ERROR] Error sending spotlight info.\n" "Original command: {}\nDiscord API error: {!s}\n\n" "See logs for details").format(cmd_string, root_exc)) else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True) # Other errors can bubble up else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True) # Other errors can bubble up
async def on_error(self, event, *args, **kwargs): exc_info = sys.exc_info() if exc_info[0] is KeyboardInterrupt: logger.warning("Interrupted by user (SIGINT)") raise exc_info[1] log_msg = "Error occurred in {}({}, {})".format( event, ', '.join(repr(arg) for arg in args), ', '.join(key + '=' + repr(value) for key, value in kwargs.items())) logger.exception(log_msg) await self.bot.send_message( self.dest_output, "[ERROR] {} - see logs for details".format( exc_log_str(exc_info[1]))) try: message = args[0] await self.bot.send_message( message.channel, "An error occurred! Details have been logged. " "Please let the mods know so we can investigate.") except IndexError: pass except AttributeError: logger.warning( "Couldn't extract channel context from previous error - " "is args[0] not a message?")
async def on_error(self, event, *args, **kwargs): exc_info = sys.exc_info() if exc_info[0] is KeyboardInterrupt: logger.warning("Interrupted by user (SIGINT)") raise exc_info[1] elif exc_info[0] is asyncio.CancelledError: raise exc_info[1] elif exc_info[0] is BotNotReady: logger.warning("Event {} called before on_ready: ignoring".format(event)) return log_msg = "{}({}, {})".format( event, ', '.join(repr(arg) for arg in args), ', '.join(key + '=' + repr(value) for key, value in kwargs.items())) logger.exception("Error occurred in " + log_msg) await self.send_output("[ERROR] In {}\n\n{}\n\nSee log for details".format( log_msg, exc_log_str(exc_info[1]))) try: message = args[0] await self.bot.send_message(message.channel, "An error occurred! Details have been logged. " "Please let the mods know so we can investigate.") except IndexError: pass except AttributeError: logger.warning("Couldn't extract channel context from previous error - " "is args[0] not a message?")
async def _command_error(self, e: Exception, ctx: commands.Context): if isinstance(e, commands.BadArgument) and e.args[0] == 'channel': logger.error("No sticky for channel #{}".format(e.args[1].name)) await self.send_message( ctx.message.channel, ctx.message.author.mention + " Error: No sticky configured for channel #{}".format( e.args[1].name)) if isinstance(e, discord.Forbidden): data = self.cog_state.get(ctx.args[0].id) logger.error( "Error updating sticky {!r}: Permission error.\n\n{}".format( data, tb_log_str(e))) await self.send_message( ctx.message.channel, ctx.message.author.mention + "Error updating sticky {!r}. Discord permission error.".format( data)) elif isinstance(e, discord.HTTPException): data = self.cog_state.get(ctx.args[0].id) await self.send_message( ctx.message.channel, ctx.message.author.mention + "Error updating sticky {!r}. HTTP error: {}".format( data, exc_log_str(e))) else: await self.core.on_command_error(e, ctx, force=True ) # Other errors can bubble up
async def on_task_update_sticky_error(self, e: Exception, t: TaskInstance): data = t.args[0] # type: StickyData if isinstance(e, discord.Forbidden): await self.send_output( "Error updating sticky {!r}. Permission error: {}".format( data, exc_log_str(e))) elif isinstance(e, discord.HTTPException): await self.send_output( "Error updating sticky {!r}. Will retry. HTTP error: {}". format(data, exc_log_str(e))) self.scheduler.schedule_task_in(self.task_update_sticky, 60, args=(data, )) else: logger.error("Error updating sticky {!r}: {}".format( data, tb_log_str(e))) await self.send_output("Error updating sticky {!r}: {}".format( data, exc_log_str(e)))
async def wikichannel_error(self, exc: Exception, ctx: commands.Context): if isinstance(exc, commands.CommandInvokeError): root_exc = exc.__cause__ if exc.__cause__ is not None else exc if isinstance(root_exc, KeyError): ch = root_exc.args[0] await self.send_message( ctx.message.channel, "Channel {} is not configured for wikichannel.".format( ch.mention)) logger.warning( "Channel #{} not configured for wikichannel.".format( ch.name)) elif isinstance(root_exc, reddit.NotFound): msg = "404 Not Found: {}".format(root_exc.response.url.path) await self.send_message(ctx.message.channel, ctx.message.author.mention + " " + msg) logger.warning(msg) elif isinstance(root_exc, reddit.OAuthException): msg = "An OAuth error occurred: {}".format( exc_log_str(root_exc)) await self.send_message(ctx.message.channel, ctx.message.author.mention + " " + msg) logger.error(msg) elif isinstance(root_exc, reddit.RequestException): msg = "A reddit request error occurred: {}".format( exc_log_str(root_exc)) await self.send_message(ctx.message.channel, ctx.message.author.mention + " " + msg) logger.error(msg) elif isinstance(root_exc, reddit.ResponseException): msg = "A reddit error occurred: {}".format( exc_log_str(root_exc)) await self.send_message(ctx.message.channel, ctx.message.author.mention + " " + msg) logger.warning(msg) else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True ) # Other errors can bubble up else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True ) # Other errors can bubble up
async def testfile_error(self, exc: Exception, ctx: commands.Context): exc = exc.__cause__ if exc.__cause__ is not None else exc if isinstance(exc, IOError): await self.send_message( ctx.message.channel, "Error opening test file test/wikichannel.txt: {}".format( exc_log_str(exc))) logger.error( "Error opening test file test/wikichannel.txt: {}".format( tb_log_str(exc))) else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True ) # Other errors can bubble up
async def on_reminder_expired(self, reminder: ReminderData): logger.info("Reminder has expired: {!r}".format(reminder)) user = discord.utils.get(self.bot.get_all_members(), id=reminder.user_id) try: await self.bot.send_message( user, "**Reminder** At {} UTC, you asked me to send you a reminder: {}".format( format_datetime(reminder.timestamp), reminder.message ) ) except discord.errors.DiscordException as e: logger.error("Error sending reminder: {}".format(exc_log_str(e))) reminder.remind_time += 30 # try again a little later reminder.start_timer(self.bot.loop, self.on_reminder_expired) else: try: self.reminders.remove(reminder) except ValueError: logger.warning("on_reminder_expired: Reminder not in list of reminders - " "already removed? {!r}".format(reminder)) self._save_reminders()
async def remove(self, ctx: commands.Context, channel: discord.Channel): """!kazhelp brief: Disable a configured sticky message in a channel. description: | Disables the sticky message in the specified channel. This will delete any existing sticky message in that channel. parameters: - name: channel type: channel description: Channel to change examples: - command: ".sticky rem #resources" description: "Disables the sticky message in the #resources channel and removes any existing messages." """ try: data = self.cog_state.messages[channel.id] except KeyError: raise commands.BadArgument("channel", channel) if data.posted_message_id: try: message = await data.get_posted_message(self.bot) logger.info("Deleting old sticky message in #{.name:s}".format( channel)) await self.bot.delete_message(message) except discord.HTTPException as e: logger.warning( "Failed to delete existing sticky message in #{}; skipping: {}" .format(channel.name, tb_log_str(e))) await self.send_output( "[WARNING] Failed to delete existing sticky message in #{}; skipping.\n\n{}" .format(channel.name, exc_log_str(e))) self.cancel_channel_task(channel) sticky_config = self.cog_state.messages del sticky_config[channel.id] self.cog_state.set('messages', sticky_config) await self.send_message(ctx.message.channel, "Removed sticky in #{}".format(data.channel))
async def on_reminder_expired_error(self, e: Exception, t: TaskInstance): r = t.args[0] r.retries += 1 retry = True logger.error("Error sending reminder: {}".format(exc_log_str(e))) if not isinstance(e, discord.HTTPException): logger.error( "Reminders: non-HTTP error; giving up: {!r}".format(r)) await self.send_output( "Giving up on reminder: {!r}. Non-HTTP error occurred".format( r)) retry = False elif isinstance(e, discord.Forbidden ) and e.code == DiscordErrorCodes.CANNOT_PM_USER: logger.error( "Reminders: can't send PM to user; giving up: {!r}".format(r)) await self.send_public(( "{} You seem to have PMs from this server disabled or you've blocked me. " "I need to be able to PM you to send you reminders. (reminder missed)" ).format(user_mention(r.user_id))) await self.send_output( "Giving up on reminder: {!r}. User has PMs disabled.".format(r) ) retry = False elif r.retries > self.MAX_RETRIES: logger.error( "Reminders: max retries reached; giving up: {!r}".format(r)) await self.send_output( "Giving up on reminder: {!r}. Too many retries".format(r)) retry = False else: logger.debug("Will retry reminder: {!r}".format(r)) if not retry: t.cancel() self.reminders.remove(r) self._save_reminders()
def parse(self, command: Union[commands.Command, KazCog], bot: commands.Bot): """ Parse KazTron structured help documentation. This method stores the parsed documentation and some live information (e.g. annotated command checks) in the command object's ``kaz_structured_help`` attribute. This method is expected to be called by other help formatter routines that will then use this structured data to generate the final format (e.g. in-bot help, or generating HTML or Markdown for online documentation). Further helper methods :meth:`parse_tags` and :meth:`parse_links` are provided, in order to allow formatting of tags (IMPORTANT, WARNING, etc.) and inter-command links when needed. :param command: Command whose help to parse. :param bot: Bot instance the command is tied to :return: final parsed data :raise ValueError: command does not contain kazhelp-formatted data. """ try: doc_data = copy.deepcopy(command.kaz_structured_help) except AttributeError: # kaz_structured_help doesn't exist yet try: doc_data = self._parse_yaml(command) except YAMLError as e: if isinstance(command, commands.Command): name = "command {} (cog {})".format( command.qualified_name, type(command.instance).__name__ ) else: name = "cog {}".format(type(command).__name__) raise ValueError("Error parsing !kazhelp YAML for `{}`: {}" .format(name, exc_log_str(e))) from e if isinstance(command, commands.Command): self._process_checks(doc_data, command, bot) command.kaz_structured_help = copy.deepcopy(doc_data) self._parse_vars(command, doc_data) return doc_data
async def on_command_error(self, exc, ctx, force=False): """ Handles all command errors (see the ``discord.ext.commands.errors`` module). This method will do nothing if a command is detected to have an error handler ("on_error"); if you want on_command_error's default behaviour to take over, within a command error handler, you can call this method and pass ``force=True``. If you define custom command error handlers, note that CommandInvokeError is the one you want to handle for arbitrary errors (i.e. any exception raised that isn't derived from CommandError will cause discord.py to raise a CommandInvokeError from it). """ cmd_string = message_log_str(ctx.message) if not force and hasattr(ctx.command, "on_error"): return if ctx is not None and ctx.command is not None: usage_str = get_usage_str(ctx) else: usage_str = '(Unable to retrieve usage information)' if isinstance(exc, commands.CommandOnCooldown): await self.bot.send_message( ctx.message.channel, "`{}` is on cooldown! Try again in {:.0f} seconds.".format( get_command_str(ctx), max(exc.retry_after, 1.0))) elif isinstance(exc, commands.CommandInvokeError): root_exc = exc.__cause__ if exc.__cause__ is not None else exc if isinstance(root_exc, KeyboardInterrupt): logger.warning("Interrupted by user (SIGINT)") raise root_exc elif isinstance(root_exc, discord.HTTPException): # API errors err_msg = 'While executing {c}\n\nDiscord API error {e!s}' \ .format(c=cmd_string, e=root_exc) logger.error(err_msg + "\n\n{}".format(tb_log_str(root_exc))) await self.bot.send_message( self.dest_output, "[ERROR] " + err_msg + "\n\nSee log for details") else: logger.error( "An error occurred while processing the command: {}\n\n{}". format(cmd_string, tb_log_str(root_exc))) await self.bot.send_message( self.dest_output, "[ERROR] While executing {}\n\n{}\n\nSee logs for details". format(cmd_string, exc_log_str(root_exc))) # In all cases (except if return early/re-raise) await self.bot.send_message( ctx.message.channel, "An error occurred! Details have been logged. Let a mod know so we can " "investigate.") elif isinstance(exc, commands.DisabledCommand): msg = "Attempt to use disabled command: {}".format(cmd_string) logger.warning(msg) # No need to log this on Discord - not something mods need to be aware of # No need to inform user of this - prevents spam, "disabled" commands could just not # exist elif isinstance(exc, ModOnlyError): err_msg = "Unauthorised user for this command (not a moderator): {!r}".format( cmd_string) logger.warning(err_msg) await self.bot.send_message(self.dest_output, '[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, "Only mods can use that command.") elif isinstance(exc, AdminOnlyError): err_msg = "Unauthorised user for this command (not an admin): {!r}".format( cmd_string) logger.warning(err_msg) await self.bot.send_message(self.dest_output, '[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, "Only admins can use that command.") elif isinstance(exc, (UnauthorizedUserError, commands.CheckFailure)): logger.warning("Check failed on command: {!r}\n\n{}".format( cmd_string, tb_log_str(exc))) await self.bot.send_message( ctx.message.channel, "You're not allowed to use that command. " " *(Dev note: Implement error handler with more precise reason)*" ) elif isinstance(exc, UnauthorizedChannelError): err_msg = "Unauthorised channel for this command: {!r}".format( cmd_string) logger.warning(err_msg) await self.bot.send_message(self.dest_output, '[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, "You can't use that command here.") elif isinstance(exc, commands.NoPrivateMessage): msg = "Attempt to use non-PM command in PM: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message( ctx.message.channel, "Sorry, you can't use that command in PM.") # No need to log this on Discord, spammy and isn't something mods need to be aware of elif isinstance(exc, commands.BadArgument): exc_msg = exc.args[0] if len( exc.args) > 0 else '(No error message).' msg = "Bad argument passed in command: {}\n{}".format( cmd_string, exc_msg) logger.warning(msg) await self.bot.send_message(ctx.message.channel, ( "Invalid argument(s) for the command `{}`. {}\n\n**Usage:** `{}`\n\n" "Use `{}` for help. " "*(Dev note: Add error handler with more precise reason when possible)*" ).format(get_command_str(ctx), exc_msg, usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.TooManyArguments): msg = "Too many arguments passed in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message( ctx.message.channel, "Too many arguments.\n\n**Usage:** `{}`\n\nUse `{}` for help.". format(usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.MissingRequiredArgument): msg = "Missing required arguments in command: {}".format( cmd_string) logger.warning(msg) await self.bot.send_message( ctx.message.channel, "Missing argument(s) for the command `{}`.\n\n**Usage:** `{}`\n\nUse `{}` for help." .format(get_command_str(ctx), usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.CommandNotFound): msg = "Unknown command: {}".format(cmd_string) # avoid some natural language things that start with period (ellipsis, etc.) if ctx.invoked_with not in [ '.', '..' ] and not ctx.invoked_with.startswith('.'): logger.warning(msg) await self.bot.send_message( ctx.message.channel, "Sorry, I don't know the command `{}{.invoked_with}`". format(get_command_prefix(ctx), ctx)) else: logger.exception("Unknown exception occurred") await self.bot.send_message( ctx.message.channel, "An unexpected error occurred! Details have been logged. Let a mod know so we can " "investigate.") await self.bot.send_message( self.dest_output, ("[ERROR] Unknown error while trying to process command {}\n" "Error: {!s}\n\nSee logs for details").format( cmd_string, exc))
async def on_command_error(self, exc, ctx, force=False): """ Handles all command errors (see the ``discord.ext.commands.errors`` module). This method will do nothing if a command is detected to have an error handler ("on_error"); if you want on_command_error's default behaviour to take over, within a command error handler, you can call this method and pass ``force=True``. If you define custom command error handlers, note that CommandInvokeError is the one you want to handle for arbitrary errors (i.e. any exception raised that isn't derived from CommandError will cause discord.py to raise a CommandInvokeError from it). """ cmd_string = message_log_str(ctx.message) author_mention = ctx.message.author.mention + ' ' if not force and hasattr(ctx.command, "on_error"): return if ctx is not None and ctx.command is not None: usage_str = get_usage_str(ctx) else: usage_str = '(Unable to retrieve usage information)' if isinstance(exc, DeleteMessage): try: await self.bot.delete_message(exc.message) logger.info("on_command_error: Deleted invoking message") except discord.errors.DiscordException: logger.exception("Can't delete invoking message!") exc = exc.cause # and continue on to handle the cause of the DeleteMessage... if isinstance(exc, commands.CommandOnCooldown): await self.bot.send_message(ctx.message.channel, author_mention + "`{}` is on cooldown! Try again in {:.0f} seconds." .format(get_command_str(ctx), max(exc.retry_after, 1.0))) elif isinstance(exc, commands.CommandInvokeError): root_exc = exc.__cause__ if exc.__cause__ is not None else exc if isinstance(root_exc, KeyboardInterrupt): logger.warning("Interrupted by user (SIGINT)") raise root_exc elif isinstance(root_exc, discord.HTTPException): # API errors err_msg = 'While executing {c}\n\nDiscord API error {e!s}' \ .format(c=cmd_string, e=root_exc) logger.error(err_msg + "\n\n{}".format(tb_log_str(root_exc))) await self.send_output( "[ERROR] " + err_msg + "\n\nSee log for details") else: logger.error("An error occurred while processing the command: {}\n\n{}" .format(cmd_string, tb_log_str(root_exc))) await self.send_output( "[ERROR] While executing {}\n\n{}\n\nSee logs for details" .format(cmd_string, exc_log_str(root_exc))) # In all cases (except if return early/re-raise) await self.bot.send_message(ctx.message.channel, author_mention + "An error occurred! Details have been logged. Let a mod know so we can " "investigate.") elif isinstance(exc, commands.DisabledCommand): msg = "Attempt to use disabled command: {}".format(cmd_string) logger.warning(msg) # No need to log this on Discord - not something mods need to be aware of # No need to inform user of this - prevents spam, "disabled" commands could just not # exist elif isinstance(exc, ModOnlyError): err_msg = "Unauthorised user for this command (not a moderator): {!r}".format( cmd_string) logger.warning(err_msg) await self.send_output('[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, author_mention + "Only mods can use that command.") elif isinstance(exc, AdminOnlyError): err_msg = "Unauthorised user for this command (not an admin): {!r}".format( cmd_string) logger.warning(err_msg) await self.send_output('[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, author_mention + "Only admins can use that command.") elif isinstance(exc, (UnauthorizedUserError, commands.CheckFailure)): logger.warning( "Check failed on command: {!r}\n\n{}".format(cmd_string, tb_log_str(exc))) await self.bot.send_message(ctx.message.channel, author_mention + "You're not allowed to use that command. " " *(Dev note: Implement error handler with more precise reason)*") elif isinstance(exc, UnauthorizedChannelError): err_msg = "Unauthorised channel for this command: {!r}".format( cmd_string) logger.warning(err_msg) await self.send_output('[WARNING] ' + err_msg) await self.bot.send_message(ctx.message.channel, author_mention + "You can't use that command here.") elif isinstance(exc, commands.NoPrivateMessage): msg = "Attempt to use non-PM command in PM: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, "Sorry, you can't use that command in PM.") # No need to log this on Discord, spammy and isn't something mods need to be aware of elif isinstance(exc, commands.BadArgument): exc_msg = exc.args[0] if len(exc.args) > 0 else '(No error message).' msg = "Bad argument passed in command: {}\n{}".format(cmd_string, exc_msg) logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + ("Invalid argument(s): {}\n\n**Usage:** `{}`\n\n" "Use `{}` for help.") .format(exc_msg, usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.TooManyArguments): msg = "Too many arguments passed in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + "Too many arguments.\n\n**Usage:** `{}`\n\nUse `{}` for help." .format(usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, commands.MissingRequiredArgument): msg = "Missing required arguments in command: {}".format(cmd_string) logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + "Missing argument(s).\n\n**Usage:** `{}`\n\nUse `{}` for help." .format(usage_str, get_help_str(ctx))) # No need to log user errors to mods elif isinstance(exc, BotNotReady): try: cog_name = exc.args[0] except IndexError: cog_name = 'unknown' logger.warning("Attempted to use command while cog is not ready: {}".format(cmd_string)) await self.bot.send_message( ctx.message.channel, author_mention + "Sorry, I'm still loading the {} module! Try again in a few seconds." .format(cog_name) ) elif isinstance(exc, commands.CommandNotFound): msg = "Unknown command: {}".format(cmd_string) # safe to assume commands usually words - symbolic commands are rare # and we want to avoid emoticons ('._.', etc.), punctuation ('...') and decimal numbers # without leading 0 (.12) being detected if ctx.invoked_with and all(c.isalnum() for c in ctx.invoked_with) \ and not ctx.invoked_with[0].isdigit(): logger.warning(msg) await self.bot.send_message(ctx.message.channel, author_mention + "Sorry, I don't know the command `{}{.invoked_with}`" .format(get_command_prefix(ctx), ctx)) else: logger.exception("Unknown exception occurred") await self.bot.send_message(ctx.message.channel, author_mention + "An unexpected error occurred! Details have been logged. Let a mod know so we can " "investigate.") await self.send_output( ("[ERROR] Unknown error while trying to process command {}\n" "Error: {!s}\n\nSee logs for details").format(cmd_string, exc))
'-v', action='store_true', help='Show each project added to the database.') parser.add_argument( '--allow-duplicates', '-d', action='store_true', help= 'Do not check if an existing project exists before adding. If not specified, this ' 'script will skip any user+title matches already in the database.') args = parser.parse_args() args.file = str(Path(args.file).resolve()) add_application_path() from kaztron.cog.projects import query, wizard from kaztron.utils.discord import extract_user_id from kaztron.utils.logging import tb_log_str, exc_log_str query.init_db() try: r = import_file(args.file, args.dry_run, args.verbose, not args.allow_duplicates) except OSError as e: print("[ERROR]", exc_log_str(e)) sys.exit(1) except Exception as e: print("[ERROR]", tb_log_str(e)) sys.exit(1) sys.exit(0 if r else 1)