diff --git a/discord/audit_logs.py b/discord/audit_logs.py index 2fbbfda70..4d51de642 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -188,15 +188,31 @@ def _guild_hash_transformer(path: str) -> Callable[[AuditLogEntry, Optional[str] def _transform_automod_trigger_metadata( entry: AuditLogEntry, data: AutoModerationTriggerMetadata ) -> Optional[AutoModTrigger]: + + if isinstance(entry.target, AutoModRule): + # Trigger type cannot be changed, so type should be the same before and after updates. + # Avoids checking which keys are in data to guess trigger type + # or returning None if data is empty. + try: + return AutoModTrigger.from_data(type=entry.target.trigger.type.value, data=data) + except Exception: + pass + + # If cannot get trigger type from the rule and data is empty, then cannot determine trigger type if not data: return None - # discord doesn't provide the type of the trigger - # have to infer from the data and present keys + # Try to infer trigger type from available keys in data if 'presets' in data: - return AutoModTrigger(presets=AutoModPresets._from_value(data['presets']), allow_list=data.get('allow_list')) # type: ignore + return AutoModTrigger( + type=enums.AutoModRuleTriggerType.keyword_preset, + presets=AutoModPresets._from_value(data['presets']), # type: ignore + allow_list=data.get('allow_list'), + ) elif 'keyword_filter' in data: - return AutoModTrigger(keyword_filter=data['keyword_filter']) # type: ignore + return AutoModTrigger(type=enums.AutoModRuleTriggerType.keyword, keyword_filter=data['keyword_filter']) # type: ignore + elif 'mention_total_limit' in data: + return AutoModTrigger(type=enums.AutoModRuleTriggerType.mention_spam, mention_limit=data['mention_total_limit']) # type: ignore def _transform_automod_actions(entry: AuditLogEntry, data: List[AutoModerationAction]) -> List[AutoModRuleAction]: diff --git a/discord/automod.py b/discord/automod.py index 3f3d5f3d6..5c406299e 100644 --- a/discord/automod.py +++ b/discord/automod.py @@ -114,7 +114,21 @@ class AutoModRuleAction: class AutoModTrigger: - """Represents a trigger for an auto moderation rule. + r"""Represents a trigger for an auto moderation rule. + + The following table illustrates relevant attributes for each :class:`AutoModRuleTriggerType`: + + +-----------------------------------------------+--------------------------------------+ + | Type | Attributes | + +===============================================+======================================+ + | :attr:`AutoModRuleTriggerType.keyword` | :attr:`keyword_filter` | + +-----------------------------------------------+--------------------------------------+ + | :attr:`AutoModRuleTriggerType.spam` | | + +-----------------------------------------------+--------------------------------------+ + | :attr:`AutoModRuleTriggerType.keyword_preset` | :attr:`presets`\, :attr:`allow_list` | + +-----------------------------------------------+--------------------------------------+ + | :attr:`AutoModRuleTriggerType.mention_spam` | :attr:`mention_limit` | + +-----------------------------------------------+--------------------------------------+ .. versionadded:: 2.0 @@ -122,12 +136,15 @@ class AutoModTrigger: ----------- type: :class:`AutoModRuleTriggerType` The type of trigger. - keyword_filter: Optional[List[:class:`str`]] + keyword_filter: List[:class:`str`] The list of strings that will trigger the keyword filter. - presets: Optional[:class:`AutoModPresets`] + presets: :class:`AutoModPresets` The presets used with the preset keyword filter. - allow_list: Optional[List[:class:`str`]] + allow_list: List[:class:`str`] The list of words that are exempt from the commonly flagged words. + mention_limit: :class:`int` + The total number of user and role mentions a message can contain. + Has a maximum of 50. """ __slots__ = ( @@ -135,6 +152,7 @@ class AutoModTrigger: 'keyword_filter', 'presets', 'allow_list', + 'mention_limit', ) def __init__( @@ -144,42 +162,52 @@ class AutoModTrigger: keyword_filter: Optional[List[str]] = None, presets: Optional[AutoModPresets] = None, allow_list: Optional[List[str]] = None, + mention_limit: Optional[int] = None, ) -> None: - self.keyword_filter: Optional[List[str]] = keyword_filter - self.presets: Optional[AutoModPresets] = presets - self.allow_list: Optional[List[str]] = allow_list - if keyword_filter and presets: - raise ValueError('Please pass only one of keyword_filter or presets.') + if type is None and sum(arg is not None for arg in (keyword_filter, presets, mention_limit)) > 1: + raise ValueError('Please pass only one of keyword_filter, presets, or mention_limit.') if type is not None: self.type = type - elif self.keyword_filter is not None: + elif keyword_filter is not None: self.type = AutoModRuleTriggerType.keyword - elif self.presets is not None: + elif presets is not None: self.type = AutoModRuleTriggerType.keyword_preset + elif mention_limit is not None: + self.type = AutoModRuleTriggerType.mention_spam else: - raise ValueError('Please pass the trigger type explicitly if not using keyword_filter or presets.') + raise ValueError( + 'Please pass the trigger type explicitly if not using keyword_filter, presets, or mention_limit.' + ) + + self.keyword_filter: List[str] = keyword_filter if keyword_filter is not None else [] + self.presets: AutoModPresets = presets if presets is not None else AutoModPresets() + self.allow_list: List[str] = allow_list if allow_list is not None else [] + self.mention_limit: int = mention_limit if mention_limit is not None else 0 @classmethod def from_data(cls, type: int, data: Optional[AutoModerationTriggerMetadataPayload]) -> Self: type_ = try_enum(AutoModRuleTriggerType, type) - if type_ is AutoModRuleTriggerType.keyword: - return cls(keyword_filter=data['keyword_filter']) # type: ignore # unable to typeguard due to outer payload + if data is None: + return cls(type=type_) + elif type_ is AutoModRuleTriggerType.keyword: + return cls(type=type_, keyword_filter=data.get('keyword_filter')) elif type_ is AutoModRuleTriggerType.keyword_preset: - return cls(presets=AutoModPresets._from_value(data['presets']), allow_list=data.get('allow_list')) # type: ignore # unable to typeguard due to outer payload + return cls( + type=type_, presets=AutoModPresets._from_value(data.get('presets', [])), allow_list=data.get('allow_list') + ) + elif type_ is AutoModRuleTriggerType.mention_spam: + return cls(type=type_, mention_limit=data.get('mention_total_limit')) else: return cls(type=type_) - def to_metadata_dict(self) -> Dict[str, Any]: - if self.keyword_filter is not None: + def to_metadata_dict(self) -> Optional[Dict[str, Any]]: + if self.type is AutoModRuleTriggerType.keyword: return {'keyword_filter': self.keyword_filter} - elif self.presets is not None: - ret: Dict[str, Any] = {'presets': self.presets.to_array()} - if self.allow_list: - ret['allow_list'] = self.allow_list - return ret - - return {} + elif self.type is AutoModRuleTriggerType.keyword_preset: + return {'presets': self.presets.to_array(), 'allow_list': self.allow_list} + elif self.type is AutoModRuleTriggerType.mention_spam: + return {'mention_total_limit': self.mention_limit} class AutoModRule: @@ -361,7 +389,9 @@ class AutoModRule: payload['event_type'] = event_type if trigger is not MISSING: - payload['trigger_metadata'] = trigger.to_metadata_dict() + trigger_metadata = trigger.to_metadata_dict() + if trigger_metadata is not None: + payload['trigger_metadata'] = trigger_metadata if enabled is not MISSING: payload['enabled'] = enabled diff --git a/discord/enums.py b/discord/enums.py index 51e5078ab..0368e96bc 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -710,6 +710,7 @@ class AutoModRuleTriggerType(Enum): harmful_link = 2 spam = 3 keyword_preset = 4 + mention_spam = 5 class AutoModRuleEventType(Enum): diff --git a/discord/types/automod.py b/discord/types/automod.py index 0a1150fa9..9f6ef9447 100644 --- a/discord/types/automod.py +++ b/discord/types/automod.py @@ -69,10 +69,18 @@ class _AutoModerationTriggerMetadataKeyword(TypedDict): class _AutoModerationTriggerMetadataKeywordPreset(TypedDict): presets: List[AutoModerationTriggerPresets] + allow_list: List[str] + + +class _AutoModerationTriggerMetadataMentionLimit(TypedDict): + mention_total_limit: int AutoModerationTriggerMetadata = Union[ - _AutoModerationTriggerMetadataKeyword, _AutoModerationTriggerMetadataKeywordPreset, Empty + _AutoModerationTriggerMetadataKeyword, + _AutoModerationTriggerMetadataKeywordPreset, + _AutoModerationTriggerMetadataMentionLimit, + Empty, ] diff --git a/docs/api.rst b/docs/api.rst index 3429db63e..4f0fbe95a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3123,6 +3123,11 @@ of :class:`enum.Enum`. The rule will trigger when something triggers based on the set keyword preset types. + .. attribute:: mention_spam + + The rule will trigger when combined number of role and user mentions + is greater than the set limit. + .. class:: AutoModRuleEventType Represents the event type of an automod rule.