Browse Source

Add specialcase for AutoMod triggers in audit log diff

pull/9629/head
z03h 1 year ago
committed by GitHub
parent
commit
a5d03d4a1e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 113
      discord/audit_logs.py
  2. 8
      discord/types/audit_log.py
  3. 6
      docs/api.rst

113
discord/audit_logs.py

@ -61,6 +61,7 @@ if TYPE_CHECKING:
from .types.audit_log import ( from .types.audit_log import (
AuditLogChange as AuditLogChangePayload, AuditLogChange as AuditLogChangePayload,
AuditLogEntry as AuditLogEntryPayload, AuditLogEntry as AuditLogEntryPayload,
_AuditLogChange_TriggerMetadata as AuditLogChangeTriggerMetadataPayload,
) )
from .types.channel import ( from .types.channel import (
PermissionOverwrite as PermissionOverwritePayload, PermissionOverwrite as PermissionOverwritePayload,
@ -71,7 +72,7 @@ if TYPE_CHECKING:
from .types.role import Role as RolePayload from .types.role import Role as RolePayload
from .types.snowflake import Snowflake from .types.snowflake import Snowflake
from .types.command import ApplicationCommandPermissions from .types.command import ApplicationCommandPermissions
from .types.automod import AutoModerationTriggerMetadata, AutoModerationAction from .types.automod import AutoModerationAction
from .user import User from .user import User
from .app_commands import AppCommand from .app_commands import AppCommand
from .webhook import Webhook from .webhook import Webhook
@ -230,30 +231,6 @@ def _guild_hash_transformer(path: str) -> Callable[[AuditLogEntry, Optional[str]
return _transform return _transform
def _transform_automod_trigger_metadata(
entry: AuditLogEntry, data: AutoModerationTriggerMetadata
) -> Optional[AutoModTrigger]:
# Try to get trigger type from target.trigger or infer from keys in data
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
_type = entry.target.trigger.type.value
elif not data:
_type = enums.AutoModRuleTriggerType.spam.value
elif 'presets' in data:
_type = enums.AutoModRuleTriggerType.keyword_preset.value
elif 'keyword_filter' in data or 'regex_patterns' in data:
_type = enums.AutoModRuleTriggerType.keyword.value
elif 'mention_total_limit' in data or 'mention_raid_protection_enabled' in data:
_type = enums.AutoModRuleTriggerType.mention_spam.value
else:
# some unknown type
_type = -1
return AutoModTrigger.from_data(type=_type, data=data)
def _transform_automod_actions(entry: AuditLogEntry, data: List[AutoModerationAction]) -> List[AutoModRuleAction]: def _transform_automod_actions(entry: AuditLogEntry, data: List[AutoModerationAction]) -> List[AutoModRuleAction]:
return [AutoModRuleAction.from_data(action) for action in data] return [AutoModRuleAction.from_data(action) for action in data]
@ -357,7 +334,6 @@ class AuditLogChanges:
'image_hash': ('cover_image', _transform_cover_image), 'image_hash': ('cover_image', _transform_cover_image),
'trigger_type': (None, _enum_transformer(enums.AutoModRuleTriggerType)), 'trigger_type': (None, _enum_transformer(enums.AutoModRuleTriggerType)),
'event_type': (None, _enum_transformer(enums.AutoModRuleEventType)), 'event_type': (None, _enum_transformer(enums.AutoModRuleEventType)),
'trigger_metadata': ('trigger', _transform_automod_trigger_metadata),
'actions': (None, _transform_automod_actions), 'actions': (None, _transform_automod_actions),
'exempt_channels': (None, _transform_channels_or_threads), 'exempt_channels': (None, _transform_channels_or_threads),
'exempt_roles': (None, _transform_roles), 'exempt_roles': (None, _transform_roles),
@ -403,6 +379,21 @@ class AuditLogChanges:
self._handle_role(self.after, self.before, entry, elem['new_value']) # type: ignore # new_value is a list of roles in this case self._handle_role(self.after, self.before, entry, elem['new_value']) # type: ignore # new_value is a list of roles in this case
continue continue
# special case for automod trigger
if attr == 'trigger_metadata':
# given full metadata dict
self._handle_trigger_metadata(entry, elem, data) # type: ignore # should be trigger metadata
continue
elif entry.action is enums.AuditLogAction.automod_rule_update and attr.startswith('$'):
# on update, some trigger attributes are keys and formatted as $(add/remove)_{attribute}
action, _, trigger_attr = attr.partition('_')
# new_value should be a list of added/removed strings for keyword_filter, regex_patterns, or allow_list
if action == '$add':
self._handle_trigger_attr_update(self.before, self.after, entry, trigger_attr, elem['new_value']) # type: ignore
elif action == '$remove':
self._handle_trigger_attr_update(self.after, self.before, entry, trigger_attr, elem['new_value']) # type: ignore
continue
try: try:
key, transformer = self.TRANSFORMERS[attr] key, transformer = self.TRANSFORMERS[attr]
except (ValueError, KeyError): except (ValueError, KeyError):
@ -479,6 +470,76 @@ class AuditLogChanges:
guild = entry.guild guild = entry.guild
diff.app_command_permissions.append(AppCommandPermissions(data=data, guild=guild, state=state)) diff.app_command_permissions.append(AppCommandPermissions(data=data, guild=guild, state=state))
def _handle_trigger_metadata(
self,
entry: AuditLogEntry,
data: AuditLogChangeTriggerMetadataPayload,
full_data: List[AuditLogChangePayload],
):
trigger_value: Optional[int] = None
trigger_type: Optional[enums.AutoModRuleTriggerType] = None
# try to get trigger type from before or after
trigger_type = getattr(self.before, 'trigger_type', getattr(self.after, 'trigger_type', None))
if trigger_type is None:
if isinstance(entry.target, AutoModRule):
# Trigger type cannot be changed, so it should be the same before and after updates.
# Avoids checking which keys are in data to guess trigger type
trigger_value = entry.target.trigger.type.value
else:
# found a trigger type from before or after
trigger_value = trigger_type.value
if trigger_value is None:
# try to find trigger type in the full list of changes
_elem = utils.find(lambda elem: elem['key'] == 'trigger_type', full_data)
if _elem is not None:
trigger_value = _elem.get('old_value', _elem.get('new_value')) # type: ignore # trigger type values should be int
if trigger_value is None:
# try to infer trigger_type from the keys in old or new value
combined = (data.get('old_value') or {}).keys() | (data.get('new_value') or {}).keys()
if not combined:
trigger_value = enums.AutoModRuleTriggerType.spam.value
elif 'presets' in combined:
trigger_value = enums.AutoModRuleTriggerType.keyword_preset.value
elif 'keyword_filter' in combined or 'regex_patterns' in combined:
trigger_value = enums.AutoModRuleTriggerType.keyword.value
elif 'mention_total_limit' in combined or 'mention_raid_protection_enabled' in combined:
trigger_value = enums.AutoModRuleTriggerType.mention_spam.value
else:
# some unknown type
trigger_value = -1
self.before.trigger = AutoModTrigger.from_data(trigger_value, data.get('old_value'))
self.after.trigger = AutoModTrigger.from_data(trigger_value, data.get('new_value'))
def _handle_trigger_attr_update(
self, first: AuditLogDiff, second: AuditLogDiff, entry: AuditLogEntry, attr: str, data: List[str]
):
self._create_trigger(first, entry)
trigger = self._create_trigger(second, entry)
try:
# guard unexpecte non list attributes or non iterable data
getattr(trigger, attr).extend(data)
except (AttributeError, TypeError):
pass
def _create_trigger(self, diff: AuditLogDiff, entry: AuditLogEntry) -> AutoModTrigger:
# check if trigger has already been created
if not hasattr(diff, 'trigger'):
# create a trigger
if isinstance(entry.target, AutoModRule):
# get trigger type from the automod rule
trigger_type = entry.target.trigger.type
else:
# unknown trigger type
trigger_type = enums.try_enum(enums.AutoModRuleTriggerType, -1)
diff.trigger = AutoModTrigger(type=trigger_type)
return diff.trigger
class _AuditLogProxy: class _AuditLogProxy:
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Any) -> None:

8
discord/types/audit_log.py

@ -37,6 +37,7 @@ from .role import Role
from .channel import ChannelType, DefaultReaction, PrivacyLevel, VideoQualityMode, PermissionOverwrite, ForumTag from .channel import ChannelType, DefaultReaction, PrivacyLevel, VideoQualityMode, PermissionOverwrite, ForumTag
from .threads import Thread from .threads import Thread
from .command import ApplicationCommand, ApplicationCommandPermissions from .command import ApplicationCommand, ApplicationCommandPermissions
from .automod import AutoModerationTriggerMetadata
AuditLogEvent = Literal[ AuditLogEvent = Literal[
1, 1,
@ -278,6 +279,12 @@ class _AuditLogChange_DefaultReactionEmoji(TypedDict):
old_value: Optional[DefaultReaction] old_value: Optional[DefaultReaction]
class _AuditLogChange_TriggerMetadata(TypedDict):
key: Literal['trigger_metadata']
new_value: Optional[AutoModerationTriggerMetadata]
old_value: Optional[AutoModerationTriggerMetadata]
AuditLogChange = Union[ AuditLogChange = Union[
_AuditLogChange_Str, _AuditLogChange_Str,
_AuditLogChange_AssetHash, _AuditLogChange_AssetHash,
@ -300,6 +307,7 @@ AuditLogChange = Union[
_AuditLogChange_AppliedTags, _AuditLogChange_AppliedTags,
_AuditLogChange_AvailableTags, _AuditLogChange_AvailableTags,
_AuditLogChange_DefaultReactionEmoji, _AuditLogChange_DefaultReactionEmoji,
_AuditLogChange_TriggerMetadata,
] ]

6
docs/api.rst

@ -4132,6 +4132,12 @@ AuditLogDiff
The trigger for the automod rule. The trigger for the automod rule.
.. note ::
The :attr:`~AutoModTrigger.type` of the trigger may be incorrect.
Some attributes such as :attr:`~AutoModTrigger.keyword_filter`, :attr:`~AutoModTrigger.regex_patterns`,
and :attr:`~AutoModTrigger.allow_list` will only have the added or removed values.
:type: :class:`AutoModTrigger` :type: :class:`AutoModTrigger`
.. attribute:: actions .. attribute:: actions

Loading…
Cancel
Save