@@ -59,29 +59,59 @@ async def actions_for(
5959 ) -> tuple [ActionSettings | None , list [str ], dict [ListType , list [Filter ]]]:
6060 """Dispatch the given event to the list's filters, and return actions to take and messages to relay to mods."""
6161 text = clean_input (ctx .content , keep_newlines = True )
62-
63- matches = list (DISCORD_INVITE .finditer (text ))
64- invite_codes = {m .group ("invite" ) for m in matches }
62+ matches , invite_codes , refined_invites = self ._process_invite_codes (text )
6563 if not invite_codes :
6664 return None , [], {}
67- all_triggers = {}
6865
66+ _ , failed = self [ListType .ALLOW ].defaults .validations .evaluate (ctx )
67+ check_if_allowed = not failed
68+
69+ invites_for_inspection , unknown_invites = await self ._fetch_and_categorize_invites (
70+ refined_invites , check_if_allowed
71+ )
72+
73+ new_ctx = ctx .replace (content = {invite .guild .id for invite in invites_for_inspection .values ()})
74+ triggered = await self [ListType .DENY ].filter_list_result (new_ctx )
75+
76+ blocked_invites , invites_for_inspection = self ._apply_deny_filters (invites_for_inspection , triggered )
77+
78+ all_triggers = await self ._check_allow_list (
79+ ctx , invites_for_inspection , unknown_invites , check_if_allowed
80+ )
81+
82+ if not triggered and not unknown_invites :
83+ return None , [], all_triggers
84+
85+ actions = self ._determine_actions (unknown_invites , triggered , all_triggers )
86+
87+ blocked_invites |= unknown_invites
88+ ctx .matches += {match [0 ] for match in matches if refined_invites .get (match .group ("invite" )) in blocked_invites }
89+ ctx .alert_embeds += (self ._guild_embed (invite ) for invite in blocked_invites .values () if invite )
90+ if unknown_invites :
91+ ctx .potential_phish [self ] = set (unknown_invites )
92+
93+ messages = self ._build_messages (triggered , unknown_invites )
94+ return actions , messages , all_triggers
95+
96+ @staticmethod
97+ def _process_invite_codes (text : str ) -> tuple [list [re .Match ], set [str ], dict [str , str ]]:
98+ """Extract invite codes from the text and refine obfuscated codes."""
99+ matches = list (DISCORD_INVITE .finditer (text ))
100+ invite_codes = {m .group ("invite" ) for m in matches }
69101 refined_invites = {}
70102 for invite_code in invite_codes :
71- # Attempt to overcome an obfuscated invite.
72- # If the result is incorrect, it won't make the whitelist more permissive or the blacklist stricter.
73103 refined_invite_code = invite_code
74104 if match := REFINED_INVITE_CODE .search (invite_code ):
75105 refined_invite_code = match .group ("invite" )
76106 refined_invites [invite_code ] = refined_invite_code
77-
78- _ , failed = self [ ListType . ALLOW ]. defaults . validations . evaluate ( ctx )
79- # If the allowed list doesn't operate in the context, unknown invites are allowed.
80- check_if_allowed = not failed
81-
82- # Sort the invites into two categories:
83- invites_for_inspection = dict () # Found guild invites requiring further inspection.
84- unknown_invites = dict () # Either don't resolve or group DMs.
107+ return matches , invite_codes , refined_invites
108+
109+ async def _fetch_and_categorize_invites (
110+ self , refined_invites : dict [ str , str ], check_if_allowed : bool
111+ ) -> tuple [ dict [ str , Invite ], dict [ str , Invite | None ]]:
112+ """Fetch invites from the Discord API and categorize them into guild invites or unknown/group DMs."""
113+ invites_for_inspection = {}
114+ unknown_invites = {}
85115 for invite_code in refined_invites .values ():
86116 try :
87117 invite = await bot .instance .fetch_invite (invite_code )
@@ -91,63 +121,72 @@ async def actions_for(
91121 else :
92122 if invite .guild :
93123 invites_for_inspection [invite_code ] = invite
94- elif check_if_allowed : # Group DM
124+ elif check_if_allowed :
95125 unknown_invites [invite_code ] = invite
126+ return invites_for_inspection , unknown_invites
127+
128+ async def _check_allow_list (
129+ self , ctx : FilterContext , invites_for_inspection : dict [str , Invite ],
130+ unknown_invites : dict [str , Invite | None ], check_if_allowed : bool
131+ ) -> dict [ListType , list [Filter ]]:
132+ """Check remaining invites against the allow list and update unknown invites."""
133+ if not check_if_allowed :
134+ return {}
135+ guilds_for_inspection = {invite .guild .id for invite in invites_for_inspection .values ()}
136+ new_ctx = ctx .replace (content = guilds_for_inspection )
137+ all_triggers = {
138+ ListType .ALLOW : [
139+ filter_ for filter_ in self [ListType .ALLOW ].filters .values ()
140+ if await filter_ .triggered_on (new_ctx )
141+ ]
142+ }
143+ allowed = {filter_ .content for filter_ in all_triggers [ListType .ALLOW ]}
144+ unknown_invites .update ({
145+ code : invite for code , invite in invites_for_inspection .items () if invite .guild .id not in allowed
146+ })
147+ return all_triggers
96148
97- # Find any blocked invites
98- new_ctx = ctx .replace (content = {invite .guild .id for invite in invites_for_inspection .values ()})
99- triggered = await self [ListType .DENY ].filter_list_result (new_ctx )
149+ @staticmethod
150+ def _apply_deny_filters (
151+ invites_for_inspection : dict [str , Invite ], triggered : list [Filter ]
152+ ) -> tuple [dict [str , Invite ], dict [str , Invite ]]:
153+ """Separate blocked invites and filter out partnered/verified guilds from inspection."""
100154 blocked_guilds = {filter_ .content for filter_ in triggered }
101155 blocked_invites = {
102- code : invite for code , invite in invites_for_inspection .items () if invite .guild .id in blocked_guilds
156+ code : invite for code , invite in invites_for_inspection .items ()
157+ if invite .guild .id in blocked_guilds
103158 }
104-
105- # Remove the ones which are already confirmed as blocked, or otherwise ones which are partnered or verified.
106- invites_for_inspection = {
159+ filtered_invites = {
107160 code : invite for code , invite in invites_for_inspection .items ()
108161 if invite .guild .id not in blocked_guilds
109- and "PARTNERED" not in invite .guild .features and "VERIFIED" not in invite .guild .features
162+ and "PARTNERED" not in invite .guild .features
163+ and "VERIFIED" not in invite .guild .features
110164 }
165+ return blocked_invites , filtered_invites
111166
112- # Remove any remaining invites which are allowed
113- guilds_for_inspection = {invite .guild .id for invite in invites_for_inspection .values ()}
114-
115- if check_if_allowed : # Whether unknown invites need to be checked.
116- new_ctx = ctx .replace (content = guilds_for_inspection )
117- all_triggers [ListType .ALLOW ] = [
118- filter_ for filter_ in self [ListType .ALLOW ].filters .values ()
119- if await filter_ .triggered_on (new_ctx )
120- ]
121- allowed = {filter_ .content for filter_ in all_triggers [ListType .ALLOW ]}
122- unknown_invites .update ({
123- code : invite for code , invite in invites_for_inspection .items () if invite .guild .id not in allowed
124- })
125-
126- if not triggered and not unknown_invites :
127- return None , [], all_triggers
128-
167+ def _determine_actions (
168+ self , unknown_invites : dict , triggered : list [Filter ],
169+ all_triggers : dict [ListType , list [Filter ]]
170+ ) -> ActionSettings | None :
171+ """Determine the actions to take based on triggered filters and unknown invites."""
129172 actions = None
130- if unknown_invites : # There are invites which weren't allowed but aren't explicitly blocked.
173+ if unknown_invites :
131174 actions = self [ListType .ALLOW ].defaults .actions
132- # Blocked invites come second so that their actions have preference.
133175 if triggered :
134176 if actions :
135177 actions = actions .union (self [ListType .DENY ].merge_actions (triggered ))
136178 else :
137179 actions = self [ListType .DENY ].merge_actions (triggered )
138180 all_triggers [ListType .DENY ] = triggered
181+ return actions
139182
140- blocked_invites |= unknown_invites
141- ctx .matches += {match [0 ] for match in matches if refined_invites .get (match .group ("invite" )) in blocked_invites }
142- ctx .alert_embeds += (self ._guild_embed (invite ) for invite in blocked_invites .values () if invite )
143- if unknown_invites :
144- ctx .potential_phish [self ] = set (unknown_invites )
145-
183+ def _build_messages (self , triggered : list [Filter ], unknown_invites : dict ) -> list [str ]:
184+ """Build alert messages for triggered filters and unknown invites."""
146185 messages = self [ListType .DENY ].format_messages (triggered )
147186 messages += [
148187 f"`{ code } - { invite .guild .id } `" if invite else f"`{ code } `" for code , invite in unknown_invites .items ()
149188 ]
150- return actions , messages , all_triggers
189+ return messages
151190
152191 @staticmethod
153192 def _guild_embed (invite : Invite ) -> Embed :
0 commit comments