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 .file import File
from .state import ConnectionState
from .flags import AttachmentFlags
from .enums import MediaLoadingState, try_enum
from . import utils
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
@ -45,7 +52,40 @@ __all__ = (
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(
self,
@ -200,6 +240,22 @@ class AttachmentBase:
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):
"""Represents an attachment from Discord.
@ -268,56 +324,34 @@ class Attachment(Hashable, AttachmentBase):
The normalised version of the attachment's filename.
.. 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__ = (
'id',
'size',
'height',
'width',
'filename',
'url',
'proxy_url',
'_http',
'content_type',
'description',
'ephemeral',
'duration',
'waveform',
'_flags',
'title',
)
def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
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.url: str = data['url']
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.size: int = data['size']
self.ephemeral: bool = data.get('ephemeral', False)
self.duration: Optional[float] = data.get('duration_secs')
self.title: Optional[str] = data.get('title')
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)
super().__init__(data, state)
def is_spoiler(self) -> bool:
""":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:
""":class:`bool`: Whether this attachment is a voice message."""
@ -326,33 +360,18 @@ class Attachment(Hashable, AttachmentBase):
def __repr__(self) -> str:
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:
result: AttachmentPayload = {
'filename': self.filename,
'id': self.id,
'proxy_url': self.proxy_url,
'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
result: AttachmentPayload = super().to_dict() # pyright: ignore[reportAssignmentType]
result['id'] = self.id
result['filename'] = self.filename
result['size'] = self.size
return result
class UnfurledAttachment(AttachmentBase):
"""Represents an unfurled attachment item from a :class:`Component`.
.. versionadded:: tbd
.. versionadded:: 2.6
.. container:: operations
@ -370,48 +389,35 @@ class UnfurledAttachment(AttachmentBase):
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`
The unfurled attachment URL.
proxy_url: Optional[:class:`str`]
The proxy URL. This is cached version of the :attr:`~UnfurledAttachment.url` in the
The attachment URL. If the message this attachment was attached
to is deleted, then this will 404.
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
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`]
The attachment's `media type <https://en.wikipedia.org/wiki/Media_type>`_
.. note::
This will be ``None`` if :meth:`.is_resolved` is ``False``.
description: Optional[:class:`str`]
The attachment's description. Only applicable to images.
spoiler: :class:`bool`
Whether the attachment is a spoiler or not. Unlike :meth:`.is_spoiler`, this uses the API returned
data.
loading_state: :class:`MediaLoadingState`
The load state of this attachment on Discord side.
description
The cache state of this unfurled attachment.
"""
__slots__ = (
'url',
'proxy_url',
'height',
'width',
'content_type',
'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`
not this one.
.. versionadded:: tbd
.. versionadded:: 2.6
Attributes
----------
@ -732,7 +732,7 @@ class TextDisplay(Component):
This inherits from :class:`Component`.
.. versionadded:: tbd
.. versionadded:: 2.6
Parameters
----------
@ -770,7 +770,7 @@ class ThumbnailComponent(Component):
The user constructuble and usable type to create a thumbnail
component is :class:`discord.ui.Thumbnail` not this one.
.. versionadded:: tbd
.. versionadded:: 2.6
Attributes
----------

4
discord/types/attachment.py

@ -49,10 +49,8 @@ class Attachment(AttachmentBase):
ephemeral: NotRequired[bool]
duration_secs: NotRequired[float]
waveform: NotRequired[str]
title: NotRequired[str]
class UnfurledAttachment(AttachmentBase):
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):
type: Literal[12]
items: List[MediaItem]
items: List[UnfurledAttachment]
class FileComponent(ComponentBase):
type: Literal[13]
file: MediaItem
file: UnfurledAttachment
spoiler: NotRequired[bool]

7
discord/ui/section.py

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

Loading…
Cancel
Save