diff options
author | 2023-02-28 20:41:59 +0200 | |
---|---|---|
committer | 2023-02-28 20:41:59 +0200 | |
commit | 3211097574ffaee1026f200272f33cc0bbc73ed7 (patch) | |
tree | a411674791fa87f583ec49ec715f9e7b497c03e9 | |
parent | Copy message from other infraction if result doesn't have one. (diff) |
Fix antispam alerting
- Only upload deletion logs when there's actually deletion.
- Don't upload deletion logs when an antispam event begins (could happen when another filter demanded an alert to be sent), but instead delay it until the antispam alert. Otherwise the antispam alert would cause an error because it would try to upload duplicate messages.
- Correctly include any messages and channels added to the antispam event after the initial deletion.
-rw-r--r-- | bot/exts/filtering/_filter_context.py | 4 | ||||
-rw-r--r-- | bot/exts/filtering/_filter_lists/antispam.py | 18 | ||||
-rw-r--r-- | bot/exts/filtering/_settings_types/actions/remove_context.py | 2 | ||||
-rw-r--r-- | bot/exts/filtering/_ui/ui.py | 17 |
4 files changed, 28 insertions, 13 deletions
diff --git a/bot/exts/filtering/_filter_context.py b/bot/exts/filtering/_filter_context.py index 9faa16b53..f5f635f9c 100644 --- a/bot/exts/filtering/_filter_context.py +++ b/bot/exts/filtering/_filter_context.py @@ -44,11 +44,13 @@ class FilterContext: matches: list[str] = field(default_factory=list) # What exactly was found notification_domain: str = "" # A domain to send the user for context filter_info: dict['Filter', str] = field(default_factory=dict) # Additional info from a filter. + messages_deletion: bool = False # Whether the messages were deleted. Can't upload deletion log otherwise. # Additional actions to perform additional_actions: list[Callable[[FilterContext], Coroutine]] = field(default_factory=list) - related_messages: set[Message] = field(default_factory=set) + related_messages: set[Message] = field(default_factory=set) # Deletion will include these. related_channels: set[TextChannel | Thread | DMChannel] = field(default_factory=set) attachments: dict[int, list[str]] = field(default_factory=dict) # Message ID to attachment URLs. + upload_deletion_logs: bool = True # Whether it's allowed to upload deletion logs. @classmethod def from_message( diff --git a/bot/exts/filtering/_filter_lists/antispam.py b/bot/exts/filtering/_filter_lists/antispam.py index 147998c1c..c5ce292e3 100644 --- a/bot/exts/filtering/_filter_lists/antispam.py +++ b/bot/exts/filtering/_filter_lists/antispam.py @@ -77,17 +77,23 @@ class AntispamList(UniquesListBase): self.message_deletion_queue[ctx.author] = DeletionContext() ctx.additional_actions.append(self._create_deletion_context_handler(ctx.author)) ctx.related_channels |= {msg.channel for msg in ctx.related_messages} - else: # The additional messages found are already part of the deletion context + else: # The additional messages found are already part of a deletion context ctx.related_messages = set() current_infraction = self.message_deletion_queue[ctx.author].current_infraction + # In case another filter wants an alert, prevent deleted messages from being uploaded now and also for + # the spam alert (upload happens during alerting). + # Deleted messages API doesn't accept duplicates and will error. + # Additional messages are necessarily part of the deletion. + ctx.upload_deletion_logs = False self.message_deletion_queue[ctx.author].add(ctx, triggers) - current_actions = sublist.merge_actions(triggers) if triggers else None + current_actions = sublist.merge_actions(triggers) # Don't alert yet. current_actions.pop("ping", None) current_actions.pop("send_alert", None) + new_infraction = current_actions["infraction_and_notification"].copy() - # Smaller infraction value = higher in hierarchy. + # Smaller infraction value => higher in hierarchy. if not current_infraction or new_infraction.infraction_type.value < current_infraction.value: # Pick the first triggered filter for the reason, there's no good way to decide between them. new_infraction.infraction_reason = ( @@ -161,11 +167,13 @@ class DeletionContext: new_ctx.action_descriptions[-1] += f" (+{descriptions_num - 20} other actions)" new_ctx.related_messages = reduce( or_, (other_ctx.related_messages for other_ctx in other_contexts), ctx.related_messages - ) + ) | {ctx.message for ctx in other_contexts} new_ctx.related_channels = reduce( or_, (other_ctx.related_channels for other_ctx in other_contexts), ctx.related_channels - ) + ) | {ctx.channel for ctx in other_contexts} new_ctx.attachments = reduce(or_, (other_ctx.attachments for other_ctx in other_contexts), ctx.attachments) + new_ctx.upload_deletion_logs = True + new_ctx.messages_deletion = all(ctx.messages_deletion for ctx in self.contexts) rules = list(self.rules) actions = antispam_list[ListType.DENY].merge_actions(rules) diff --git a/bot/exts/filtering/_settings_types/actions/remove_context.py b/bot/exts/filtering/_settings_types/actions/remove_context.py index e030f06d2..a34822542 100644 --- a/bot/exts/filtering/_settings_types/actions/remove_context.py +++ b/bot/exts/filtering/_settings_types/actions/remove_context.py @@ -59,6 +59,8 @@ class RemoveContext(ActionEntry): if not ctx.message or not ctx.message.guild: return + # If deletion somehow fails at least this will allow scheduling for deletion. + ctx.messages_deletion = True channel_messages = defaultdict(set) # Duplicates will cause batch deletion to fail. for message in {ctx.message} | ctx.related_messages: channel_messages[message.channel].add(message) diff --git a/bot/exts/filtering/_ui/ui.py b/bot/exts/filtering/_ui/ui.py index dc996faf7..24fb507e3 100644 --- a/bot/exts/filtering/_ui/ui.py +++ b/bot/exts/filtering/_ui/ui.py @@ -57,22 +57,25 @@ T = TypeVar('T') async def _build_alert_message_content(ctx: FilterContext, current_message_length: int) -> str: """Build the content section of the alert.""" # For multiple messages and those with attachments or excessive newlines, use the logs API - if any(( + if ctx.messages_deletion and ctx.upload_deletion_logs and any(( ctx.related_messages, len(ctx.attachments) > 0, ctx.content.count('\n') > 15 )): url = await upload_log(ctx.related_messages, bot.instance.user.id, ctx.attachments) - alert_content = f"A complete log of the offending messages can be found [here]({url})" - else: - alert_content = escape_markdown(ctx.content) - remaining_chars = MAX_EMBED_DESCRIPTION - current_message_length + return f"A complete log of the offending messages can be found [here]({url})" + + alert_content = escape_markdown(ctx.content) + remaining_chars = MAX_EMBED_DESCRIPTION - current_message_length - if len(alert_content) > remaining_chars: + if len(alert_content) > remaining_chars: + if ctx.messages_deletion and ctx.upload_deletion_logs: url = await upload_log([ctx.message], bot.instance.user.id, ctx.attachments) log_site_msg = f"The full message can be found [here]({url})" # 7 because that's the length of "[...]\n\n" - alert_content = alert_content[:remaining_chars - (7 + len(log_site_msg))] + "[...]\n\n" + log_site_msg + return alert_content[:remaining_chars - (7 + len(log_site_msg))] + "[...]\n\n" + log_site_msg + else: + return alert_content[:remaining_chars - 5] + "[...]" return alert_content |