diff --git a/CHANGELOG.md b/CHANGELOG.md index 84ff6f2ce5..ff7b529989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ These changes are available on the `master` branch, but have not yet been releas ## Added +- Added new AutoMod trigger metadata properties `regex_patterns`, `allow_list`, and + `mention_total_limit`; and added the `mention_spam` trigger type. + ([#1809](https://github.com/Pycord-Development/pycord/pull/1809)) +- Added missing `image` parameter to `Guild.create_scheduled_event()` method. + ([#1831](https://github.com/Pycord-Development/pycord/pull/1831)) - New `ApplicationRoleConnectionMetadata` class for application role connection metadata, along with the `fetch_role_connection_metadata_records` and `update_role_connection_metadata_records` methods in `Client`. diff --git a/discord/automod.py b/discord/automod.py index 3d85b33a7e..5f091d0f65 100644 --- a/discord/automod.py +++ b/discord/automod.py @@ -39,7 +39,12 @@ from .mixins import Hashable from .object import Object -__all__ = ("AutoModRule",) +__all__ = ( + "AutoModRule", + "AutoModAction", + "AutoModActionMetadata", + "AutoModTriggerMetadata", +) if TYPE_CHECKING: from .abc import Snowflake @@ -167,35 +172,76 @@ def __repr__(self) -> str: class AutoModTriggerMetadata: - """Represents a rule's trigger metadata. - - Depending on the trigger type, different attributes will be used. + r"""Represents a rule's trigger metadata, defining additional data used to determine when a rule triggers. + + Depending on the trigger type, different metadata attributes will be used: + + +-----------------------------+--------------------------------------------------------------------------------+ + | Attribute | Trigger Types | + +=============================+================================================================================+ + | :attr:`keyword_filter` | :attr:`AutoModTriggerType.keyword` | + +-----------------------------+--------------------------------------------------------------------------------+ + | :attr:`regex_patterns` | :attr:`AutoModTriggerType.keyword` | + +-----------------------------+--------------------------------------------------------------------------------+ + | :attr:`presets` | :attr:`AutoModTriggerType.keyword_preset` | + +-----------------------------+--------------------------------------------------------------------------------+ + | :attr:`allow_list` | :attr:`AutoModTriggerType.keyword`\, :attr:`AutoModTriggerType.keyword_preset` | + +-----------------------------+--------------------------------------------------------------------------------+ + | :attr:`mention_total_limit` | :attr:`AutoModTriggerType.mention_spam` | + +-----------------------------+--------------------------------------------------------------------------------+ + + Each attribute has limits that may change based on the trigger type. + See `here `_ + for information on attribute limits. .. versionadded:: 2.0 Attributes ---------- keyword_filter: List[:class:`str`] - A list of substrings to filter. Only for triggers of type :attr:`AutoModTriggerType.keyword`. + A list of substrings to filter. + + regex_patterns: List[:class:`str`] + A list of regex patterns to filter using Rust-flavored regex, which is not + fully compatible with regex syntax supported by the builtin `re` module. + + .. versionadded:: 2.4 + presets: List[:class:`AutoModKeywordPresetType`] - A list of keyword presets to filter. Only for triggers of type :attr:`AutoModTriggerType.keyword_preset`. - """ + A list of preset keyword sets to filter. - # maybe add a table of action types and attributes? - # wording for presets could change + allow_list: List[:class:`str`] + A list of substrings to allow, overriding keyword and regex matches. + + .. versionadded:: 2.4 + + mention_total_limit: :class:`int` + The total number of unique role and user mentions allowed. + + .. versionadded:: 2.4 + """ __slots__ = ( "keyword_filter", + "regex_patterns", "presets", + "allow_list", + "mention_total_limit", ) def __init__( self, keyword_filter: list[str] = MISSING, + regex_patterns: list[str] = MISSING, presets: list[AutoModKeywordPresetType] = MISSING, + allow_list: list[str] = MISSING, + mention_total_limit: int = MISSING, ): self.keyword_filter = keyword_filter + self.regex_patterns = regex_patterns self.presets = presets + self.allow_list = allow_list + self.mention_total_limit = mention_total_limit def to_dict(self) -> dict: data = {} @@ -203,9 +249,18 @@ def to_dict(self) -> dict: if self.keyword_filter is not MISSING: data["keyword_filter"] = self.keyword_filter + if self.regex_patterns is not MISSING: + data["regex_patterns"] = self.regex_patterns + if self.presets is not MISSING: data["presets"] = [wordset.value for wordset in self.presets] + if self.allow_list is not MISSING: + data["allow_list"] = self.allow_list + + if self.mention_total_limit is not MISSING: + data["mention_total_limit"] = self.mention_total_limit + return data @classmethod @@ -215,17 +270,29 @@ def from_dict(cls, data: AutoModTriggerMetadataPayload): if (keyword_filter := data.get("keyword_filter")) is not None: kwargs["keyword_filter"] = keyword_filter + if (regex_patterns := data.get("regex_patterns")) is not None: + kwargs["regex_patterns"] = regex_patterns + if (presets := data.get("presets")) is not None: kwargs["presets"] = [ try_enum(AutoModKeywordPresetType, wordset) for wordset in presets ] + if (allow_list := data.get("allow_list")) is not None: + kwargs["allow_list"] = allow_list + + if (mention_total_limit := data.get("mention_total_limit")) is not None: + kwargs["mention_total_limit"] = mention_total_limit + return cls(**kwargs) def __repr__(self) -> str: repr_attrs = ( "keyword_filter", + "regex_patterns", "presets", + "allow_list", + "mention_total_limit", ) inner = [] diff --git a/discord/enums.py b/discord/enums.py index d221400374..61bcd9b240 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -881,6 +881,7 @@ class AutoModTriggerType(Enum): harmful_link = 2 spam = 3 keyword_preset = 4 + mention_spam = 5 class AutoModEventType(Enum): diff --git a/discord/guild.py b/discord/guild.py index b3436b16c9..fb876be2d6 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -2156,13 +2156,14 @@ async def fetch_channel(self, channel_id: int, /) -> GuildChannel | Thread: def bans( self, limit: int | None = None, - before: SnowflakeTime | None = None, - after: SnowflakeTime | None = None, + before: Snowflake | None = None, + after: Snowflake | None = None, ) -> BanIterator: """|coro| Retrieves an :class:`.AsyncIterator` that enables receiving the guild's bans. In order to use this, you must have the :attr:`~Permissions.ban_members` permission. + Users will always be returned in ascending order sorted by user ID. If both the ``before`` and ``after`` parameters are provided, only before is respected. .. versionchanged:: 2.0 The ``limit``, ``before``. and ``after`` parameters were added. Now returns a :class:`.BanIterator` instead @@ -2174,14 +2175,10 @@ def bans( ---------- limit: Optional[:class:`int`] The number of bans to retrieve. Defaults to 1000. - before: Optional[Union[:class:`.abc.Snowflake`, :class:`datetime.datetime`]] - Retrieve bans before this date or object. - If a datetime is provided, it is recommended to use a UTC aware datetime. - If the datetime is naive, it is assumed to be local time. - after: Optional[Union[:class:`.abc.Snowflake`, :class:`datetime.datetime`]] - Retrieve bans after this date or object. - If a datetime is provided, it is recommended to use a UTC aware datetime. - If the datetime is naive, it is assumed to be local time. + before: Optional[:class:`.abc.Snowflake`] + Retrieve bans before the given user. + after: Optional[:class:`.abc.Snowflake`] + Retrieve bans after the given user. Yields ------ @@ -3650,6 +3647,7 @@ async def create_scheduled_event( location: str | int | VoiceChannel | StageChannel | ScheduledEventLocation, privacy_level: ScheduledEventPrivacyLevel = ScheduledEventPrivacyLevel.guild_only, reason: str | None = None, + image: bytes = MISSING, ) -> ScheduledEvent | None: """|coro| Creates a scheduled event. @@ -3672,6 +3670,8 @@ async def create_scheduled_event( so there is no need to change this parameter. reason: Optional[:class:`str`] The reason to show in the audit log. + image: Optional[:class:`bytes`] + The cover image of the scheduled event Returns ------- @@ -3709,6 +3709,9 @@ async def create_scheduled_event( if end_time is not MISSING: payload["scheduled_end_time"] = end_time.isoformat() + if image is not MISSING: + payload["image"] = utils._bytes_to_base64_data(image) + data = await self._state.http.create_scheduled_event( guild_id=self.id, reason=reason, **payload ) diff --git a/discord/http.py b/discord/http.py index d9533898d5..9759e45722 100644 --- a/discord/http.py +++ b/discord/http.py @@ -2232,6 +2232,7 @@ def create_scheduled_event( "description", "entity_type", "entity_metadata", + "image", ) payload = {k: v for k, v in payload.items() if k in valid_keys} diff --git a/discord/iterators.py b/discord/iterators.py index f4ec6cda28..2375b48677 100644 --- a/discord/iterators.py +++ b/discord/iterators.py @@ -689,11 +689,6 @@ def create_member(self, data): class BanIterator(_AsyncIterator["BanEntry"]): def __init__(self, guild, limit=None, before=None, after=None): - if isinstance(after, datetime.datetime): - after = Object(id=time_snowflake(after, high=True)) - - if isinstance(before, datetime.datetime): - before = Object(id=time_snowflake(before, high=True)) self.guild = guild self.limit = limit diff --git a/discord/opus.py b/discord/opus.py index 84e3cea612..abb64029d3 100644 --- a/discord/opus.py +++ b/discord/opus.py @@ -147,7 +147,7 @@ def _err_ne(result: T, func: Callable, args: list) -> T: # The fourth is the error handler. exported_functions: list[tuple[Any, ...]] = [ # Generic - ("opus_get_version_string", None, ctypes.c_char_p, None), + ("opus_get_version_string", [], ctypes.c_char_p, None), ("opus_strerror", [ctypes.c_int], ctypes.c_char_p, None), # Encoder functions ("opus_encoder_get_size", [ctypes.c_int], ctypes.c_int, None), @@ -169,7 +169,7 @@ def _err_ne(result: T, func: Callable, args: list) -> T: ctypes.c_int32, _err_lt, ), - ("opus_encoder_ctl", None, ctypes.c_int32, _err_lt), + ("opus_encoder_ctl", [EncoderStructPtr, ctypes.c_int], ctypes.c_int32, _err_lt), ("opus_encoder_destroy", [EncoderStructPtr], None, None), # Decoder functions ("opus_decoder_get_size", [ctypes.c_int], ctypes.c_int, None), @@ -205,7 +205,7 @@ def _err_ne(result: T, func: Callable, args: list) -> T: ctypes.c_int, _err_lt, ), - ("opus_decoder_ctl", None, ctypes.c_int32, _err_lt), + ("opus_decoder_ctl", [DecoderStructPtr, ctypes.c_int], ctypes.c_int32, _err_lt), ("opus_decoder_destroy", [DecoderStructPtr], None, None), ( "opus_decoder_get_nb_samples", diff --git a/discord/raw_models.py b/discord/raw_models.py index 65f9d8f151..3353c4c17e 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -28,7 +28,7 @@ import datetime from typing import TYPE_CHECKING -from .automod import AutoModAction +from .automod import AutoModAction, AutoModTriggerType from .enums import ChannelType, try_enum if TYPE_CHECKING: @@ -409,6 +409,10 @@ class AutoModActionExecutionEvent: The action that was executed. rule_id: :class:`int` The ID of the rule that the action belongs to. + rule_trigger_type: :class:`AutoModTriggerType` + The category of trigger the rule belongs to. + + .. versionadded:: 2.4 guild_id: :class:`int` The ID of the guild that the action was executed in. guild: Optional[:class:`Guild`] @@ -443,6 +447,7 @@ class AutoModActionExecutionEvent: __slots__ = ( "action", "rule_id", + "rule_trigger_type", "guild_id", "guild", "user_id", @@ -461,6 +466,9 @@ class AutoModActionExecutionEvent: def __init__(self, state: ConnectionState, data: AutoModActionExecution) -> None: self.action: AutoModAction = AutoModAction.from_dict(data["action"]) self.rule_id: int = int(data["rule_id"]) + self.rule_trigger_type: AutoModTriggerType = try_enum( + AutoModTriggerType, int(data["rule_trigger_type"]) + ) self.guild_id: int = int(data["guild_id"]) self.guild: Guild | None = state._get_guild(self.guild_id) self.user_id: int = int(data["user_id"]) diff --git a/discord/types/automod.py b/discord/types/automod.py index 1a1542c06e..4632c8d8c4 100644 --- a/discord/types/automod.py +++ b/discord/types/automod.py @@ -27,7 +27,7 @@ from .._typed_dict import NotRequired, TypedDict from .snowflake import Snowflake -AutoModTriggerType = Literal[1, 2, 3, 4] +AutoModTriggerType = Literal[1, 2, 3, 4, 5] AutoModEventType = Literal[1] @@ -38,7 +38,10 @@ class AutoModTriggerMetadata(TypedDict, total=False): keyword_filter: list[str] + regex_patterns: list[str] presets: list[AutoModKeywordPresetType] + allow_list: list[str] + mention_total_limit: int class AutoModActionMetadata(TypedDict, total=False): diff --git a/docs/api/enums.rst b/docs/api/enums.rst index 9107841e77..5d89ce0f56 100644 --- a/docs/api/enums.rst +++ b/docs/api/enums.rst @@ -1912,3 +1912,97 @@ of :class:`enum.Enum`. .. attribute:: boolean_not_equal The metadata value (``integer``) is not equal to the guild's configured value (``integer``; 1). + +.. class:: AutoModTriggerType + + Represents an AutoMod trigger type. + + .. versionadded:: 2.0 + + .. attribute:: keyword + + Represents a keyword rule trigger, which are customizable by a guild. + + Possible attributes for :class:`AutoModTriggerMetadata`: + + - :attr:`~AutoModTriggerMetadata.keyword_filter` + - :attr:`~AutoModTriggerMetadata.regex_patterns` + - :attr:`~AutoModTriggerMetadata.allow_list` + + .. attribute:: keyword_preset + + Represents a preset keyword rule trigger. + + Possible attributes for :class:`AutoModTriggerMetadata`: + + - :attr:`~AutoModTriggerMetadata.presets` + - :attr:`~AutoModTriggerMetadata.allow_list` + + .. attribute:: spam + + Represents the spam rule trigger. + + There are no possible attributes for :class:`AutoModTriggerMetadata`. + + .. attribute:: mention_spam + + Represents a mention spam keyword rule trigger. + + Possible attributes for :class:`AutoModTriggerMetadata`: + + - :attr:`~AutoModTriggerMetadata.mention_total_limit` + + .. versionadded:: 2.4 + + .. attribute:: harmful_link + + Represents a harmful link rule trigger. + + .. deprecated:: 2.4 + Removed by Discord and merged into :attr:`spam`. + +.. class:: AutoModEventType + + Represents an AutoMod event type. + + .. versionadded:: 2.0 + + .. attribute:: message_send + + Represents a message send AutoMod event. + +.. class:: AutoModActionType + + Represents the type of action AutoMod is performing. + + .. versionadded:: 2.0 + + .. attribute:: block_message + + Represents a block message action. + + .. attribute:: send_alert_message + + Represents a send alert message action. + + .. attribute:: timeout + + Represents a timeout action. + +.. class:: AutoModKeywordPresetType + + Represents an AutoMod keyword preset type. + + .. versionadded:: 2.0 + + .. attribute:: profanity + + Represents the profanity keyword preset rule. + + .. attribute:: sexual_content + + Represents the sexual content keyword preset rule. + + .. attribute:: slurs + + Represents the slurs keyword preset rule. diff --git a/docs/api/models.rst b/docs/api/models.rst index df13a6c90e..d4520da8b2 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -146,11 +146,29 @@ Guild .. autoclass:: Template() :members: +AutoMod +~~~~~~~ + .. attributetable:: AutoModRule .. autoclass:: AutoModRule() :members: +.. attributetable:: AutoModAction + +.. autoclass:: AutoModAction() + :members: + +.. attributetable:: AutoModActionMetadata + +.. autoclass:: AutoModActionMetadata() + :members: + +.. attributetable:: AutoModTriggerMetadata + +.. autoclass:: AutoModTriggerMetadata() + :members: + Invites ~~~~~~~ diff --git a/requirements/dev.txt b/requirements/dev.txt index 867c487ea7..c25710c322 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,10 +1,10 @@ -r _.txt -pylint~=2.15.8 +pylint~=2.15.9 pytest~=7.2.0 pytest-asyncio~=0.20.3 # pytest-order~=1.0.1 mypy~=0.991 -coverage~=6.5 +coverage~=7.0 pre-commit==2.20.0 codespell==2.2.2 bandit==1.7.4 diff --git a/requirements/docs.txt b/requirements/docs.txt index 343fc649c9..feeef1ed63 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -2,7 +2,7 @@ sphinx==5.3.0 sphinxcontrib_trio==1.1.2 sphinxcontrib-websupport==1.2.4 myst-parser==0.18.1 -sphinxext-opengraph==0.7.3 +sphinxext-opengraph==0.7.5 sphinx-copybutton==0.5.1 furo@ git+https://github.com/BobDotCom/furo@temp sphinx-autodoc-typehints==1.19.5