Skip to content

feat(flags): add new flags to Embed and update Attachment flags #1283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1282.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`Embed.flags`, ``Embed.image.flags``, ``Embed.thumbnail.flags``, :class:`EmbedFlags`, :class:`EmbedMediaFlags` and update the :class:`AttachmentFlags`.
32 changes: 32 additions & 0 deletions disnake/embeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from . import utils
from .colour import Colour
from .file import File
from .flags import EmbedFlags, EmbedMediaFlags
from .utils import MISSING, classproperty, warn_deprecated

__all__ = ("Embed",)
Expand Down Expand Up @@ -89,6 +90,7 @@ class _EmbedMediaProxy(Sized, Protocol):
proxy_url: Optional[str]
height: Optional[int]
width: Optional[int]
flags: Optional[EmbedMediaFlags]

class _EmbedVideoProxy(Sized, Protocol):
url: Optional[str]
Expand Down Expand Up @@ -182,6 +184,7 @@ class Embed:
"_fields",
"description",
"_files",
"_flags",
)

_default_colour: ClassVar[Optional[Colour]] = None
Expand Down Expand Up @@ -220,6 +223,7 @@ def __init__(
self._image: Optional[EmbedImagePayload] = None
self._footer: Optional[EmbedFooterPayload] = None
self._fields: Optional[List[EmbedFieldPayload]] = None
self._flags: Optional[int] = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not default to 0?


self._files: Dict[_FileKey, File] = {}

Expand Down Expand Up @@ -267,12 +271,20 @@ def from_dict(cls, data: EmbedData) -> Self:
self.timestamp = utils.parse_time(data.get("timestamp"))

self._thumbnail = data.get("thumbnail")
if self._thumbnail and (thumbnail_flags := self._thumbnail.get("flags")):
self._thumbnail["flags"] = EmbedMediaFlags._from_value(thumbnail_flags) # type: ignore

self._video = data.get("video")
self._provider = data.get("provider")
self._author = data.get("author")

self._image = data.get("image")
if self._image and (image_flags := self._image.get("flags")):
self._image["flags"] = EmbedMediaFlags._from_value(image_flags) # type: ignore

Comment on lines +274 to +284
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like how this is done but I don't see much alternatives

self._footer = data.get("footer")
self._fields = data.get("fields")
self._flags = data.get("flags")

return self

Expand Down Expand Up @@ -371,6 +383,16 @@ def timestamp(self, value: Optional[datetime.datetime]) -> None:
f"Expected datetime.datetime or None received {type(value).__name__} instead"
)

@property
def flags(self) -> Optional[EmbedFlags]:
"""Optional[:class:`EmbedFlags`]: Returns the embed's flags.

.. versionadded:: 2.11
"""
if self._flags is None:
return
return EmbedFlags._from_value(self._flags)

@property
def footer(self) -> _EmbedFooterProxy:
"""Returns an ``EmbedProxy`` denoting the footer contents.
Expand Down Expand Up @@ -455,6 +477,11 @@ def image(self) -> _EmbedMediaProxy:
- ``proxy_url``
- ``width``
- ``height``
- ``flags``

.. versionchanged:: 2.11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be versionadded?


Added the ``flags`` attribute.

If an attribute is not set, it will be ``None``.
"""
Expand Down Expand Up @@ -508,6 +535,11 @@ def thumbnail(self) -> _EmbedMediaProxy:
- ``proxy_url``
- ``width``
- ``height``
- ``flags``

.. versionchanged:: 2.11

Added the ``flags`` attribute.

If an attribute is not set, it will be ``None``.
"""
Expand Down
191 changes: 191 additions & 0 deletions disnake/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
"SKUFlags",
"ApplicationInstallTypes",
"InteractionContextTypes",
"EmbedFlags",
"EmbedMediaFlags",
)

BF = TypeVar("BF", bound="BaseFlags")
Expand Down Expand Up @@ -2600,11 +2602,51 @@ class AttachmentFlags(BaseFlags):
@_generated
def __init__(self, *, is_remix: bool = ...) -> None: ...

@flag_value
def is_clip(self):
""":class:`bool`: Returns ``True`` if the attachment is a clip.

.. versionadded:: 2.11
"""
return 1 << 0

@flag_value
def is_thumbnail(self):
""":class:`bool`: Returns ``True`` if the attachment is the thumbnail of a thread in a media channel.

.. versionadded:: 2.11
"""
return 1 << 1

@flag_value
def is_remix(self):
""":class:`bool`: Returns ``True`` if the attachment has been edited using the Remix feature."""
return 1 << 2

@flag_value
def is_spoiler(self):
""":class:`bool`: Returns ``True`` if the attachment was marked as a spoiler.

.. versionadded:: 2.11
"""
return 1 << 3

@flag_value
def contains_explicit_media(self):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on calling this something different? Like is_nsfw or is_explicit

""":class:`bool`: Returns ``True`` if the attachment was flagged as sensitive content.

.. versionadded:: 2.11
"""
return 1 << 4

@flag_value
def is_animated(self):
""":class:`bool`: Returns ``True`` if the attachment is an animated image.

.. versionadded:: 2.11
"""
return 1 << 5


class SKUFlags(BaseFlags):
"""Wraps up Discord SKU flags.
Expand Down Expand Up @@ -2892,3 +2934,152 @@ def bot_dm(self):
def private_channel(self):
""":class:`bool`: Returns ``True`` if the command is usable in DMs and group DMs with other users."""
return 1 << 2


class EmbedFlags(BaseFlags):
"""Wraps up Discord Embed flags.

.. collapse:: operations

.. describe:: x == y

Checks if two EmbedFlags instances are equal.
.. describe:: x != y

Checks if two EmbedFlags instances are not equal.
.. describe:: x <= y

Checks if an EmbedFlags instance is a subset of another EmbedFlags instance.
.. describe:: x >= y

Checks if an EmbedFlags instance is a superset of another EmbedFlags instance.
.. describe:: x < y

Checks if an EmbedFlags instance is a strict subset of another EmbedFlags instance.
.. describe:: x > y

Checks if an EmbedFlags instance is a strict superset of another EmbedFlags instance.
.. describe:: x | y, x |= y

Returns a new EmbedFlags instance with all enabled flags from both x and y.
(Using ``|=`` will update in place).
.. describe:: x & y, x &= y

Returns a new EmbedFlags instance with only flags enabled on both x and y.
(Using ``&=`` will update in place).
.. describe:: x ^ y, x ^= y

Returns a new EmbedFlags instance with only flags enabled on one of x or y, but not both.
(Using ``^=`` will update in place).
.. describe:: ~x

Returns a new EmbedFlags instance with all flags from x inverted.
.. 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.

Additionally supported are a few operations on class attributes.

.. describe:: EmbedFlags.y | EmbedFlags.z, EmbedFlags(y=True) | EmbedFlags.z

Returns an EmbedFlags instance with all provided flags enabled.

.. describe:: ~EmbedFlags.y

Returns an EmbedFlags instance with all flags except ``y`` inverted from their default value.

.. versionadded:: 2.11

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):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on calling this something different? Like is_nsfw or is_explicit

""":class:`bool`: Returns ``True`` if the embed was flagged as sensitive content."""
return 1 << 4

@flag_value
def is_content_inventory_entry(self):
""":class:`bool`: Returns ``True`` if the embed is a reply to an activity card."""
return 1 << 5


class EmbedMediaFlags(BaseFlags):
"""Wraps up Discord Embed media flags.

.. collapse:: operations

.. describe:: x == y

Checks if two EmbedMediaFlags instances are equal.
.. describe:: x != y

Checks if two EmbedMediaFlags instances are not equal.
.. describe:: x <= y

Checks if an EmbedMediaFlags instance is a subset of another EmbedMediaFlags instance.
.. describe:: x >= y

Checks if an EmbedMediaFlags instance is a superset of another EmbedMediaFlags instance.
.. describe:: x < y

Checks if an EmbedMediaFlags instance is a strict subset of another EmbedMediaFlags instance.
.. describe:: x > y

Checks if an EmbedMediaFlags instance is a strict superset of another EmbedMediaFlags instance.
.. describe:: x | y, x |= y

Returns a new EmbedMediaFlags instance with all enabled flags from both x and y.
(Using ``|=`` will update in place).
.. describe:: x & y, x &= y

Returns a new EmbedMediaFlags instance with only flags enabled on both x and y.
(Using ``&=`` will update in place).
.. describe:: x ^ y, x ^= y

Returns a new EmbedMediaFlags instance with only flags enabled on one of x or y, but not both.
(Using ``^=`` will update in place).
.. describe:: ~x

Returns a new EmbedMediaFlags instance with all flags from x inverted.
.. 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.

Additionally supported are a few operations on class attributes.

.. describe:: EmbedMediaFlags.y | EmbedMediaFlags.z, EmbedMediaFlags(y=True) | EmbedMediaFlags.z

Returns an EmbedMediaFlags instance with all provided flags enabled.

.. describe:: ~EmbedMediaFlags.y

Returns an EmbedMediaFlags instance with all flags except ``y`` inverted from their default value.

.. versionadded:: 2.11

Attributes
----------
value: :class:`int`
The raw value. You should query flags via the properties
rather than using this raw value.
"""

@flag_value
def is_animated(self):
""":class:`bool`: Returns ``True`` if the embed media is animated."""
return 1 << 5
28 changes: 28 additions & 0 deletions disnake/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)

from . import utils
from .appinfo import AppInfo
from .channel import PartialMessageable
from .components import ActionRow, MessageComponent, _component_factory
from .embeds import Embed
Expand Down Expand Up @@ -307,6 +308,21 @@ class Attachment(Hashable):
(see :attr:`MessageFlags.is_voice_message`).

.. versionadded:: 2.9

clip_participants: List[:class:`User`]
If this attachment is a clip returns a list of users who were in the stream.

.. versionadded:: 2.11

clip_created_at: Optional[:class:`datetime.datetime`]
If this attachment is a clip returns the creation timestamp.

.. versionadded:: 2.11

application: Optional[:class:`AppInfo`]
If this attachment is a clip returns the application in the stream, if recognized.

.. versionadded:: 2.11
"""

__slots__ = (
Expand All @@ -325,6 +341,9 @@ class Attachment(Hashable):
"duration",
"waveform",
"_flags",
"clip_participants",
"clip_created_at",
"application",
)

def __init__(self, *, data: AttachmentPayload, state: ConnectionState) -> None:
Expand All @@ -345,6 +364,15 @@ def __init__(self, *, data: AttachmentPayload, state: ConnectionState) -> None:
b64decode(waveform_data) if (waveform_data := data.get("waveform")) else None
)
self._flags: int = data.get("flags", 0)
self.clip_participants: List[User] = [
User(state=state, data=d) for d in data.get("clip_participants", [])
]
self.clip_created_at: Optional[datetime.datetime] = utils.parse_time(
data.get("clip_created_at")
)
self.application: Optional[AppInfo] = (
AppInfo(state=state, data=d) if (d := data.get("application")) else None
)

def is_spoiler(self) -> bool:
"""Whether this attachment contains a spoiler.
Expand Down
3 changes: 3 additions & 0 deletions disnake/types/embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class EmbedThumbnail(TypedDict):
proxy_url: NotRequired[str]
height: NotRequired[int]
width: NotRequired[int]
flags: NotRequired[int]


class EmbedVideo(TypedDict, total=False):
Expand All @@ -36,6 +37,7 @@ class EmbedImage(TypedDict):
proxy_url: NotRequired[str]
height: NotRequired[int]
width: NotRequired[int]
flags: NotRequired[int]


class EmbedProvider(TypedDict, total=False):
Expand Down Expand Up @@ -67,3 +69,4 @@ class Embed(TypedDict, total=False):
provider: EmbedProvider
author: EmbedAuthor
fields: List[EmbedField]
flags: int
Loading
Loading