From 6afb05148d27d0a201ff616007cb7ffa38d72cda Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 21 Apr 2021 20:22:36 +0200 Subject: [PATCH] scanner exception handling --- README.md | 1 + src/scanners/scanner.py | 341 +++++++++++++++++++++------------------- 2 files changed, 178 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index d699661..eac16f3 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ python3 src/main.py * **v1.14** * `mobile/mention` arg to fix mobile bug * `%repeat`, `%mobile` to repeat commands + * bug fix * **v1.13** * improved scan `%words` * remove old and unused logs at start and guild leaving diff --git a/src/scanners/scanner.py b/src/scanners/scanner.py index 934c334..7ea4cda 100644 --- a/src/scanners/scanner.py +++ b/src/scanners/scanner.py @@ -57,198 +57,211 @@ class Scanner(ABC): ): args = list(args) guild = message.guild - with GuildLogs(guild) as logs: - # If "%cmd help" redirect to "%help cmd" - if "help" in args: - await client.bot.help(client, message, "help", args[0]) - return + progress = None + try: + with GuildLogs(guild) as logs: + # If "%cmd help" redirect to "%help cmd" + if "help" in args: + await client.bot.help(client, message, "help", args[0]) + return - # check args validity - str_channel_mentions = [ - str(channel.id) for channel in message.channel_mentions - ] - str_mentions = [str(member.id) for member in message.mentions] - dates = [] - for i, arg in enumerate(args[1:]): - skip_check = False - if re.match(r"^<@!?\d+>$", arg): - arg = arg[3:-1] if "!" in arg else arg[2:-1] - elif re.match(r"^<#!?\d+>$", arg): - arg = arg[3:-1] if "!" in arg else arg[2:-1] - elif re.match(ISO8601_REGEX, arg) or re.match(RELATIVE_REGEX, arg): - dates += [parse_time(arg)] - skip_check = True - if len(dates) > 2: + # check args validity + str_channel_mentions = [ + str(channel.id) for channel in message.channel_mentions + ] + str_mentions = [str(member.id) for member in message.mentions] + dates = [] + for i, arg in enumerate(args[1:]): + skip_check = False + if re.match(r"^<@!?\d+>$", arg): + arg = arg[3:-1] if "!" in arg else arg[2:-1] + elif re.match(r"^<#!?\d+>$", arg): + arg = arg[3:-1] if "!" in arg else arg[2:-1] + elif re.match(ISO8601_REGEX, arg) or re.match(RELATIVE_REGEX, arg): + dates += [parse_time(arg)] + skip_check = True + if len(dates) > 2: + await message.channel.send( + f"Too many date arguments: `{arg}`", reference=message + ) + return + if ( + arg + not in self.valid_args + + ["me", "here", "fast", "fresh", "mobile", "mention"] + and (not arg.isdigit() or not self.has_digit_args) + and arg not in str_channel_mentions + and arg not in str_mentions + and arg not in other_mentions + and not skip_check + ): await message.channel.send( - f"Too many date arguments: `{arg}`", reference=message + f"Unrecognized argument: `{arg}`", reference=message ) return - if ( - arg - not in self.valid_args - + ["me", "here", "fast", "fresh", "mobile", "mention"] - and (not arg.isdigit() or not self.has_digit_args) - and arg not in str_channel_mentions - and arg not in str_mentions - and arg not in other_mentions - and not skip_check - ): + + self.start_date = None if len(dates) < 1 else min(dates) + self.stop_date = None if len(dates) < 2 else max(dates) + + if self.start_date is not None and self.start_date > datetime.now(): await message.channel.send( - f"Unrecognized argument: `{arg}`", reference=message + f"Start date is after today", reference=message ) return - self.start_date = None if len(dates) < 1 else min(dates) - self.stop_date = None if len(dates) < 2 else max(dates) + # Get selected channels or all of them if no channel arguments + self.channels = no_duplicate(message.channel_mentions) - if self.start_date is not None and self.start_date > datetime.now(): - await message.channel.send( - f"Start date is after today", reference=message - ) - return + # transform the "here" arg + if "here" in args: + self.channels += [message.channel] - # Get selected channels or all of them if no channel arguments - self.channels = no_duplicate(message.channel_mentions) + self.full = len(self.channels) == 0 + if self.full: + self.channels = guild.text_channels - # transform the "here" arg - if "here" in args: - self.channels += [message.channel] + # Get selected members + self.members = no_duplicate(message.mentions) + self.raw_members = no_duplicate(message.raw_mentions) - self.full = len(self.channels) == 0 - if self.full: - self.channels = guild.text_channels + # transform the "me" arg + if "me" in args: + self.members += [message.author] + self.raw_members += [message.author.id] - # Get selected members - self.members = no_duplicate(message.mentions) - self.raw_members = no_duplicate(message.raw_mentions) + self.mention_users = "mention" in args or "mobile" in args - # transform the "me" arg - if "me" in args: - self.members += [message.author] - self.raw_members += [message.author.id] + if not await self.init(message, *args): + return - self.mention_users = "mention" in args or "mobile" in args - - if not await self.init(message, *args): - return - - # Start computing data - async with message.channel.typing(): - progress = await message.channel.send( - "```Starting analysis...```", - reference=message, - allowed_mentions=discord.AllowedMentions.none(), - ) - total_msg, total_chan = await logs.load( - progress, - self.channels, - self.start_date, - self.stop_date, - fast="fast" in args, - fresh="fresh" in args, - ) - if total_msg == CANCELLED: - await message.channel.send( - "Operation cancelled by user", + # Start computing data + async with message.channel.typing(): + progress = await message.channel.send( + "```Starting analysis...```", reference=message, + allowed_mentions=discord.AllowedMentions.none(), ) - elif total_msg == ALREADY_RUNNING: - await message.channel.send( - "An analysis is already running on this server, please be patient.", - reference=message, + total_msg, total_chan = await logs.load( + progress, + self.channels, + self.start_date, + self.stop_date, + fast="fast" in args, + fresh="fresh" in args, ) - elif total_msg == NO_FILE: - await message.channel.send(gdpr.TEXT) - else: - if self.start_date is not None and len(logs.channels) > 0: - self.start_date = max( - self.start_date, - min( - [ - logs.channels[channel.id].start_date - for channel in self.channels - if channel.id in logs.channels - and logs.channels[channel.id].start_date is not None - ] - ), - ) - if self.stop_date is None: - self.stop_date = datetime.utcnow() - - self.msg_count = 0 - self.total_msg = 0 - self.chan_count = 0 - t0 = datetime.now() - for channel in self.channels: - if channel.id in logs.channels: - channel_logs = logs.channels[channel.id] - count = sum( - [ - self.compute_message(channel_logs, message_log) - for message_log in channel_logs.messages - if ( - self.start_date is None - or message_log.created_at >= self.start_date - ) - and ( - self.stop_date is None - or message_log.created_at <= self.stop_date - ) - ] - ) - self.total_msg += len(channel_logs.messages) - self.msg_count += count - self.chan_count += 1 if count > 0 else 0 - logging.info(f"scan {guild.id} > scanned in {delta(t0):,}ms") - if self.msg_count == 0: + if total_msg == CANCELLED: await message.channel.send( - "There are no messages found matching the filters", + "Operation cancelled by user", reference=message, ) + elif total_msg == ALREADY_RUNNING: + await message.channel.send( + "An analysis is already running on this server, please be patient.", + reference=message, + ) + elif total_msg == NO_FILE: + await message.channel.send(gdpr.TEXT) else: - await progress.edit(content="```Computing results...```") - # Display results - t0 = datetime.now() - results = self.get_results( - get_intro( - self.intro_context, - self.full, - self.channels, - self.members, - self.msg_count, - self.chan_count, + if self.start_date is not None and len(logs.channels) > 0: + self.start_date = max( self.start_date, - self.stop_date, + min( + [ + logs.channels[channel.id].start_date + for channel in self.channels + if channel.id in logs.channels + and logs.channels[channel.id].start_date + is not None + ] + ), ) - ) - logging.info(f"scan {guild.id} > results in {delta(t0):,}ms") - response = "" - first = True - allowed_mentions = ( - discord.AllowedMentions.all() - if self.mention_users - else discord.AllowedMentions.none() - ) - for r in results: - if r: - if len(response + "\n" + r) > 2000: - await message.channel.send( - response, - reference=message if first else None, - allowed_mentions=allowed_mentions, - ) - first = False - response = "" - response += "\n" + r - if len(response) > 0: + if self.stop_date is None: + self.stop_date = datetime.utcnow() + + self.msg_count = 0 + self.total_msg = 0 + self.chan_count = 0 + t0 = datetime.now() + for channel in self.channels: + if channel.id in logs.channels: + channel_logs = logs.channels[channel.id] + count = sum( + [ + self.compute_message(channel_logs, message_log) + for message_log in channel_logs.messages + if ( + self.start_date is None + or message_log.created_at >= self.start_date + ) + and ( + self.stop_date is None + or message_log.created_at <= self.stop_date + ) + ] + ) + self.total_msg += len(channel_logs.messages) + self.msg_count += count + self.chan_count += 1 if count > 0 else 0 + logging.info(f"scan {guild.id} > scanned in {delta(t0):,}ms") + if self.msg_count == 0: await message.channel.send( - response, - reference=message if first else None, - allowed_mentions=allowed_mentions, + "There are no messages found matching the filters", + reference=message, ) - command_cache.cache(self, message, args) + else: + await progress.edit(content="```Computing results...```") + # Display results + t0 = datetime.now() + results = self.get_results( + get_intro( + self.intro_context, + self.full, + self.channels, + self.members, + self.msg_count, + self.chan_count, + self.start_date, + self.stop_date, + ) + ) + logging.info( + f"scan {guild.id} > results in {delta(t0):,}ms" + ) + response = "" + first = True + allowed_mentions = ( + discord.AllowedMentions.all() + if self.mention_users + else discord.AllowedMentions.none() + ) + for r in results: + if r: + if len(response + "\n" + r) > 2000: + await message.channel.send( + response, + reference=message if first else None, + allowed_mentions=allowed_mentions, + ) + first = False + response = "" + response += "\n" + r + if len(response) > 0: + await message.channel.send( + response, + reference=message if first else None, + allowed_mentions=allowed_mentions, + ) + command_cache.cache(self, message, args) # Delete custom progress message await progress.delete() + except Exception as error: + logging.exception(error) + await message.channel.send( + "An unexpected error happened while computing your command, we're sorry for the inconvenience.", + reference=message, + ) + if progress is not None: + await progress.delete() @abstractmethod async def init(self, message: discord.Message, *args: str) -> bool: