Browse Source

feat: First components v2 commit

pull/10166/head
DA-344 3 months ago
parent
commit
75134562fd
  1. 178
      discord/attachment.py
  2. 6
      discord/components.py
  3. 4
      discord/types/attachment.py
  4. 4
      discord/types/components.py
  5. 7
      discord/ui/section.py

178
discord/attachment.py

@ -29,12 +29,19 @@ from typing import TYPE_CHECKING, Any, Optional, Union
from .mixins import Hashable from .mixins import Hashable
from .file import File from .file import File
from .state import ConnectionState
from .flags import AttachmentFlags from .flags import AttachmentFlags
from .enums import MediaLoadingState, try_enum
from . import utils from . import utils
if TYPE_CHECKING: if TYPE_CHECKING:
from .types.attachment import Attachment as AttachmentPayload from .types.attachment import (
AttachmentBase as AttachmentBasePayload,
Attachment as AttachmentPayload,
UnfurledAttachment as UnfurledAttachmentPayload,
)
from .http import HTTPClient
from .state import ConnectionState
MISSING = utils.MISSING MISSING = utils.MISSING
@ -45,7 +52,40 @@ __all__ = (
class AttachmentBase: class AttachmentBase:
url: str
__slots__ = (
'url',
'proxy_url',
'description',
'filename',
'spoiler',
'height',
'width',
'content_type',
'_flags',
'_http',
'_state',
)
def __init__(self, data: AttachmentBasePayload, state: ConnectionState) -> None:
self._state: ConnectionState = state
self._http: HTTPClient = state.http
self.url: str = data['url']
self.proxy_url: str = data['proxy_url']
self.description: Optional[str] = data.get('description')
self.spoiler: bool = data.get('spoiler', False)
self.height: Optional[int] = data.get('height')
self.width: Optional[int] = data.get('width')
self.content_type: Optional[str] = data.get('content_type')
self._flags: int = data.get('flags', 0)
@property
def flags(self) -> AttachmentFlags:
""":class:`AttachmentFlags`: The attachment's flag value."""
return AttachmentFlags._from_value(self._flags)
def __str__(self) -> str:
return self.url or ''
async def save( async def save(
self, self,
@ -200,6 +240,22 @@ class AttachmentBase:
spoiler=spoiler, spoiler=spoiler,
) )
def to_dict(self):
base = {
'url': self.url,
'proxy_url': self.proxy_url,
'spoiler': self.spoiler,
}
if self.width:
base['width'] = self.width
if self.height:
base['height'] = self.height
if self.description:
base['description'] = self.description
return base
class Attachment(Hashable, AttachmentBase): class Attachment(Hashable, AttachmentBase):
"""Represents an attachment from Discord. """Represents an attachment from Discord.
@ -268,56 +324,34 @@ class Attachment(Hashable, AttachmentBase):
The normalised version of the attachment's filename. The normalised version of the attachment's filename.
.. versionadded:: 2.5 .. versionadded:: 2.5
spoiler: :class:`bool`
Whether the attachment is a spoiler or not. Unlike :meth:`.is_spoiler`, this uses the API returned
data.
.. versionadded:: 2.6
""" """
__slots__ = ( __slots__ = (
'id', 'id',
'size', 'size',
'height',
'width',
'filename',
'url',
'proxy_url',
'_http',
'content_type',
'description',
'ephemeral', 'ephemeral',
'duration', 'duration',
'waveform', 'waveform',
'_flags',
'title', 'title',
) )
def __init__(self, *, data: AttachmentPayload, state: ConnectionState): def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
self.id: int = int(data['id']) self.id: int = int(data['id'])
self.size: int = data['size']
self.height: Optional[int] = data.get('height')
self.width: Optional[int] = data.get('width')
self.filename: str = data['filename'] self.filename: str = data['filename']
self.url: str = data['url'] self.size: int = data['size']
self.proxy_url: str = data['proxy_url']
self._http = state.http
self.content_type: Optional[str] = data.get('content_type')
self.description: Optional[str] = data.get('description')
self.ephemeral: bool = data.get('ephemeral', False) self.ephemeral: bool = data.get('ephemeral', False)
self.duration: Optional[float] = data.get('duration_secs') self.duration: Optional[float] = data.get('duration_secs')
self.title: Optional[str] = data.get('title') self.title: Optional[str] = data.get('title')
super().__init__(data, state)
waveform = data.get('waveform')
self.waveform: Optional[bytes] = (
utils._base64_to_bytes(waveform) if waveform is not None else None
)
self._flags: int = data.get('flags', 0)
@property
def flags(self) -> AttachmentFlags:
""":class:`AttachmentFlags`: The attachment's flags."""
return AttachmentFlags._from_value(self._flags)
def is_spoiler(self) -> bool: def is_spoiler(self) -> bool:
""":class:`bool`: Whether this attachment contains a spoiler.""" """:class:`bool`: Whether this attachment contains a spoiler."""
return self.filename.startswith('SPOILER_') return self.spoiler or self.filename.startswith('SPOILER_')
def is_voice_message(self) -> bool: def is_voice_message(self) -> bool:
""":class:`bool`: Whether this attachment is a voice message.""" """:class:`bool`: Whether this attachment is a voice message."""
@ -326,33 +360,18 @@ class Attachment(Hashable, AttachmentBase):
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<Attachment id={self.id} filename={self.filename!r} url={self.url!r}>' return f'<Attachment id={self.id} filename={self.filename!r} url={self.url!r}>'
def __str__(self) -> str:
return self.url or ''
def to_dict(self) -> AttachmentPayload: def to_dict(self) -> AttachmentPayload:
result: AttachmentPayload = { result: AttachmentPayload = super().to_dict() # pyright: ignore[reportAssignmentType]
'filename': self.filename, result['id'] = self.id
'id': self.id, result['filename'] = self.filename
'proxy_url': self.proxy_url, result['size'] = self.size
'size': self.size,
'url': self.url,
'spoiler': self.is_spoiler(),
}
if self.height:
result['height'] = self.height
if self.width:
result['width'] = self.width
if self.content_type:
result['content_type'] = self.content_type
if self.description is not None:
result['description'] = self.description
return result return result
class UnfurledAttachment(AttachmentBase): class UnfurledAttachment(AttachmentBase):
"""Represents an unfurled attachment item from a :class:`Component`. """Represents an unfurled attachment item from a :class:`Component`.
.. versionadded:: tbd .. versionadded:: 2.6
.. container:: operations .. container:: operations
@ -370,48 +389,35 @@ class UnfurledAttachment(AttachmentBase):
Attributes Attributes
---------- ----------
height: Optional[:class:`int`]
The attachment's height, in pixels. Only applicable to images and videos.
width: Optional[:class:`int`]
The attachment's width, in pixels. Only applicable to images and videos.
url: :class:`str` url: :class:`str`
The unfurled attachment URL. The attachment URL. If the message this attachment was attached
proxy_url: Optional[:class:`str`] to is deleted, then this will 404.
The proxy URL. This is cached version of the :attr:`~UnfurledAttachment.url` in the proxy_url: :class:`str`
The proxy URL. This is a cached version of the :attr:`~Attachment.url` in the
case of images. When the message is deleted, this URL might be valid for a few case of images. When the message is deleted, this URL might be valid for a few
minutes or not valid at all. minutes or not valid at all.
.. note::
This will be ``None`` if :meth:`.is_resolved` is ``False``.
height: Optional[:class:`int`]
The unfurled attachment's height, in pixels.
.. note::
This will be ``None`` if :meth:`.is_resolved` is ``False``.
width: Optional[:class:`int`]
The unfurled attachment's width, in pixels.
.. note::
This will be ``None`` if :meth:`.is_resolved` is ``False``.
content_type: Optional[:class:`str`] content_type: Optional[:class:`str`]
The attachment's `media type <https://en.wikipedia.org/wiki/Media_type>`_ The attachment's `media type <https://en.wikipedia.org/wiki/Media_type>`_
description: Optional[:class:`str`]
.. note:: The attachment's description. Only applicable to images.
spoiler: :class:`bool`
This will be ``None`` if :meth:`.is_resolved` is ``False``. Whether the attachment is a spoiler or not. Unlike :meth:`.is_spoiler`, this uses the API returned
data.
loading_state: :class:`MediaLoadingState` loading_state: :class:`MediaLoadingState`
The load state of this attachment on Discord side. The cache state of this unfurled attachment.
description
""" """
__slots__ = ( __slots__ = (
'url',
'proxy_url',
'height',
'width',
'content_type',
'loading_state', 'loading_state',
'_resolved',
'_state',
) )
def __init__(self, ) def __init__(self, data: UnfurledAttachmentPayload, state: ConnectionState) -> None:
self.loading_state: MediaLoadingState = try_enum(MediaLoadingState, data['loading_state'])
super().__init__(data, state)
def __repr__(self) -> str:
return f'<UnfurledAttachment url={self.url!r}>'

6
discord/components.py

@ -690,7 +690,7 @@ class SectionComponent(Component):
The user constructible and usable type to create a section is :class:`discord.ui.Section` The user constructible and usable type to create a section is :class:`discord.ui.Section`
not this one. not this one.
.. versionadded:: tbd .. versionadded:: 2.6
Attributes Attributes
---------- ----------
@ -732,7 +732,7 @@ class TextDisplay(Component):
This inherits from :class:`Component`. This inherits from :class:`Component`.
.. versionadded:: tbd .. versionadded:: 2.6
Parameters Parameters
---------- ----------
@ -770,7 +770,7 @@ class ThumbnailComponent(Component):
The user constructuble and usable type to create a thumbnail The user constructuble and usable type to create a thumbnail
component is :class:`discord.ui.Thumbnail` not this one. component is :class:`discord.ui.Thumbnail` not this one.
.. versionadded:: tbd .. versionadded:: 2.6
Attributes Attributes
---------- ----------

4
discord/types/attachment.py

@ -49,10 +49,8 @@ class Attachment(AttachmentBase):
ephemeral: NotRequired[bool] ephemeral: NotRequired[bool]
duration_secs: NotRequired[float] duration_secs: NotRequired[float]
waveform: NotRequired[str] waveform: NotRequired[str]
title: NotRequired[str]
class UnfurledAttachment(AttachmentBase): class UnfurledAttachment(AttachmentBase):
loading_state: LoadingState loading_state: LoadingState
src_is_animated: NotRequired[bool]
placeholder: str
placeholder_version: int

4
discord/types/components.py

@ -143,12 +143,12 @@ class ThumbnailComponent(ComponentBase, UnfurledAttachment):
class MediaGalleryComponent(ComponentBase): class MediaGalleryComponent(ComponentBase):
type: Literal[12] type: Literal[12]
items: List[MediaItem] items: List[UnfurledAttachment]
class FileComponent(ComponentBase): class FileComponent(ComponentBase):
type: Literal[13] type: Literal[13]
file: MediaItem file: UnfurledAttachment
spoiler: NotRequired[bool] spoiler: NotRequired[bool]

7
discord/ui/section.py

@ -26,6 +26,9 @@ from __future__ import annotations
from typing import List, Optional from typing import List, Optional
from .item import Item from .item import Item
from ..components import SectionComponent
__all__ = ('Section',)
class Section(Item): class Section(Item):
@ -47,4 +50,6 @@ class Section(Item):
def __init__(self, *, accessory: Optional[Item]) -> None: def __init__(self, *, accessory: Optional[Item]) -> None:
self.accessory: Optional[Item] = accessory self.accessory: Optional[Item] = accessory
self._children: List[Item] = [] self._children: List[Item] = []
self._underlying = SectionComponent self._underlying = SectionComponent._raw_construct(
accessory=accessory,
)

Loading…
Cancel
Save