|
|
|
@ -44,13 +44,14 @@ from typing import ( |
|
|
|
Type, |
|
|
|
overload, |
|
|
|
) |
|
|
|
from collections.abc import Iterable |
|
|
|
|
|
|
|
from . import utils |
|
|
|
from .asset import Asset |
|
|
|
from .reaction import Reaction |
|
|
|
from .emoji import Emoji |
|
|
|
from .partial_emoji import PartialEmoji |
|
|
|
from .enums import InteractionType, MessageReferenceType, MessageType, ChannelType, try_enum |
|
|
|
from .enums import InteractionType, MessageReferenceType, MessageType, ChannelType, BaseTheme, try_enum |
|
|
|
from .errors import HTTPException |
|
|
|
from .components import _component_factory |
|
|
|
from .embeds import Embed |
|
|
|
@ -65,6 +66,7 @@ from .sticker import StickerItem, GuildSticker |
|
|
|
from .threads import Thread |
|
|
|
from .channel import PartialMessageable |
|
|
|
from .poll import Poll |
|
|
|
from .colour import Colour |
|
|
|
|
|
|
|
if TYPE_CHECKING: |
|
|
|
from typing_extensions import Self |
|
|
|
@ -81,6 +83,7 @@ if TYPE_CHECKING: |
|
|
|
CallMessage as CallMessagePayload, |
|
|
|
PurchaseNotificationResponse as PurchaseNotificationResponsePayload, |
|
|
|
GuildProductPurchase as GuildProductPurchasePayload, |
|
|
|
SharedClientTheme as SharedClientThemePayload, |
|
|
|
) |
|
|
|
|
|
|
|
from .types.interactions import MessageInteraction as MessageInteractionPayload |
|
|
|
@ -120,6 +123,7 @@ __all__ = ( |
|
|
|
'CallMessage', |
|
|
|
'GuildProductPurchase', |
|
|
|
'PurchaseNotification', |
|
|
|
'SharedClientTheme', |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
@ -950,6 +954,138 @@ class MessageInteractionMetadata(Hashable): |
|
|
|
return self.user.id == self._integration_owners.get(1) |
|
|
|
|
|
|
|
|
|
|
|
class SharedClientTheme: |
|
|
|
"""Represents a shared client theme from a :class:`~discord.Message`. |
|
|
|
|
|
|
|
This can be constructed by users to create a new shared client theme for sending and |
|
|
|
is received using :attr:`Message.shared_client_theme` when a message contains a shared client theme. |
|
|
|
|
|
|
|
.. versionadded:: 2.8 |
|
|
|
|
|
|
|
Parameters |
|
|
|
----------- |
|
|
|
colours: Iterable[Union[:class:`Colour`, :class:`int`]] |
|
|
|
An iterable of the theme's colours. Must be between 1 and 5 colours. |
|
|
|
colors: Iterable[Union[:class:`Colour`, :class:`int`]] |
|
|
|
An alias for ``colours``. |
|
|
|
gradient_angle: :class:`int` |
|
|
|
The direction of the theme's gradient in degrees. Must be between 0 and 360. |
|
|
|
This is only applicable if there are at least 2 colours. |
|
|
|
intensity: :class:`int` |
|
|
|
The intensity of the theme's colors. Must be between 0 and 100. |
|
|
|
theme: :class:`BaseTheme` |
|
|
|
The base theme to use for this client theme. Defaults to :attr:`BaseTheme.dark`. |
|
|
|
|
|
|
|
Raises |
|
|
|
------- |
|
|
|
ValueError |
|
|
|
- If the number of colours is not between 1 and 5. |
|
|
|
- If ``gradient_angle`` is set but there are less than 2 colours. |
|
|
|
- If ``gradient_angle`` is not between 0 and 360. |
|
|
|
- If ``intensity`` is not between 0 and 100. |
|
|
|
- If ``theme`` is not an instance of :class:`BaseTheme`. |
|
|
|
""" |
|
|
|
|
|
|
|
def __init__( |
|
|
|
self, |
|
|
|
*, |
|
|
|
colours: Iterable[Union[Colour, int]] = MISSING, |
|
|
|
colors: Iterable[Union[Colour, int]] = MISSING, |
|
|
|
gradient_angle: int = 0, |
|
|
|
intensity: int = 0, |
|
|
|
theme: BaseTheme = BaseTheme.dark, |
|
|
|
) -> None: |
|
|
|
colours = colours if colours is not MISSING else colors |
|
|
|
self._colours = [colour if isinstance(colour, Colour) else Colour(colour) for colour in colours] |
|
|
|
self.gradient_angle = gradient_angle |
|
|
|
self.intensity = intensity |
|
|
|
self.theme = theme |
|
|
|
|
|
|
|
@classmethod |
|
|
|
def from_dict(cls, data: SharedClientThemePayload) -> Self: |
|
|
|
"""Creates a :class:`SharedClientTheme` from a dictionary. |
|
|
|
|
|
|
|
Possible keys can be found in the |
|
|
|
:ddocs:`api docs <resources/message#shared-client-theme-object>`. |
|
|
|
""" |
|
|
|
return cls( |
|
|
|
colours=[Colour(int(colour, 16)) for colour in data.get('colors', [])], |
|
|
|
gradient_angle=data.get('gradient_angle', 0), |
|
|
|
intensity=data.get('base_mix', 0), |
|
|
|
theme=try_enum(BaseTheme, data.get('base_theme', 'dark')), |
|
|
|
) |
|
|
|
|
|
|
|
def to_dict(self) -> SharedClientThemePayload: |
|
|
|
return { |
|
|
|
'colors': [str(colour).lstrip('#') for colour in self._colours], |
|
|
|
'gradient_angle': self.gradient_angle, |
|
|
|
'base_mix': self.intensity, |
|
|
|
'base_theme': self.theme.value, |
|
|
|
} |
|
|
|
|
|
|
|
def __repr__(self) -> str: |
|
|
|
return f'<SharedClientTheme colours={self.colours!r} gradient_angle={self.gradient_angle} intensity={self.intensity} theme={self.theme!r}>' |
|
|
|
|
|
|
|
@property |
|
|
|
def colours(self) -> List[Colour]: |
|
|
|
"""List[:class:`Colour`]: A list of the theme's colours.""" |
|
|
|
return self._colours |
|
|
|
|
|
|
|
colors = colours |
|
|
|
|
|
|
|
@colours.setter |
|
|
|
def colours(self, value: List[Colour]) -> None: |
|
|
|
if not value: |
|
|
|
raise ValueError('colours cannot be empty') |
|
|
|
|
|
|
|
if len(value) > 5: |
|
|
|
raise ValueError('cannot have more than 5 colours') |
|
|
|
|
|
|
|
if len(value) < 2: |
|
|
|
self.intensity = 0 |
|
|
|
|
|
|
|
self._colours = value |
|
|
|
|
|
|
|
@property |
|
|
|
def gradient_angle(self) -> int: |
|
|
|
""":class:`int`: The direction of the theme's gradient in degrees. |
|
|
|
|
|
|
|
This is only applicable if there are at least 2 colours. |
|
|
|
""" |
|
|
|
return self._gradient_angle |
|
|
|
|
|
|
|
@gradient_angle.setter |
|
|
|
def gradient_angle(self, value: int) -> None: |
|
|
|
if len(self.colours) < 2 and value != 0: |
|
|
|
raise ValueError('gradient_angle may only be set if there are at least 2 colours') |
|
|
|
|
|
|
|
if not 0 <= value <= 360: |
|
|
|
raise ValueError('gradient_angle must be between 0 and 360') |
|
|
|
self._gradient_angle = value |
|
|
|
|
|
|
|
@property |
|
|
|
def intensity(self) -> int: |
|
|
|
""":class:`int`: The intensity of the theme's colors.""" |
|
|
|
return self._intensity |
|
|
|
|
|
|
|
@intensity.setter |
|
|
|
def intensity(self, value: int) -> None: |
|
|
|
if not 0 <= value <= 100: |
|
|
|
raise ValueError('intensity must be between 0 and 100') |
|
|
|
self._intensity = value |
|
|
|
|
|
|
|
@property |
|
|
|
def theme(self) -> BaseTheme: |
|
|
|
""":class:`BaseTheme`: The base theme to use for this client theme.""" |
|
|
|
return self._theme |
|
|
|
|
|
|
|
@theme.setter |
|
|
|
def theme(self, value: BaseTheme) -> None: |
|
|
|
if not isinstance(value, BaseTheme): |
|
|
|
raise ValueError('theme must be an instance of BaseTheme') |
|
|
|
self._theme = value |
|
|
|
|
|
|
|
|
|
|
|
def flatten_handlers(cls: Type[Message]) -> Type[Message]: |
|
|
|
prefix = len('_handle_') |
|
|
|
handlers = [ |
|
|
|
@ -1781,6 +1917,7 @@ class PartialMessage(Hashable): |
|
|
|
suppress_embeds: bool = ..., |
|
|
|
silent: bool = ..., |
|
|
|
poll: Poll = ..., |
|
|
|
shared_client_theme: SharedClientTheme = ..., |
|
|
|
) -> Message: ... |
|
|
|
|
|
|
|
@overload |
|
|
|
@ -1801,6 +1938,7 @@ class PartialMessage(Hashable): |
|
|
|
suppress_embeds: bool = ..., |
|
|
|
silent: bool = ..., |
|
|
|
poll: Poll = ..., |
|
|
|
shared_client_theme: SharedClientTheme = ..., |
|
|
|
) -> Message: ... |
|
|
|
|
|
|
|
@overload |
|
|
|
@ -1821,6 +1959,7 @@ class PartialMessage(Hashable): |
|
|
|
suppress_embeds: bool = ..., |
|
|
|
silent: bool = ..., |
|
|
|
poll: Poll = ..., |
|
|
|
shared_client_theme: SharedClientTheme = ..., |
|
|
|
) -> Message: ... |
|
|
|
|
|
|
|
@overload |
|
|
|
@ -1841,6 +1980,7 @@ class PartialMessage(Hashable): |
|
|
|
suppress_embeds: bool = ..., |
|
|
|
silent: bool = ..., |
|
|
|
poll: Poll = ..., |
|
|
|
shared_client_theme: SharedClientTheme = ..., |
|
|
|
) -> Message: ... |
|
|
|
|
|
|
|
async def reply(self, content: Optional[str] = None, **kwargs: Any) -> Message: |
|
|
|
@ -2138,6 +2278,10 @@ class Message(PartialMessage, Hashable): |
|
|
|
The message snapshots attached to this message. |
|
|
|
|
|
|
|
.. versionadded:: 2.5 |
|
|
|
shared_client_theme: Optional[:class:`SharedClientTheme`] |
|
|
|
The client theme shared in this message. |
|
|
|
|
|
|
|
.. versionadded:: 2.8 |
|
|
|
""" |
|
|
|
|
|
|
|
__slots__ = ( |
|
|
|
@ -2178,6 +2322,7 @@ class Message(PartialMessage, Hashable): |
|
|
|
'purchase_notification', |
|
|
|
'message_snapshots', |
|
|
|
'_pinned_at', |
|
|
|
'shared_client_theme', |
|
|
|
) |
|
|
|
|
|
|
|
if TYPE_CHECKING: |
|
|
|
@ -2323,6 +2468,14 @@ class Message(PartialMessage, Hashable): |
|
|
|
else: |
|
|
|
self.purchase_notification = PurchaseNotification(purchase_notification) |
|
|
|
|
|
|
|
self.shared_client_theme: Optional[SharedClientTheme] = None |
|
|
|
try: |
|
|
|
shared_client_theme = data['shared_client_theme'] # pyright: ignore[reportTypedDictNotRequiredAccess] |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
else: |
|
|
|
self.shared_client_theme = SharedClientTheme.from_dict(shared_client_theme) |
|
|
|
|
|
|
|
for handler in ('author', 'member', 'mentions', 'mention_roles', 'components', 'call'): |
|
|
|
try: |
|
|
|
getattr(self, f'_handle_{handler}')(data[handler]) # type: ignore |
|
|
|
@ -2509,6 +2662,9 @@ class Message(PartialMessage, Hashable): |
|
|
|
def _handle_interaction_metadata(self, data: MessageInteractionMetadataPayload): |
|
|
|
self.interaction_metadata = MessageInteractionMetadata(state=self._state, guild=self.guild, data=data) |
|
|
|
|
|
|
|
def _handle_shared_client_theme(self, data: SharedClientThemePayload): |
|
|
|
self.shared_client_theme = SharedClientTheme.from_dict(data) |
|
|
|
|
|
|
|
def _handle_call(self, data: CallMessagePayload): |
|
|
|
if data is not None: |
|
|
|
self.call = CallMessage(state=self._state, message=self, data=data) |
|
|
|
|