From a5600fd2197be95547b290807e448bd2a31d7601 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:57:49 +0100 Subject: [PATCH 1/6] feat: Add embed (media) flags, and more attachment flags --- discord/embeds.py | 31 +++++++++- discord/flags.py | 137 +++++++++++++++++++++++++++++++++++++++++ discord/types/embed.py | 2 + docs/api.rst | 16 +++++ 4 files changed, 184 insertions(+), 2 deletions(-) diff --git a/discord/embeds.py b/discord/embeds.py index 258ef0dfd86c..0d050275a5dd 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -29,6 +29,7 @@ from . import utils from .colour import Colour +from .flags import EmbedFlags, EmbedMediaFlags # fmt: off __all__ = ( @@ -76,6 +77,7 @@ class _EmbedMediaProxy(Protocol): proxy_url: Optional[str] height: Optional[int] width: Optional[int] + flags: Optional[EmbedMediaFlags] class _EmbedVideoProxy(Protocol): url: Optional[str] @@ -146,6 +148,10 @@ class Embed: colour: Optional[Union[:class:`Colour`, :class:`int`]] The colour code of the embed. Aliased to ``color`` as well. This can be set during initialisation. + flags: Optional[:class:`EmbedFlags`] + This embed flags. + + .. versionadded:: 2.5 """ __slots__ = ( @@ -162,6 +168,7 @@ class Embed: '_author', '_fields', 'description', + 'flags', ) def __init__( @@ -174,6 +181,7 @@ def __init__( url: Optional[Any] = None, description: Optional[Any] = None, timestamp: Optional[datetime.datetime] = None, + flags: Optional[EmbedFlags] = None, ): self.colour = colour if colour is not None else color @@ -181,6 +189,7 @@ def __init__( self.type: EmbedType = type self.url: Optional[str] = url self.description: Optional[str] = description + self.flags: Optional[EmbedFlags] = flags if self.title is not None: self.title = str(self.title) @@ -245,6 +254,11 @@ def from_dict(cls, data: Mapping[str, Any]) -> Self: else: setattr(self, '_' + attr, value) + try: + self.flags = EmbedFlags._from_value(data['flags']) + except KeyError: + pass + return self def copy(self) -> Self: @@ -399,13 +413,17 @@ def image(self) -> _EmbedMediaProxy: - ``proxy_url`` - ``width`` - ``height`` + - ``flags`` If the attribute has no value then ``None`` is returned. """ # Lying to the type checker for better developer UX. - return EmbedProxy(getattr(self, '_image', {})) # type: ignore + data = getattr(self, '_image', {}) + if 'flags' in data: + data['flags'] = EmbedMediaFlags._from_value(data['flags']) + return EmbedProxy(data) # type: ignore - def set_image(self, *, url: Optional[Any]) -> Self: + def set_image(self, *, url: Optional[Any], flags: Optional[EmbedMediaFlags] = None) -> Self: """Sets the image for the embed content. This function returns the class instance to allow for fluent-style @@ -417,6 +435,11 @@ def set_image(self, *, url: Optional[Any]) -> Self: The source URL for the image. Only HTTP(S) is supported. If ``None`` is passed, any existing image is removed. Inline attachment URLs are also supported, see :ref:`local_image`. + flags: Optional[:class:`EmbedMediaFlags`] + The flags of the image. + If ``None`` is passed, any existing flags are removed. + + .. versionadded:: 2.5 """ if url is None: @@ -427,6 +450,7 @@ def set_image(self, *, url: Optional[Any]) -> Self: else: self._image = { 'url': str(url), + 'flags': flags.value if flags else None, } return self @@ -753,4 +777,7 @@ def to_dict(self) -> EmbedData: if self.title: result['title'] = self.title + if self.flags: + result['flags'] = self.flags.value + return result # type: ignore # This payload is equivalent to the EmbedData type diff --git a/discord/flags.py b/discord/flags.py index de806ba9c046..08c928b3713f 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -63,6 +63,7 @@ 'RoleFlags', 'AppInstallationType', 'SKUFlags', + 'EmbedFlags', ) BF = TypeVar('BF', bound='BaseFlags') @@ -2173,6 +2174,21 @@ def remix(self): """:class:`bool`: Returns ``True`` if the attachment has been edited using the remix feature.""" return 1 << 2 + @flag_value + def spoiler(self): + """:class:`bool`: Returns ``True`` if the attachment was marked as a spoiler.""" + return 1 << 3 + + @flag_value + def contains_explicit_media(self): + """:class:`bool`: Returns ``True`` if the attachment was flagged as sensitive content.""" + return 1 << 4 + + @flag_value + def animated(self): + """:class:`bool`: Returns ``True`` if the attachment is an animated image.""" + return 1 << 5 + @fill_with_flags() class RoleFlags(BaseFlags): @@ -2308,3 +2324,124 @@ def guild_subscription(self): def user_subscription(self): """:class:`bool`: Returns ``True`` if the SKU is a user subscription.""" return 1 << 8 + + +@fill_with_flags() +class EmbedFlags(BaseFlags): + r"""Wraps up the Discord Embed flags + + .. versionadded:: 2.5 + + .. container:: operations + + .. describe:: x == y + + Checks if two EmbedFlags are equal. + + .. describe:: x != y + + Checks if two EmbedFlags are not equal. + + .. describe:: x | y, x |= y + + Returns an EmbedFlags instance with all enabled flags from + both x and y. + + .. describe:: x ^ y, x ^= y + + Returns an EmbedFlags instance with only flags enabled on + only one of x or y, not on both. + + .. describe:: ~x + + Returns an EmbedFlags instance with all flags inverted from x. + + .. describe:: hash(x) + + Returns the flag's hash. + + .. describe:: iter(x) + + Returns an iterator of ``(name, value)`` pairs. This allows it + to be, for example, constructed as a dict or a list of pairs. + Note that aliases are not shown. + + .. describe:: bool(b) + + Returns whether any flag is set to ``True``. + + Attributes + ---------- + value: :class:`int` + The raw value. You should query flags via the properties + rather than using this raw value. + """ + + @flag_value + def contains_explicit_media(self): + """:class:`bool`: Returns ``True`` if the embed was flagged as sensitive content.""" + return 1 << 4 + + @flag_value + def content_inventory_entry(self): + """:class:`bool`: Returns ``True`` if the embed is a reply to an activity card, and is no + longer displayed. + """ + return 1 << 5 + + +@fill_with_flags() +class EmbedMediaFlags(BaseFlags): + r"""Wraps up the Discord Embed Media flags + + .. versionadded:: 2.5 + + .. container:: operations + + .. describe:: x == y + + Checks if two EmbedMediaFlags are equal. + + .. describe:: x != y + + Checks if two EmbedMediaFlags are not equal. + + .. describe:: x | y, x |= y + + Returns an EmbedMediaFlags instance with all enabled flags from + both x and y. + + .. describe:: x ^ y, x ^= y + + Returns an EmbedMediaFlags instance with only flags enabled on + only one of x or y, not on both. + + .. describe:: ~x + + Returns an EmbedMediaFlags instance with all flags inverted from x. + + .. describe:: hash(x) + + Returns the flag's hash. + + .. describe:: iter(x) + + Returns an iterator of ``(name, value)`` pairs. This allows it + to be, for example, constructed as a dict or a list of pairs. + Note that aliases are not shown. + + .. describe:: bool(b) + + Returns whether any flag is set to ``True``. + + Attributes + ---------- + value: :class:`int` + The raw value. You should query flags via the properties + rather than using this raw value. + """ + + @flag_value + def animated(self): + """:class:`bool`: Returns ``True`` if the media is animated.""" + return 1 << 5 diff --git a/discord/types/embed.py b/discord/types/embed.py index 376df3a1a7d0..f8354a3f3069 100644 --- a/discord/types/embed.py +++ b/discord/types/embed.py @@ -50,6 +50,7 @@ class EmbedVideo(TypedDict, total=False): proxy_url: str height: int width: int + flags: int class EmbedImage(TypedDict, total=False): @@ -88,3 +89,4 @@ class Embed(TypedDict, total=False): provider: EmbedProvider author: EmbedAuthor fields: List[EmbedField] + flags: int diff --git a/docs/api.rst b/docs/api.rst index 8da2ba80c894..56d5aacd434c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5708,6 +5708,22 @@ SKUFlags .. autoclass:: SKUFlags() :members: +EmbedFlags +~~~~~~~~~~ + +.. attributetable:: EmbedFlags + +.. autoclass:: EmbedFlags() + :members: + +EmbedMediaFlags +~~~~~~~~~~~~~~~ + +.. attributetable:: EmbedMediaFlags + +.. autoclass:: EmbedMediaFlags() + :members: + ForumTag ~~~~~~~~~ From f5005daf70e491e746bc6e4b9ca3236c88e46636 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:02:25 +0100 Subject: [PATCH 2/6] chore: add EmbedMediaFlags to __all__ --- discord/flags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/flags.py b/discord/flags.py index 08c928b3713f..0c389e1baf17 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -64,6 +64,7 @@ 'AppInstallationType', 'SKUFlags', 'EmbedFlags', + 'EmbedMediaFlags', ) BF = TypeVar('BF', bound='BaseFlags') From 1f37d6cc8014747e7ff44665b51f87f0978bc9a8 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sat, 25 Jan 2025 12:02:08 +0100 Subject: [PATCH 3/6] chore: Remove EmbedMediaFlags to use AttachmentFlags and remove flags params --- discord/embeds.py | 17 ++++---------- discord/flags.py | 58 ----------------------------------------------- docs/api.rst | 8 ------- 3 files changed, 5 insertions(+), 78 deletions(-) diff --git a/discord/embeds.py b/discord/embeds.py index 0d050275a5dd..7682f2338833 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -29,7 +29,7 @@ from . import utils from .colour import Colour -from .flags import EmbedFlags, EmbedMediaFlags +from .flags import AttachmentFlags, EmbedFlags # fmt: off __all__ = ( @@ -77,7 +77,7 @@ class _EmbedMediaProxy(Protocol): proxy_url: Optional[str] height: Optional[int] width: Optional[int] - flags: Optional[EmbedMediaFlags] + flags: Optional[AttachmentFlags] class _EmbedVideoProxy(Protocol): url: Optional[str] @@ -181,7 +181,6 @@ def __init__( url: Optional[Any] = None, description: Optional[Any] = None, timestamp: Optional[datetime.datetime] = None, - flags: Optional[EmbedFlags] = None, ): self.colour = colour if colour is not None else color @@ -189,7 +188,7 @@ def __init__( self.type: EmbedType = type self.url: Optional[str] = url self.description: Optional[str] = description - self.flags: Optional[EmbedFlags] = flags + self.flags: Optional[EmbedFlags] = None if self.title is not None: self.title = str(self.title) @@ -420,10 +419,10 @@ def image(self) -> _EmbedMediaProxy: # Lying to the type checker for better developer UX. data = getattr(self, '_image', {}) if 'flags' in data: - data['flags'] = EmbedMediaFlags._from_value(data['flags']) + data['flags'] = AttachmentFlags._from_value(data['flags']) return EmbedProxy(data) # type: ignore - def set_image(self, *, url: Optional[Any], flags: Optional[EmbedMediaFlags] = None) -> Self: + def set_image(self, *, url: Optional[Any]) -> Self: """Sets the image for the embed content. This function returns the class instance to allow for fluent-style @@ -435,11 +434,6 @@ def set_image(self, *, url: Optional[Any], flags: Optional[EmbedMediaFlags] = No The source URL for the image. Only HTTP(S) is supported. If ``None`` is passed, any existing image is removed. Inline attachment URLs are also supported, see :ref:`local_image`. - flags: Optional[:class:`EmbedMediaFlags`] - The flags of the image. - If ``None`` is passed, any existing flags are removed. - - .. versionadded:: 2.5 """ if url is None: @@ -450,7 +444,6 @@ def set_image(self, *, url: Optional[Any], flags: Optional[EmbedMediaFlags] = No else: self._image = { 'url': str(url), - 'flags': flags.value if flags else None, } return self diff --git a/discord/flags.py b/discord/flags.py index 0c389e1baf17..b28d4edd960b 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -64,7 +64,6 @@ 'AppInstallationType', 'SKUFlags', 'EmbedFlags', - 'EmbedMediaFlags', ) BF = TypeVar('BF', bound='BaseFlags') @@ -2389,60 +2388,3 @@ def content_inventory_entry(self): longer displayed. """ return 1 << 5 - - -@fill_with_flags() -class EmbedMediaFlags(BaseFlags): - r"""Wraps up the Discord Embed Media flags - - .. versionadded:: 2.5 - - .. container:: operations - - .. describe:: x == y - - Checks if two EmbedMediaFlags are equal. - - .. describe:: x != y - - Checks if two EmbedMediaFlags are not equal. - - .. describe:: x | y, x |= y - - Returns an EmbedMediaFlags instance with all enabled flags from - both x and y. - - .. describe:: x ^ y, x ^= y - - Returns an EmbedMediaFlags instance with only flags enabled on - only one of x or y, not on both. - - .. describe:: ~x - - Returns an EmbedMediaFlags instance with all flags inverted from x. - - .. describe:: hash(x) - - Returns the flag's hash. - - .. describe:: iter(x) - - Returns an iterator of ``(name, value)`` pairs. This allows it - to be, for example, constructed as a dict or a list of pairs. - Note that aliases are not shown. - - .. describe:: bool(b) - - Returns whether any flag is set to ``True``. - - Attributes - ---------- - value: :class:`int` - The raw value. You should query flags via the properties - rather than using this raw value. - """ - - @flag_value - def animated(self): - """:class:`bool`: Returns ``True`` if the media is animated.""" - return 1 << 5 diff --git a/docs/api.rst b/docs/api.rst index 56d5aacd434c..0b2edcd3db24 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5716,14 +5716,6 @@ EmbedFlags .. autoclass:: EmbedFlags() :members: -EmbedMediaFlags -~~~~~~~~~~~~~~~ - -.. attributetable:: EmbedMediaFlags - -.. autoclass:: EmbedMediaFlags() - :members: - ForumTag ~~~~~~~~~ From 8217a522bd286fe25c1cf5665f0759e9b991214b Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sat, 25 Jan 2025 12:06:16 +0100 Subject: [PATCH 4/6] chore: not add `flags` key on embed.to_dict --- discord/embeds.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/discord/embeds.py b/discord/embeds.py index 7682f2338833..4938dc978a90 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -770,7 +770,4 @@ def to_dict(self) -> EmbedData: if self.title: result['title'] = self.title - if self.flags: - result['flags'] = self.flags.value - return result # type: ignore # This payload is equivalent to the EmbedData type From 09c3afc43ccf34af2513a80174ec51bd4b549cf5 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 9 Feb 2025 11:03:36 +0100 Subject: [PATCH 5/6] Update discord/embeds.py Co-authored-by: Danny <1695103+Rapptz@users.noreply.github.com> --- discord/embeds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/embeds.py b/discord/embeds.py index 4938dc978a90..91de865b4e18 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -149,7 +149,7 @@ class Embed: The colour code of the embed. Aliased to ``color`` as well. This can be set during initialisation. flags: Optional[:class:`EmbedFlags`] - This embed flags. + The flags of this embed. .. versionadded:: 2.5 """ From 733428ed77377566b9fd01685c279c2fafa8297d Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 9 Feb 2025 11:05:02 +0100 Subject: [PATCH 6/6] chore: Add versionadded's --- discord/flags.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/discord/flags.py b/discord/flags.py index b28d4edd960b..20f8c5470fee 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -2176,17 +2176,26 @@ def remix(self): @flag_value def spoiler(self): - """:class:`bool`: Returns ``True`` if the attachment was marked as a spoiler.""" + """:class:`bool`: Returns ``True`` if the attachment was marked as a spoiler. + + .. versionadded:: 2.5 + """ return 1 << 3 @flag_value def contains_explicit_media(self): - """:class:`bool`: Returns ``True`` if the attachment was flagged as sensitive content.""" + """:class:`bool`: Returns ``True`` if the attachment was flagged as sensitive content. + + .. versionadded:: 2.5 + """ return 1 << 4 @flag_value def animated(self): - """:class:`bool`: Returns ``True`` if the attachment is an animated image.""" + """:class:`bool`: Returns ``True`` if the attachment is an animated image. + + .. versionadded:: 2.5 + """ return 1 << 5