Skip to content
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

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 +275 to +285
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

Copy link
Member

Choose a reason for hiding this comment

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

Rather than save it here, we could make the flags when we access the image or thumbnail, no?

Copy link
Member

Choose a reason for hiding this comment

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

This also no longer seems to be respected in serialisation.

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

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

Copy link
Member

Choose a reason for hiding this comment

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

I think following the API naming is okay

""":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