diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py index 210ae3fb05..45f9857f2a 100644 --- a/bot/exts/filtering/filtering.py +++ b/bot/exts/filtering/filtering.py @@ -47,7 +47,7 @@ build_mod_alert, format_response_error, ) -from bot.exts.filtering._utils import past_tense, repr_equals, starting_value, to_serializable +from bot.exts.filtering._utils import past_tense, repr_equals, resolve_mention, starting_value, to_serializable from bot.exts.moderation.infraction.infractions import COMP_BAN_DURATION, COMP_BAN_REASON from bot.exts.utils.snekbox._io import FileAttachment from bot.log import get_logger @@ -66,6 +66,21 @@ WEEKLY_REPORT_ISO_DAY = 3 # 1=Monday, 7=Sunday +def _clean_ban_mentions(mentions: set[str]) -> set[str]: + """Remove broad pings and moderators role pings from ban alerts.""" + blocked_mentions = {f"<@&{Roles.moderators}>", "@here", "@everyone"} + cleaned_mentions = set() + + for mention in mentions: + if mention.casefold() == "moderators": + continue + if resolve_mention(mention) in blocked_mentions: + continue + cleaned_mentions.add(mention) + + return cleaned_mentions + + async def _extract_text_file_content(att: discord.Attachment) -> str: """Extract up to the first 30 lines or first 2000 characters (whichever is shorter) of an attachment.""" file_encoding = re.search(r"charset=(\S+)", att.content_type).group(1) @@ -981,7 +996,9 @@ async def _resolve_action( # If the action is a ban, mods don't want to be pinged. if infr_action := result_actions.get("infraction_and_notification"): if infr_action.infraction_type == Infraction.BAN: - result_actions.pop("mentions", None) + if mentions := result_actions.get("mentions"): + mentions.guild_pings = _clean_ban_mentions(mentions.guild_pings) + mentions.dm_pings = _clean_ban_mentions(mentions.dm_pings) return result_actions, messages, triggers async def _send_alert(self, ctx: FilterContext, triggered_filters: dict[FilterList, Iterable[str]]) -> None: diff --git a/tests/bot/exts/filtering/test_settings_entries.py b/tests/bot/exts/filtering/test_settings_entries.py index f12b2caa55..ef00aaf424 100644 --- a/tests/bot/exts/filtering/test_settings_entries.py +++ b/tests/bot/exts/filtering/test_settings_entries.py @@ -1,5 +1,7 @@ import unittest +from unittest.mock import patch +from bot.constants import Roles from bot.exts.filtering._filter_context import Event, FilterContext from bot.exts.filtering._settings_types.actions.infraction_and_notification import ( Infraction, @@ -9,6 +11,7 @@ from bot.exts.filtering._settings_types.validations.bypass_roles import RoleBypass from bot.exts.filtering._settings_types.validations.channel_scope import ChannelScope from bot.exts.filtering._settings_types.validations.filter_dm import FilterDM +from bot.exts.filtering.filtering import _clean_ban_mentions from tests.helpers import MockCategoryChannel, MockDMChannel, MockMember, MockMessage, MockRole, MockTextChannel @@ -218,3 +221,20 @@ def test_infraction_merge_of_different_infraction_types(self): "infraction_channel": 0 } ) + + @patch("bot.exts.filtering.filtering.resolve_mention") + def test_clean_ban_mentions_removes_moderator_and_broad_mentions(self, resolve_mention_mock): + """Ban-alert mention cleanup should remove moderator, here, and everyone pings.""" + resolve_mention_mock.side_effect = lambda mention: { + "here": "@here", + "everyone": "@everyone", + "Moderators": f"<@&{Roles.moderators}>", + str(Roles.moderators): f"<@&{Roles.moderators}>", + "other-role": "<@&123>", + }.get(mention, mention) + + mentions = {"here", "everyone", "Moderators", str(Roles.moderators), "other-role", "12345"} + + cleaned = _clean_ban_mentions(mentions) + + self.assertSetEqual(cleaned, {"other-role", "12345"})