Skip to content

Commit

Permalink
Merge branch 'master' into voice-status-rest
Browse files Browse the repository at this point in the history
  • Loading branch information
Puncher1 authored Oct 21, 2023
2 parents 1b40e06 + e1c1a72 commit 125f5c2
Show file tree
Hide file tree
Showing 16 changed files with 963 additions and 54 deletions.
1 change: 1 addition & 0 deletions discord/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from .invite import *
from .template import *
from .welcome_screen import *
from .sku import *
from .widget import *
from .object import *
from .reaction import *
Expand Down
39 changes: 15 additions & 24 deletions discord/audit_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from .mixins import Hashable
from .object import Object
from .permissions import PermissionOverwrite, Permissions
from .automod import AutoModTrigger, AutoModRuleAction, AutoModPresets, AutoModRule
from .automod import AutoModTrigger, AutoModRuleAction, AutoModRule
from .role import Role
from .emoji import Emoji
from .partial_emoji import PartialEmoji
Expand Down Expand Up @@ -234,33 +234,24 @@ 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
# or returning None if data is empty.
try:
return AutoModTrigger.from_data(type=entry.target.trigger.type.value, data=data)
except Exception:
pass

# Try to infer trigger type from available keys in data
if 'presets' in data:
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(
type=enums.AutoModRuleTriggerType.keyword,
keyword_filter=data['keyword_filter'], # type: ignore
allow_list=data.get('allow_list'),
regex_patterns=data.get('regex_patterns'),
)
elif 'mention_total_limit' in data:
return AutoModTrigger(type=enums.AutoModRuleTriggerType.mention_spam, mention_limit=data['mention_total_limit']) # type: ignore
_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:
return AutoModTrigger(type=enums.AutoModRuleTriggerType.spam)
# some unknown type
_type = -1

return AutoModTrigger.from_data(type=_type, data=data)


def _transform_automod_actions(entry: AuditLogEntry, data: List[AutoModerationAction]) -> List[AutoModRuleAction]:
Expand Down
118 changes: 95 additions & 23 deletions discord/automod.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from __future__ import annotations
import datetime

from typing import TYPE_CHECKING, Any, Dict, Optional, List, Set, Union, Sequence, overload
from typing import TYPE_CHECKING, Any, Dict, Optional, List, Set, Union, Sequence, overload, Literal

from .enums import AutoModRuleTriggerType, AutoModRuleActionType, AutoModRuleEventType, try_enum
from .flags import AutoModPresets
Expand Down Expand Up @@ -85,36 +85,81 @@ class AutoModRuleAction:
__slots__ = ('type', 'channel_id', 'duration', 'custom_message')

@overload
def __init__(self, *, channel_id: Optional[int] = ...) -> None:
def __init__(self, *, channel_id: int = ...) -> None:
...

@overload
def __init__(self, *, duration: Optional[datetime.timedelta] = ...) -> None:
def __init__(self, *, type: Literal[AutoModRuleActionType.send_alert_message], channel_id: int = ...) -> None:
...

@overload
def __init__(self, *, custom_message: Optional[str] = ...) -> None:
def __init__(self, *, duration: datetime.timedelta = ...) -> None:
...

@overload
def __init__(self, *, type: Literal[AutoModRuleActionType.timeout], duration: datetime.timedelta = ...) -> None:
...

@overload
def __init__(self, *, custom_message: str = ...) -> None:
...

@overload
def __init__(self, *, type: Literal[AutoModRuleActionType.block_message]) -> None:
...

@overload
def __init__(self, *, type: Literal[AutoModRuleActionType.block_message], custom_message: Optional[str] = ...) -> None:
...

@overload
def __init__(
self,
*,
type: Optional[AutoModRuleActionType] = ...,
channel_id: Optional[int] = ...,
duration: Optional[datetime.timedelta] = ...,
custom_message: Optional[str] = ...,
) -> None:
...

def __init__(
self,
*,
type: Optional[AutoModRuleActionType] = None,
channel_id: Optional[int] = None,
duration: Optional[datetime.timedelta] = None,
custom_message: Optional[str] = None,
) -> None:
self.channel_id: Optional[int] = channel_id
self.duration: Optional[datetime.timedelta] = duration
self.custom_message: Optional[str] = custom_message

if sum(v is None for v in (channel_id, duration, custom_message)) < 2:
raise ValueError('Only one of channel_id, duration, or custom_message can be passed.')

self.type: AutoModRuleActionType = AutoModRuleActionType.block_message
if channel_id:
self.type: AutoModRuleActionType
self.channel_id: Optional[int] = None
self.duration: Optional[datetime.timedelta] = None
self.custom_message: Optional[str] = None

if type is not None:
self.type = type
elif channel_id is not None:
self.type = AutoModRuleActionType.send_alert_message
elif duration:
elif duration is not None:
self.type = AutoModRuleActionType.timeout
else:
self.type = AutoModRuleActionType.block_message

if self.type is AutoModRuleActionType.send_alert_message:
if channel_id is None:
raise ValueError('channel_id cannot be None if type is send_alert_message')
self.channel_id = channel_id

if self.type is AutoModRuleActionType.timeout:
if duration is None:
raise ValueError('duration cannot be None set if type is timeout')
self.duration = duration

if self.type is AutoModRuleActionType.block_message:
self.custom_message = custom_message

def __repr__(self) -> str:
return f'<AutoModRuleAction type={self.type.value} channel={self.channel_id} duration={self.duration}>'
Expand All @@ -127,7 +172,11 @@ def from_data(cls, data: AutoModerationActionPayload) -> Self:
elif data['type'] == AutoModRuleActionType.send_alert_message.value:
channel_id = int(data['metadata']['channel_id'])
return cls(channel_id=channel_id)
return cls(custom_message=data.get('metadata', {}).get('custom_message'))
elif data['type'] == AutoModRuleActionType.block_message.value:
custom_message = data.get('metadata', {}).get('custom_message')
return cls(type=AutoModRuleActionType.block_message, custom_message=custom_message)

return cls(type=AutoModRuleActionType.block_member_interactions)

def to_dict(self) -> Dict[str, Any]:
ret = {'type': self.type.value, 'metadata': {}}
Expand Down Expand Up @@ -155,7 +204,11 @@ class AutoModTrigger:
+-----------------------------------------------+------------------------------------------------+
| :attr:`AutoModRuleTriggerType.keyword_preset` | :attr:`presets`\, :attr:`allow_list` |
+-----------------------------------------------+------------------------------------------------+
| :attr:`AutoModRuleTriggerType.mention_spam` | :attr:`mention_limit` |
| :attr:`AutoModRuleTriggerType.mention_spam` | :attr:`mention_limit`, |
| | :attr:`mention_raid_protection` |
+-----------------------------------------------+------------------------------------------------+
| :attr:`AutoModRuleTriggerType.member_profile` | :attr:`keyword_filter`, :attr:`regex_patterns`,|
| | :attr:`allow_list` |
+-----------------------------------------------+------------------------------------------------+
.. versionadded:: 2.0
Expand All @@ -165,8 +218,8 @@ class AutoModTrigger:
type: :class:`AutoModRuleTriggerType`
The type of trigger.
keyword_filter: List[:class:`str`]
The list of strings that will trigger the keyword filter. Maximum of 1000.
Keywords can only be up to 60 characters in length.
The list of strings that will trigger the filter.
Maximum of 1000. Keywords can only be up to 60 characters in length.
This could be combined with :attr:`regex_patterns`.
regex_patterns: List[:class:`str`]
Expand All @@ -185,6 +238,10 @@ class AutoModTrigger:
mention_limit: :class:`int`
The total number of user and role mentions a message can contain.
Has a maximum of 50.
mention_raid_protection: :class:`bool`
Whether mention raid protection is enabled or not.
.. versionadded:: 2.4
"""

__slots__ = (
Expand All @@ -194,6 +251,7 @@ class AutoModTrigger:
'allow_list',
'mention_limit',
'regex_patterns',
'mention_raid_protection',
)

def __init__(
Expand All @@ -205,27 +263,32 @@ def __init__(
allow_list: Optional[List[str]] = None,
mention_limit: Optional[int] = None,
regex_patterns: Optional[List[str]] = None,
mention_raid_protection: Optional[bool] = None,
) -> None:
if type is None and sum(arg is not None for arg in (keyword_filter or regex_patterns, presets, mention_limit)) > 1:
raise ValueError('Please pass only one of keyword_filter, regex_patterns, presets, or mention_limit.')
unique_args = (keyword_filter or regex_patterns, presets, mention_limit or mention_raid_protection)
if type is None and sum(arg is not None for arg in unique_args) > 1:
raise ValueError(
'Please pass only one of keyword_filter/regex_patterns, presets, or mention_limit/mention_raid_protection.'
)

if type is not None:
self.type = type
elif keyword_filter is not None or regex_patterns is not None:
self.type = AutoModRuleTriggerType.keyword
elif presets is not None:
self.type = AutoModRuleTriggerType.keyword_preset
elif mention_limit is not None:
elif mention_limit is not None or mention_raid_protection is not None:
self.type = AutoModRuleTriggerType.mention_spam
else:
raise ValueError(
'Please pass the trigger type explicitly if not using keyword_filter, presets, or mention_limit.'
'Please pass the trigger type explicitly if not using keyword_filter, regex_patterns, presets, mention_limit, or mention_raid_protection.'
)

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
self.mention_raid_protection: bool = mention_raid_protection if mention_raid_protection is not None else False
self.regex_patterns: List[str] = regex_patterns if regex_patterns is not None else []

def __repr__(self) -> str:
Expand All @@ -241,7 +304,7 @@ def from_data(cls, type: int, data: Optional[AutoModerationTriggerMetadataPayloa
type_ = try_enum(AutoModRuleTriggerType, type)
if data is None:
return cls(type=type_)
elif type_ is AutoModRuleTriggerType.keyword:
elif type_ in (AutoModRuleTriggerType.keyword, AutoModRuleTriggerType.member_profile):
return cls(
type=type_,
keyword_filter=data.get('keyword_filter'),
Expand All @@ -253,12 +316,16 @@ def from_data(cls, type: int, data: Optional[AutoModerationTriggerMetadataPayloa
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'))
return cls(
type=type_,
mention_limit=data.get('mention_total_limit'),
mention_raid_protection=data.get('mention_raid_protection_enabled'),
)
else:
return cls(type=type_)

def to_metadata_dict(self) -> Optional[Dict[str, Any]]:
if self.type is AutoModRuleTriggerType.keyword:
if self.type in (AutoModRuleTriggerType.keyword, AutoModRuleTriggerType.member_profile):
return {
'keyword_filter': self.keyword_filter,
'regex_patterns': self.regex_patterns,
Expand All @@ -267,7 +334,10 @@ def to_metadata_dict(self) -> Optional[Dict[str, Any]]:
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}
return {
'mention_total_limit': self.mention_limit,
'mention_raid_protection_enabled': self.mention_raid_protection,
}


class AutoModRule:
Expand All @@ -293,6 +363,8 @@ class AutoModRule:
The IDs of the roles that are exempt from the rule.
exempt_channel_ids: Set[:class:`int`]
The IDs of the channels that are exempt from the rule.
event_type: :class:`AutoModRuleEventType`
The type of event that will trigger the the rule.
"""

__slots__ = (
Expand Down
Loading

0 comments on commit 125f5c2

Please sign in to comment.