mirror of
https://github.com/MelisaDev/melisa.git
synced 2024-11-11 19:07:28 +03:00
feat(models): Add embed color model and methods
This commit is contained in:
parent
d998a0408d
commit
643a231e9b
14 changed files with 507 additions and 265 deletions
|
@ -21,8 +21,8 @@ from ..message.message import Message
|
||||||
from ...exceptions import EmbedFieldError
|
from ...exceptions import EmbedFieldError
|
||||||
from ...models.message.embed import Embed
|
from ...models.message.embed import Embed
|
||||||
from ...utils import Snowflake, Timestamp
|
from ...utils import Snowflake, Timestamp
|
||||||
from ...utils import APIModelBase
|
from ...utils.api_model import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .thread import ThreadMember, ThreadMetadata
|
from .thread import ThreadMember, ThreadMetadata
|
||||||
|
@ -162,32 +162,32 @@ class Channel(APIModelBase):
|
||||||
only included when part of the `resolved` data received on a slash command interaction
|
only included when part of the `resolved` data received on a slash command interaction
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = UNDEFINED
|
||||||
type: APINullable[int] = None
|
type: APINullable[int] = UNDEFINED
|
||||||
guild_id: APINullable[Snowflake] = None
|
guild_id: APINullable[Snowflake] = UNDEFINED
|
||||||
position: APINullable[int] = None
|
position: APINullable[int] = UNDEFINED
|
||||||
permission_overwrites: APINullable[List] = None
|
permission_overwrites: APINullable[List] = UNDEFINED
|
||||||
name: APINullable[str] = None
|
name: APINullable[str] = UNDEFINED
|
||||||
topic: APINullable[str] = None
|
topic: APINullable[str] = UNDEFINED
|
||||||
nsfw: APINullable[bool] = None
|
nsfw: APINullable[bool] = UNDEFINED
|
||||||
last_message_id: APINullable[Snowflake] = None
|
last_message_id: APINullable[Snowflake] = UNDEFINED
|
||||||
bitrate: APINullable[int] = None
|
bitrate: APINullable[int] = UNDEFINED
|
||||||
user_limit: APINullable[int] = None
|
user_limit: APINullable[int] = UNDEFINED
|
||||||
rate_limit_per_user: APINullable[int] = None
|
rate_limit_per_user: APINullable[int] = UNDEFINED
|
||||||
recipients: APINullable[List] = None
|
recipients: APINullable[List] = UNDEFINED
|
||||||
icon: APINullable[str] = None
|
icon: APINullable[str] = UNDEFINED
|
||||||
owner_id: APINullable[Snowflake] = None
|
owner_id: APINullable[Snowflake] = UNDEFINED
|
||||||
application_id: APINullable[Snowflake] = None
|
application_id: APINullable[Snowflake] = UNDEFINED
|
||||||
parent_id: APINullable[Snowflake] = None
|
parent_id: APINullable[Snowflake] = UNDEFINED
|
||||||
last_pin_timestamp: APINullable[Timestamp] = None
|
last_pin_timestamp: APINullable[Timestamp] = UNDEFINED
|
||||||
rtc_region: APINullable[str] = None
|
rtc_region: APINullable[str] = UNDEFINED
|
||||||
video_quality_mode: APINullable[int] = None
|
video_quality_mode: APINullable[int] = UNDEFINED
|
||||||
message_count: APINullable[int] = None
|
message_count: APINullable[int] = UNDEFINED
|
||||||
member_count: APINullable[int] = None
|
member_count: APINullable[int] = UNDEFINED
|
||||||
thread_metadata: APINullable[ThreadMetadata] = None
|
thread_metadata: APINullable[ThreadMetadata] = UNDEFINED
|
||||||
member: APINullable[List] = None
|
member: APINullable[List] = UNDEFINED
|
||||||
default_auto_archive_duration: APINullable[int] = None
|
default_auto_archive_duration: APINullable[int] = UNDEFINED
|
||||||
permissions: APINullable[str] = None
|
permissions: APINullable[str] = UNDEFINED
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mention(self):
|
def mention(self):
|
||||||
|
@ -846,7 +846,7 @@ class ThreadsList(APIModelBase):
|
||||||
|
|
||||||
threads: List[Thread]
|
threads: List[Thread]
|
||||||
members: List[ThreadMember]
|
members: List[ThreadMember]
|
||||||
has_more: APINullable[bool] = None
|
has_more: APINullable[bool] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
|
|
|
@ -9,8 +9,8 @@ from typing import List, Any, Optional, overload
|
||||||
|
|
||||||
from .channel import Channel, ChannelType, channel_types_for_converting, ThreadsList
|
from .channel import Channel, ChannelType, channel_types_for_converting, ThreadsList
|
||||||
from ...utils import Snowflake, Timestamp
|
from ...utils import Snowflake, Timestamp
|
||||||
from ...utils import APIModelBase
|
from ...utils.api_model import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
class DefaultMessageNotificationLevel(IntEnum):
|
class DefaultMessageNotificationLevel(IntEnum):
|
||||||
|
@ -292,63 +292,63 @@ class Guild(APIModelBase):
|
||||||
The scheduled events in the guild
|
The scheduled events in the guild
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = UNDEFINED
|
||||||
name: APINullable[str] = None
|
name: APINullable[str] = UNDEFINED
|
||||||
icon: APINullable[str] = None
|
icon: APINullable[str] = UNDEFINED
|
||||||
icon_hash: APINullable[str] = None
|
icon_hash: APINullable[str] = UNDEFINED
|
||||||
splash: APINullable[str] = None
|
splash: APINullable[str] = UNDEFINED
|
||||||
discovery_splash: APINullable[str] = None
|
discovery_splash: APINullable[str] = UNDEFINED
|
||||||
owner: APINullable[bool] = None
|
owner: APINullable[bool] = UNDEFINED
|
||||||
owner_id: APINullable[Snowflake] = None
|
owner_id: APINullable[Snowflake] = UNDEFINED
|
||||||
permissions: APINullable[str] = None
|
permissions: APINullable[str] = UNDEFINED
|
||||||
region: APINullable[str] = None
|
region: APINullable[str] = UNDEFINED
|
||||||
afk_channel_id: APINullable[Snowflake] = None
|
afk_channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
afk_timeout: APINullable[int] = None
|
afk_timeout: APINullable[int] = UNDEFINED
|
||||||
widget_enabled: APINullable[bool] = None
|
widget_enabled: APINullable[bool] = UNDEFINED
|
||||||
widget_channel_id: APINullable[Snowflake] = None
|
widget_channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
verification_level: APINullable[int] = None
|
verification_level: APINullable[int] = UNDEFINED
|
||||||
default_message_notifications: APINullable[int] = None
|
default_message_notifications: APINullable[int] = UNDEFINED
|
||||||
explicit_content_filter: APINullable[int] = None
|
explicit_content_filter: APINullable[int] = UNDEFINED
|
||||||
features: APINullable[List[str]] = None
|
features: APINullable[List[str]] = UNDEFINED
|
||||||
roles: APINullable[List] = None
|
roles: APINullable[List] = UNDEFINED
|
||||||
emojis: APINullable[List] = None
|
emojis: APINullable[List] = UNDEFINED
|
||||||
# TODO: Make a structures of emoji and role
|
# TODO: Make a structures of emoji and role
|
||||||
|
|
||||||
mfa_level: APINullable[int] = None
|
mfa_level: APINullable[int] = UNDEFINED
|
||||||
application_id: APINullable[Snowflake] = None
|
application_id: APINullable[Snowflake] = UNDEFINED
|
||||||
system_channel_id: APINullable[Snowflake] = None
|
system_channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
system_channel_flags: APINullable[int] = None
|
system_channel_flags: APINullable[int] = UNDEFINED
|
||||||
rules_channel_id: APINullable[Snowflake] = None
|
rules_channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
joined_at: APINullable[Timestamp] = None
|
joined_at: APINullable[Timestamp] = UNDEFINED
|
||||||
# TODO: Deal with joined_at
|
# TODO: Deal with joined_at
|
||||||
|
|
||||||
large: APINullable[bool] = None
|
large: APINullable[bool] = UNDEFINED
|
||||||
unavailable: APINullable[bool] = None
|
unavailable: APINullable[bool] = UNDEFINED
|
||||||
member_count: APINullable[int] = None
|
member_count: APINullable[int] = UNDEFINED
|
||||||
voice_states: APINullable[List] = None
|
voice_states: APINullable[List] = UNDEFINED
|
||||||
members: APINullable[List] = None
|
members: APINullable[List] = UNDEFINED
|
||||||
threads: APINullable[List] = None
|
threads: APINullable[List] = UNDEFINED
|
||||||
presences: APINullable[List] = None
|
presences: APINullable[List] = UNDEFINED
|
||||||
# TODO: Make a structure for voice_states, members, channels, threads, presences(?)
|
# TODO: Make a structure for voice_states, members, channels, threads, presences(?)
|
||||||
|
|
||||||
max_presences: APINullable[int] = None
|
max_presences: APINullable[int] = UNDEFINED
|
||||||
max_members: APINullable[int] = None
|
max_members: APINullable[int] = UNDEFINED
|
||||||
vanity_url_code: APINullable[str] = None
|
vanity_url_code: APINullable[str] = UNDEFINED
|
||||||
description: APINullable[str] = None
|
description: APINullable[str] = UNDEFINED
|
||||||
banner: APINullable[str] = None
|
banner: APINullable[str] = UNDEFINED
|
||||||
premium_tier: APINullable[str] = None
|
premium_tier: APINullable[str] = UNDEFINED
|
||||||
premium_subscription_count: APINullable[int] = None
|
premium_subscription_count: APINullable[int] = UNDEFINED
|
||||||
preferred_locale: APINullable[str] = None
|
preferred_locale: APINullable[str] = UNDEFINED
|
||||||
public_updates_channel_id: APINullable[Snowflake] = None
|
public_updates_channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
max_video_channel_users: APINullable[int] = None
|
max_video_channel_users: APINullable[int] = UNDEFINED
|
||||||
approximate_member_count: APINullable[int] = None
|
approximate_member_count: APINullable[int] = UNDEFINED
|
||||||
approximate_presence_count: APINullable[int] = None
|
approximate_presence_count: APINullable[int] = UNDEFINED
|
||||||
nsfw_level: APINullable[int] = None
|
nsfw_level: APINullable[int] = UNDEFINED
|
||||||
premium_progress_bar_enabled: APINullable[bool] = None
|
premium_progress_bar_enabled: APINullable[bool] = UNDEFINED
|
||||||
stage_instances: APINullable[List] = None
|
stage_instances: APINullable[List] = UNDEFINED
|
||||||
stickers: APINullable[List] = None
|
stickers: APINullable[List] = UNDEFINED
|
||||||
welcome_screen: APINullable = None
|
welcome_screen: APINullable = UNDEFINED
|
||||||
guild_scheduled_events: APINullable[List] = None
|
guild_scheduled_events: APINullable[List] = UNDEFINED
|
||||||
|
|
||||||
# TODO: Make a structure for welcome_screen, stage_instances,
|
# TODO: Make a structure for welcome_screen, stage_instances,
|
||||||
# stickers and guild_scheduled_events
|
# stickers and guild_scheduled_events
|
||||||
|
|
|
@ -6,7 +6,7 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from ...utils.api_model import APIModelBase
|
from ...utils.api_model import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
from ...utils.snowflake import Snowflake
|
from ...utils.snowflake import Snowflake
|
||||||
from ...utils.timestamp import Timestamp
|
from ...utils.timestamp import Timestamp
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ class ThreadMetadata(APIModelBase):
|
||||||
auto_archive_duration: int
|
auto_archive_duration: int
|
||||||
archive_timestamp: Timestamp
|
archive_timestamp: Timestamp
|
||||||
locked: bool
|
locked: bool
|
||||||
invitable: APINullable[bool] = None
|
invitable: APINullable[bool] = UNDEFINED
|
||||||
create_timestamp: APINullable[Timestamp] = None
|
create_timestamp: APINullable[Timestamp] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
|
@ -62,5 +62,5 @@ class ThreadMember(APIModelBase):
|
||||||
|
|
||||||
join_timestamp: Timestamp
|
join_timestamp: Timestamp
|
||||||
flags: int
|
flags: int
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = UNDEFINED
|
||||||
user_id: APINullable[Snowflake] = None
|
user_id: APINullable[Snowflake] = UNDEFINED
|
||||||
|
|
|
@ -8,8 +8,8 @@ from enum import IntEnum
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from ...utils import Snowflake
|
from ...utils import Snowflake
|
||||||
from ...utils import APIModelBase
|
from ...utils.api_model import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..user.user import User
|
from ..user.user import User
|
||||||
|
@ -73,18 +73,18 @@ class Webhook(APIModelBase):
|
||||||
The url used for executing the webhook (returned by the webhooks OAuth2 flow)
|
The url used for executing the webhook (returned by the webhooks OAuth2 flow)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = UNDEFINED
|
||||||
type: APINullable[int] = None
|
type: APINullable[int] = UNDEFINED
|
||||||
guild_id: APINullable[Snowflake] = None
|
guild_id: APINullable[Snowflake] = UNDEFINED
|
||||||
channel_id: APINullable[Snowflake] = None
|
channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
user: APINullable[User] = None
|
user: APINullable[User] = UNDEFINED
|
||||||
name: APINullable[str] = None
|
name: APINullable[str] = UNDEFINED
|
||||||
avatar: APINullable[str] = None
|
avatar: APINullable[str] = UNDEFINED
|
||||||
token: APINullable[str] = None
|
token: APINullable[str] = UNDEFINED
|
||||||
application_id: APINullable[Snowflake] = None
|
application_id: APINullable[Snowflake] = UNDEFINED
|
||||||
source_guild: APINullable[Guild] = None
|
source_guild: APINullable[Guild] = UNDEFINED
|
||||||
source_channel: APINullable[Channel] = None
|
source_channel: APINullable[Channel] = UNDEFINED
|
||||||
url: APINullable[str] = None
|
url: APINullable[str] = UNDEFINED
|
||||||
|
|
||||||
async def delete(
|
async def delete(
|
||||||
self, *, webhook_id: Optional[Snowflake] = None, reason: Optional[str] = None
|
self, *, webhook_id: Optional[Snowflake] = None, reason: Optional[str] = None
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
|
|
||||||
from .message import *
|
from .message import *
|
||||||
from .embed import *
|
from .embed import *
|
||||||
|
from .colors import *
|
||||||
|
|
156
melisa/models/message/colors.py
Normal file
156
melisa/models/message/colors.py
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
# Copyright MelisaDev 2022 - Present
|
||||||
|
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
|
CT = typing.TypeVar("CT", bound="Color")
|
||||||
|
|
||||||
|
|
||||||
|
class Color:
|
||||||
|
"""Represents a Discord colour. This class is similar
|
||||||
|
to a (red, green, blue) :class:`tuple`.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
.. describe:: x == y
|
||||||
|
Checks if two colours are equal.
|
||||||
|
.. describe:: x != y
|
||||||
|
Checks if two colours are not equal.
|
||||||
|
.. describe:: hash(x)
|
||||||
|
Return the colour's hash.
|
||||||
|
.. describe:: str(x)
|
||||||
|
Returns the hex format for the colour.
|
||||||
|
.. describe:: int(x)
|
||||||
|
Returns the raw colour value.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
value: :class:`int`
|
||||||
|
The raw integer colour value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ("value",)
|
||||||
|
|
||||||
|
def __init__(self, value: int):
|
||||||
|
if not isinstance(value, int):
|
||||||
|
raise TypeError(
|
||||||
|
f"Expected int parameter, received {value.__class__.__name__} instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
self.value: int = value
|
||||||
|
|
||||||
|
def _get_byte(self, byte: int) -> int:
|
||||||
|
return (self.value >> (8 * byte)) & 0xFF
|
||||||
|
|
||||||
|
def __eq__(self, other: typing.Any) -> bool:
|
||||||
|
return isinstance(other, Color) and self.value == other.value
|
||||||
|
|
||||||
|
def __ne__(self, other: typing.Any) -> bool:
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"#{self.value:0>6x}"
|
||||||
|
|
||||||
|
def __int__(self) -> int:
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"<Colour value={self.value}>"
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def r(self) -> int:
|
||||||
|
""":class:`int`: Returns the red component of the colour."""
|
||||||
|
return self._get_byte(2)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def g(self) -> int:
|
||||||
|
""":class:`int`: Returns the green component of the colour."""
|
||||||
|
return self._get_byte(1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def b(self) -> int:
|
||||||
|
""":class:`int`: Returns the blue component of the colour."""
|
||||||
|
return self._get_byte(0)
|
||||||
|
|
||||||
|
def to_rgb(self) -> typing.Tuple[int, int, int]:
|
||||||
|
"""
|
||||||
|
Tuple[:class:`int`, :class:`int`, :class:`int`]:
|
||||||
|
Returns an (r, g, b) tuple representing the colour."""
|
||||||
|
return (self.r, self.g, self.b)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_rgb(cls: typing.Type[CT], r: int, g: int, b: int) -> CT:
|
||||||
|
"""Constructs a :class:`Colour` from an RGB tuple."""
|
||||||
|
return cls((r << 16) + (g << 8) + b)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hex_code(cls, hex_code: str, /) -> Color:
|
||||||
|
"""Convert the given hexadecimal color code to a `Color`.
|
||||||
|
|
||||||
|
The inputs may be of the following format (case insensitive):
|
||||||
|
`1a2`, `#1a2`, `0x1a2` (for web-safe colors), or
|
||||||
|
`1a2b3c`, `#1a2b3c`, `0x1a2b3c` (for regular 3-byte color-codes).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
hex_code: :class:`str`
|
||||||
|
A hexadecimal color code to parse. This may optionally start with
|
||||||
|
a case insensitive `0x` or `#`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Color
|
||||||
|
A corresponding Color object.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
:class:`ValueError`
|
||||||
|
If ``hex_code`` is not a hexadecimal or is a invalid length.
|
||||||
|
"""
|
||||||
|
if hex_code.startswith("#"):
|
||||||
|
hex_code = hex_code[1:]
|
||||||
|
elif hex_code.startswith(("0x", "0X")):
|
||||||
|
hex_code = hex_code[2:]
|
||||||
|
|
||||||
|
if not all(c in string.hexdigits for c in hex_code):
|
||||||
|
raise ValueError("Color code must be hexadecimal")
|
||||||
|
|
||||||
|
if len(hex_code) == 3:
|
||||||
|
r, g, b = (c << 4 | c for c in (int(c, 16) for c in hex_code))
|
||||||
|
return cls.from_rgb(r, g, b)
|
||||||
|
|
||||||
|
if len(hex_code) == 6:
|
||||||
|
return cls.from_rgb(
|
||||||
|
int(hex_code[:2], 16), int(hex_code[2:4], 16), int(hex_code[4:6], 16)
|
||||||
|
)
|
||||||
|
|
||||||
|
raise ValueError("Color code is invalid length. Must be 3 or 6 digits")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls: typing.Type[CT]) -> CT:
|
||||||
|
"""A factory method that returns a :class:`Colour` with a value of ``0``."""
|
||||||
|
return cls(0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def random(
|
||||||
|
cls: typing.Type[CT],
|
||||||
|
*,
|
||||||
|
seed: typing.Optional[typing.Union[int, str, float, bytes, bytearray]] = None,
|
||||||
|
) -> CT:
|
||||||
|
"""A factory method that returns a :class:`Colour` with a random hue.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
seed: Optional[Union[:class:`int`, :class:`str`,
|
||||||
|
:class:`float`, :class:`bytes`, :class:`bytearray`]]
|
||||||
|
The seed to initialize the RNG with. If ``None`` is passed the default RNG is used.
|
||||||
|
"""
|
||||||
|
rand = random if seed is None else random.Random(seed)
|
||||||
|
return cls.from_hsv(rand.random(), 1, 1)
|
|
@ -9,9 +9,10 @@ from enum import Enum
|
||||||
|
|
||||||
from typing import List, Union, Optional
|
from typing import List, Union, Optional
|
||||||
|
|
||||||
|
from .colors import Color
|
||||||
from melisa.exceptions import EmbedFieldError
|
from melisa.exceptions import EmbedFieldError
|
||||||
from melisa.utils.types import UNDEFINED, UndefinedOr
|
from ...utils.api_model import APIModelBase
|
||||||
from melisa.utils.api_model import APIModelBase, APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
from melisa.utils.timestamp import Timestamp
|
from melisa.utils.timestamp import Timestamp
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ class EmbedType(Enum):
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedThumbnail(APIModelBase):
|
class EmbedThumbnail:
|
||||||
"""Representation of the Embed Thumbnail
|
"""Representation of the Embed Thumbnail
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -63,13 +64,13 @@ class EmbedThumbnail(APIModelBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url: str
|
url: str
|
||||||
proxy_url: APINullable[str] = None
|
proxy_url: APINullable[str] = UNDEFINED
|
||||||
height: APINullable[int] = None
|
height: APINullable[int] = UNDEFINED
|
||||||
width: APINullable[int] = None
|
width: APINullable[int] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedVideo(APIModelBase):
|
class EmbedVideo:
|
||||||
"""Representation of the Embed Video
|
"""Representation of the Embed Video
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -85,13 +86,13 @@ class EmbedVideo(APIModelBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url: str
|
url: str
|
||||||
proxy_url: APINullable[str] = None
|
proxy_url: APINullable[str] = UNDEFINED
|
||||||
height: APINullable[int] = None
|
height: APINullable[int] = UNDEFINED
|
||||||
width: APINullable[int] = None
|
width: APINullable[int] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedImage(APIModelBase):
|
class EmbedImage:
|
||||||
"""Representation of the Embed Image
|
"""Representation of the Embed Image
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -107,13 +108,13 @@ class EmbedImage(APIModelBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url: str
|
url: str
|
||||||
proxy_url: APINullable[str] = None
|
proxy_url: APINullable[str] = UNDEFINED
|
||||||
height: APINullable[int] = None
|
height: APINullable[int] = UNDEFINED
|
||||||
width: APINullable[int] = None
|
width: APINullable[int] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedProvider(APIModelBase):
|
class EmbedProvider:
|
||||||
"""Representation of the Embed Provider
|
"""Representation of the Embed Provider
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -124,12 +125,12 @@ class EmbedProvider(APIModelBase):
|
||||||
Url of provider
|
Url of provider
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: APINullable[str] = None
|
name: APINullable[str] = UNDEFINED
|
||||||
url: APINullable[str] = None
|
url: APINullable[str] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedAuthor(APIModelBase):
|
class EmbedAuthor:
|
||||||
"""Representation of the Embed Author
|
"""Representation of the Embed Author
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -145,13 +146,13 @@ class EmbedAuthor(APIModelBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
url: APINullable[str] = None
|
url: APINullable[str] = UNDEFINED
|
||||||
icon_url: APINullable[str] = None
|
icon_url: APINullable[str] = UNDEFINED
|
||||||
proxy_icon_url: APINullable[str] = None
|
proxy_icon_url: APINullable[str] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedFooter(APIModelBase):
|
class EmbedFooter:
|
||||||
"""Representation of the Embed Footer
|
"""Representation of the Embed Footer
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -165,12 +166,12 @@ class EmbedFooter(APIModelBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
icon_url: APINullable[str] = None
|
icon_url: APINullable[str] = UNDEFINED
|
||||||
proxy_icon_url: APINullable[str] = None
|
proxy_icon_url: APINullable[str] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class EmbedField(APIModelBase):
|
class EmbedField:
|
||||||
"""Representation of the Embed Field
|
"""Representation of the Embed Field
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -185,7 +186,7 @@ class EmbedField(APIModelBase):
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
value: str
|
value: str
|
||||||
inline: APINullable[bool] = False
|
inline: Optional[bool] = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
|
@ -202,7 +203,12 @@ class Embed(APIModelBase):
|
||||||
description: Optional[:class:`str`]
|
description: Optional[:class:`str`]
|
||||||
Description of embed
|
Description of embed
|
||||||
color: Optional[:class:`int`]
|
color: Optional[:class:`int`]
|
||||||
Color code of the embed
|
Color code of the embed.
|
||||||
|
If you really want to do something with a color,
|
||||||
|
feel free to convert it to the ``Color``: ::
|
||||||
|
|
||||||
|
color = Color(embed.color)
|
||||||
|
|
||||||
fields: Optional[List[:class:`~melisa.models.message.embed.EmbedField`]]
|
fields: Optional[List[:class:`~melisa.models.message.embed.EmbedField`]]
|
||||||
Fields information.
|
Fields information.
|
||||||
footer: Optional[:class:`~melisa.models.message.embed.EmbedFooter`]
|
footer: Optional[:class:`~melisa.models.message.embed.EmbedFooter`]
|
||||||
|
@ -221,19 +227,19 @@ class Embed(APIModelBase):
|
||||||
Video information.
|
Video information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
title: APINullable[str] = None
|
title: APINullable[str] = UNDEFINED
|
||||||
type: APINullable[EmbedType] = None
|
type: APINullable[EmbedType] = UNDEFINED
|
||||||
description: APINullable[str] = None
|
description: APINullable[str] = UNDEFINED
|
||||||
url: APINullable[str] = None
|
url: APINullable[str] = UNDEFINED
|
||||||
timestamp: APINullable[Timestamp] = None
|
timestamp: APINullable[Timestamp] = UNDEFINED
|
||||||
color: APINullable[int] = None
|
color: APINullable[Color] = UNDEFINED
|
||||||
footer: APINullable[EmbedFooter] = None
|
footer: APINullable[EmbedFooter] = UNDEFINED
|
||||||
image: APINullable[EmbedImage] = None
|
image: APINullable[EmbedImage] = UNDEFINED
|
||||||
thumbnail: APINullable[EmbedThumbnail] = None
|
thumbnail: APINullable[EmbedThumbnail] = UNDEFINED
|
||||||
video: APINullable[EmbedVideo] = None
|
video: APINullable[EmbedVideo] = UNDEFINED
|
||||||
provider: APINullable[EmbedProvider] = None
|
provider: APINullable[EmbedProvider] = UNDEFINED
|
||||||
author: APINullable[EmbedAuthor] = None
|
author: APINullable[EmbedAuthor] = UNDEFINED
|
||||||
fields: APINullable[List[EmbedField]] = None
|
fields: APINullable[List[EmbedField]] = UNDEFINED
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.title and len(self.title) > 256:
|
if self.title and len(self.title) > 256:
|
||||||
|
@ -251,6 +257,26 @@ class Embed(APIModelBase):
|
||||||
if self.fields and len(self.fields) > 25:
|
if self.fields and len(self.fields) > 25:
|
||||||
raise EmbedFieldError("""You can't set more than 25 embed fields!""")
|
raise EmbedFieldError("""You can't set more than 25 embed fields!""")
|
||||||
|
|
||||||
|
def set_color(self, color: Union[int, Color]) -> Embed:
|
||||||
|
"""Sets color in the supported by discord format.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
color: Union[:class:`~melisa.models.message.color.Color`, :class:`int`]
|
||||||
|
The datetime to set the timestamp to.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`~melisa.models.message.embed.Embed`
|
||||||
|
The new embed object.
|
||||||
|
"""
|
||||||
|
if isinstance(color, Color):
|
||||||
|
self.color = color.value
|
||||||
|
elif isinstance(color, int):
|
||||||
|
self.color = Color(value=color).value
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def set_timestamp(self, time: Union[Timestamp, datetime]) -> Embed:
|
def set_timestamp(self, time: Union[Timestamp, datetime]) -> Embed:
|
||||||
"""Sets timestamp in the supported by discord format.
|
"""Sets timestamp in the supported by discord format.
|
||||||
|
|
||||||
|
@ -261,7 +287,7 @@ class Embed(APIModelBase):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class:`~,e;osa.models.message.embed.Embed`
|
:class:`~melisa.models.message.embed.Embed`
|
||||||
The new embed object.
|
The new embed object.
|
||||||
"""
|
"""
|
||||||
self.timestamp = time.isoformat()
|
self.timestamp = time.isoformat()
|
||||||
|
@ -272,9 +298,9 @@ class Embed(APIModelBase):
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
*,
|
*,
|
||||||
url: Optional[str] = None,
|
url: Optional[str] = UNDEFINED,
|
||||||
icon_url: Optional[str] = None,
|
icon_url: Optional[str] = UNDEFINED,
|
||||||
proxy_icon_url: Optional[str] = None,
|
proxy_icon_url: Optional[str] = UNDEFINED,
|
||||||
) -> Embed:
|
) -> Embed:
|
||||||
"""Set the author for the embed.
|
"""Set the author for the embed.
|
||||||
|
|
||||||
|
@ -303,7 +329,7 @@ class Embed(APIModelBase):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_image(self, url: str, *, proxy_url: Optional[str] = None) -> Embed:
|
def set_image(self, url: str, *, proxy_url: APINullable[str] = UNDEFINED) -> Embed:
|
||||||
"""Set the image for the embed.
|
"""Set the image for the embed.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -322,7 +348,7 @@ class Embed(APIModelBase):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_thumbnail(self, url: str, *, proxy_url: Optional[str] = None) -> Embed:
|
def set_thumbnail(self, url: str, *, proxy_url: APINullable[str] = UNDEFINED) -> Embed:
|
||||||
"""Set the thumbnail for the embed.
|
"""Set the thumbnail for the embed.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -345,8 +371,8 @@ class Embed(APIModelBase):
|
||||||
self,
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
*,
|
*,
|
||||||
icon_url: Optional[str] = None,
|
icon_url: APINullable[str] = UNDEFINED,
|
||||||
proxy_icon_url: Optional[str] = None,
|
proxy_icon_url: APINullable[str] = UNDEFINED,
|
||||||
) -> Embed:
|
) -> Embed:
|
||||||
"""
|
"""
|
||||||
Sets the embed footer.
|
Sets the embed footer.
|
||||||
|
@ -392,7 +418,7 @@ class Embed(APIModelBase):
|
||||||
This embed.
|
This embed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.fields is None:
|
if self.fields is UNDEFINED:
|
||||||
self.fields = []
|
self.fields = []
|
||||||
|
|
||||||
self.fields.append(EmbedField(name=name, value=value, inline=inline))
|
self.fields.append(EmbedField(name=name, value=value, inline=inline))
|
||||||
|
@ -403,9 +429,9 @@ class Embed(APIModelBase):
|
||||||
self,
|
self,
|
||||||
index: int,
|
index: int,
|
||||||
*,
|
*,
|
||||||
name: UndefinedOr[str] = UNDEFINED,
|
name: APINullable[str] = UNDEFINED,
|
||||||
value: UndefinedOr[str] = UNDEFINED,
|
value: APINullable[str] = UNDEFINED,
|
||||||
inline: UndefinedOr[bool] = UNDEFINED,
|
inline: APINullable[bool] = UNDEFINED,
|
||||||
) -> Embed:
|
) -> Embed:
|
||||||
"""Edit an existing field on this embed.
|
"""Edit an existing field on this embed.
|
||||||
|
|
||||||
|
@ -413,11 +439,11 @@ class Embed(APIModelBase):
|
||||||
----------
|
----------
|
||||||
index: :class:`int`
|
index: :class:`int`
|
||||||
The index of the field to edit.
|
The index of the field to edit.
|
||||||
name: UndefinedOr[:class:`str`]
|
name: Optional[:class:`str`]
|
||||||
The name of the field.
|
The name of the field.
|
||||||
value: UndefinedOr[:class:`str`]
|
value: Optional[:class:`str`]
|
||||||
The value of the field.
|
The value of the field.
|
||||||
inline: UndefinedOr[:class:`bool`]
|
inline: Optional[:class:`bool`]
|
||||||
Whether the field should be displayed inline.
|
Whether the field should be displayed inline.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -468,7 +494,7 @@ class Embed(APIModelBase):
|
||||||
del self.fields[index]
|
del self.fields[index]
|
||||||
|
|
||||||
if not self.fields:
|
if not self.fields:
|
||||||
self.fields = None
|
self.fields = UNDEFINED
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from typing import List, TYPE_CHECKING, Optional, Dict
|
||||||
|
|
||||||
from ...utils import Snowflake, Timestamp
|
from ...utils import Snowflake, Timestamp
|
||||||
from ...utils import APIModelBase
|
from ...utils import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..guild.channel import Thread
|
from ..guild.channel import Thread
|
||||||
|
@ -170,36 +170,36 @@ class Message(APIModelBase):
|
||||||
Deprecated the stickers sent with the message
|
Deprecated the stickers sent with the message
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = UNDEFINED
|
||||||
channel_id: APINullable[Snowflake] = None
|
channel_id: APINullable[Snowflake] = UNDEFINED
|
||||||
guild_id: APINullable[Snowflake] = None
|
guild_id: APINullable[Snowflake] = UNDEFINED
|
||||||
author: APINullable[Dict] = None
|
author: APINullable[Dict] = UNDEFINED
|
||||||
member: APINullable[Dict] = None
|
member: APINullable[Dict] = UNDEFINED
|
||||||
content: APINullable[str] = None
|
content: APINullable[str] = UNDEFINED
|
||||||
timestamp: APINullable[Timestamp] = None
|
timestamp: APINullable[Timestamp] = UNDEFINED
|
||||||
edited_timestamp: APINullable[Timestamp] = None
|
edited_timestamp: APINullable[Timestamp] = UNDEFINED
|
||||||
tts: APINullable[bool] = None
|
tts: APINullable[bool] = UNDEFINED
|
||||||
mention_everyone: APINullable[bool] = None
|
mention_everyone: APINullable[bool] = UNDEFINED
|
||||||
mentions: APINullable[List] = None
|
mentions: APINullable[List] = UNDEFINED
|
||||||
mention_roles: APINullable[List] = None
|
mention_roles: APINullable[List] = UNDEFINED
|
||||||
mention_channels: APINullable[List] = None
|
mention_channels: APINullable[List] = UNDEFINED
|
||||||
attachments: APINullable[List] = None
|
attachments: APINullable[List] = UNDEFINED
|
||||||
embeds: APINullable[List] = None
|
embeds: APINullable[List] = UNDEFINED
|
||||||
reactions: APINullable[List] = None
|
reactions: APINullable[List] = UNDEFINED
|
||||||
nonce: APINullable[int] or APINullable[str] = None
|
nonce: APINullable[int] or APINullable[str] = UNDEFINED
|
||||||
pinned: APINullable[bool] = None
|
pinned: APINullable[bool] = UNDEFINED
|
||||||
webhook_id: APINullable[Snowflake] = None
|
webhook_id: APINullable[Snowflake] = UNDEFINED
|
||||||
type: APINullable[int] = None
|
type: APINullable[int] = UNDEFINED
|
||||||
activity: APINullable[Dict] = None
|
activity: APINullable[Dict] = UNDEFINED
|
||||||
application: APINullable[Dict] = None
|
application: APINullable[Dict] = UNDEFINED
|
||||||
application_id: APINullable[Snowflake] = None
|
application_id: APINullable[Snowflake] = UNDEFINED
|
||||||
message_reference: APINullable[Dict] = None
|
message_reference: APINullable[Dict] = UNDEFINED
|
||||||
flags: APINullable[int] = None
|
flags: APINullable[int] = UNDEFINED
|
||||||
interaction: APINullable[Dict] = None
|
interaction: APINullable[Dict] = UNDEFINED
|
||||||
thread: APINullable[Thread] = None
|
thread: APINullable[Thread] = UNDEFINED
|
||||||
components: APINullable[List] = None
|
components: APINullable[List] = UNDEFINED
|
||||||
sticker_items: APINullable[List] = None
|
sticker_items: APINullable[List] = UNDEFINED
|
||||||
stickers: APINullable[List] = None
|
stickers: APINullable[List] = UNDEFINED
|
||||||
|
|
||||||
async def pin(self, *, reason: Optional[str] = None):
|
async def pin(self, *, reason: Optional[str] = None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from typing import Optional, Tuple, List, Literal
|
||||||
|
|
||||||
from ...utils import Snowflake
|
from ...utils import Snowflake
|
||||||
from ...utils import APIModelBase
|
from ...utils import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
class BasePresence:
|
class BasePresence:
|
||||||
|
@ -223,19 +223,19 @@ class Activity(BasePresence, APIModelBase):
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
type: ActivityType
|
type: ActivityType
|
||||||
created_at: APINullable[int] = None
|
created_at: APINullable[int] = UNDEFINED
|
||||||
url: APINullable[str] = None
|
url: APINullable[str] = UNDEFINED
|
||||||
timestamps: APINullable[ActivityTimestamp] = None
|
timestamps: APINullable[ActivityTimestamp] = UNDEFINED
|
||||||
application_id: APINullable[Snowflake] = None
|
application_id: APINullable[Snowflake] = UNDEFINED
|
||||||
details: APINullable[str] = None
|
details: APINullable[str] = UNDEFINED
|
||||||
state: APINullable[str] = None
|
state: APINullable[str] = UNDEFINED
|
||||||
emoji: APINullable[ActivityEmoji] = None
|
emoji: APINullable[ActivityEmoji] = UNDEFINED
|
||||||
party: APINullable[ActivityParty] = None
|
party: APINullable[ActivityParty] = UNDEFINED
|
||||||
assets: APINullable[ActivityAssets] = None
|
assets: APINullable[ActivityAssets] = UNDEFINED
|
||||||
secrets: APINullable[ActivitySecrets] = None
|
secrets: APINullable[ActivitySecrets] = UNDEFINED
|
||||||
instance: APINullable[bool] = None
|
instance: APINullable[bool] = UNDEFINED
|
||||||
flags: APINullable[ActivityFlags] = None
|
flags: APINullable[ActivityFlags] = UNDEFINED
|
||||||
buttons: APINullable[List[ActivityButton]] = None
|
buttons: APINullable[List[ActivityButton]] = UNDEFINED
|
||||||
|
|
||||||
|
|
||||||
class StatusType(Enum):
|
class StatusType(Enum):
|
||||||
|
|
|
@ -8,7 +8,7 @@ from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from ...utils.api_model import APIModelBase
|
from ...utils.api_model import APIModelBase
|
||||||
from ...utils.types import APINullable
|
from ...utils.types import APINullable, UNDEFINED
|
||||||
from ...utils.snowflake import Snowflake
|
from ...utils.snowflake import Snowflake
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,20 +149,20 @@ class User(APIModelBase):
|
||||||
The user their premium type in a usable enum.
|
The user their premium type in a usable enum.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: APINullable[Snowflake] = None
|
id: APINullable[Snowflake] = UNDEFINED
|
||||||
username: APINullable[str] = None
|
username: APINullable[str] = UNDEFINED
|
||||||
discriminator: APINullable[str] = None
|
discriminator: APINullable[str] = UNDEFINED
|
||||||
avatar: APINullable[str] = None
|
avatar: APINullable[str] = UNDEFINED
|
||||||
bot: APINullable[bool] = None
|
bot: APINullable[bool] = UNDEFINED
|
||||||
system: APINullable[bool] = None
|
system: APINullable[bool] = UNDEFINED
|
||||||
mfa_enabled: APINullable[bool] = None
|
mfa_enabled: APINullable[bool] = UNDEFINED
|
||||||
banner: APINullable[str] = None
|
banner: APINullable[str] = UNDEFINED
|
||||||
accent_color: APINullable[int] = None
|
accent_color: APINullable[int] = UNDEFINED
|
||||||
local: APINullable[str] = None
|
local: APINullable[str] = UNDEFINED
|
||||||
verified: APINullable[bool] = None
|
verified: APINullable[bool] = UNDEFINED
|
||||||
email: APINullable[str] = None
|
email: APINullable[str] = UNDEFINED
|
||||||
premium_type: APINullable[int] = None
|
premium_type: APINullable[int] = UNDEFINED
|
||||||
public_flags: APINullable[int] = None
|
public_flags: APINullable[int] = UNDEFINED
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def premium(self) -> Optional[PremiumTypes]:
|
def premium(self) -> Optional[PremiumTypes]:
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
|
||||||
from dataclasses import _is_dataclass_instance, fields
|
from dataclasses import _is_dataclass_instance, fields
|
||||||
from enum import Enum, EnumMeta
|
from enum import Enum, EnumMeta
|
||||||
from inspect import getfullargspec
|
from inspect import getfullargspec
|
||||||
|
@ -19,45 +18,58 @@ from typing import (
|
||||||
Any,
|
Any,
|
||||||
get_origin,
|
get_origin,
|
||||||
Tuple,
|
Tuple,
|
||||||
get_args,
|
get_args, Optional,
|
||||||
)
|
)
|
||||||
|
|
||||||
from typing_extensions import get_type_hints
|
from typing_extensions import get_type_hints
|
||||||
|
|
||||||
from melisa.utils.types import APINullable, TypeCache
|
from melisa.utils.types import UndefinedType, TypeCache, UNDEFINED
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
def to_dict_without_none(model):
|
def _asdict_ignore_none(obj: Generic[T]) -> Union[Tuple, Dict, T]:
|
||||||
"""
|
"""
|
||||||
Converts discord model or other object to dict.
|
Returns a dict from a dataclass that ignores
|
||||||
|
all values that are None
|
||||||
|
Modification of _asdict_inner from dataclasses
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
obj: Generic[T]
|
||||||
|
The object to convert
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
A dict without None values
|
||||||
"""
|
"""
|
||||||
if _is_dataclass_instance(model):
|
|
||||||
result = []
|
|
||||||
|
|
||||||
for field in fields(model):
|
if _is_dataclass_instance(obj):
|
||||||
value = to_dict_without_none(getattr(model, field.name))
|
result = []
|
||||||
|
for f in fields(obj):
|
||||||
|
value = _asdict_ignore_none(getattr(obj, f.name))
|
||||||
|
|
||||||
if isinstance(value, Enum):
|
if isinstance(value, Enum):
|
||||||
result.append((field.name, value.value))
|
result.append((f.name, value.value))
|
||||||
elif value is not None and not field.name.startswith("_"):
|
# This if statement was added to the function
|
||||||
result.append((field.name, value))
|
elif not isinstance(value, UndefinedType) and not f.name.startswith(
|
||||||
|
"_"
|
||||||
|
):
|
||||||
|
result.append((f.name, value))
|
||||||
|
|
||||||
return dict(result)
|
return dict(result)
|
||||||
|
|
||||||
if isinstance(model, tuple) and hasattr(model, "_fields"):
|
elif isinstance(obj, tuple) and hasattr(obj, "_fields"):
|
||||||
return type(model)(*[to_dict_without_none(v) for v in model])
|
return type(obj)(*[_asdict_ignore_none(v) for v in obj])
|
||||||
|
|
||||||
if isinstance(model, (list, tuple)):
|
elif isinstance(obj, (list, tuple)):
|
||||||
return type(model)(to_dict_without_none(v) for v in model)
|
return type(obj)(_asdict_ignore_none(v) for v in obj)
|
||||||
|
|
||||||
if isinstance(model, dict):
|
elif isinstance(obj, dict):
|
||||||
return type(model)(
|
return type(obj)(
|
||||||
(to_dict_without_none(k), to_dict_without_none(v)) for k, v in model.items()
|
(_asdict_ignore_none(k), _asdict_ignore_none(v))
|
||||||
|
for k, v in obj.items()
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
return copy.deepcopy(model)
|
return copy.deepcopy(obj)
|
||||||
|
|
||||||
|
|
||||||
class APIModelBase:
|
class APIModelBase:
|
||||||
|
@ -65,12 +77,12 @@ class APIModelBase:
|
||||||
Represents an object which has been fetched from the Discord API.
|
Represents an object which has been fetched from the Discord API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_client = None
|
_client: Optional[Any] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _http(self):
|
def _http(self):
|
||||||
if not self._client:
|
if not self._client:
|
||||||
return None
|
raise AttributeError("Object is not yet linked to a client")
|
||||||
|
|
||||||
return self._client.http
|
return self._client.http
|
||||||
|
|
||||||
|
@ -78,17 +90,21 @@ class APIModelBase:
|
||||||
def set_client(cls, client):
|
def set_client(cls, client):
|
||||||
cls._client = client
|
cls._client = client
|
||||||
|
|
||||||
def __get_types(self, arg_type: type) -> Tuple[type]:
|
def __get_types(self, attr: str, arg_type: type) -> Tuple[type]:
|
||||||
origin = get_origin(arg_type)
|
origin = get_origin(arg_type)
|
||||||
|
|
||||||
if origin is Union:
|
if origin is Union:
|
||||||
|
# Ahh yes, typing module has no type annotations for this...
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
args: Tuple[type] = get_args(arg_type)
|
args: Tuple[type] = get_args(arg_type)
|
||||||
|
|
||||||
if 2 <= len(args) < 4:
|
if 2 <= len(args) < 4:
|
||||||
return args
|
return args
|
||||||
|
|
||||||
raise TypeError
|
raise ValueError(
|
||||||
|
f"Attribute `{attr}` in `{type(self).__name__}` has too many "
|
||||||
|
f"or not enough arguments! (got {len(args)} expected 2-3)"
|
||||||
|
)
|
||||||
|
|
||||||
return (arg_type,)
|
return (arg_type,)
|
||||||
|
|
||||||
|
@ -99,8 +115,8 @@ class APIModelBase:
|
||||||
if getattr(attr_type, "__factory__", None):
|
if getattr(attr_type, "__factory__", None):
|
||||||
factory = attr_type.__factory__
|
factory = attr_type.__factory__
|
||||||
|
|
||||||
if attr_value is None:
|
if attr_value is UNDEFINED:
|
||||||
return None
|
return UNDEFINED
|
||||||
|
|
||||||
if attr_type is not None and isinstance(attr_value, attr_type):
|
if attr_type is not None and isinstance(attr_value, attr_type):
|
||||||
return attr_value
|
return attr_value
|
||||||
|
@ -108,9 +124,6 @@ class APIModelBase:
|
||||||
if isinstance(attr_value, dict):
|
if isinstance(attr_value, dict):
|
||||||
return factory(attr_value)
|
return factory(attr_value)
|
||||||
|
|
||||||
if isinstance(attr_value, datetime.datetime):
|
|
||||||
return attr_value
|
|
||||||
|
|
||||||
return factory(attr_value)
|
return factory(attr_value)
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
|
@ -126,14 +139,19 @@ class APIModelBase:
|
||||||
if attr.startswith("_"):
|
if attr.startswith("_"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
types = self.__get_types(attr_type)
|
types = self.__get_types(attr, attr_type)
|
||||||
|
|
||||||
types = tuple(
|
types = tuple(
|
||||||
filter(lambda tpe: tpe is not None and tpe is not None, types)
|
filter(
|
||||||
|
lambda tpe: tpe is not None and tpe is not UNDEFINED, types
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not types:
|
if not types:
|
||||||
raise TypeError
|
raise ValueError(
|
||||||
|
f"Attribute `{attr}` in `{type(self).__name__}` only "
|
||||||
|
"consisted of missing/optional type!"
|
||||||
|
)
|
||||||
|
|
||||||
specific_tp = types[0]
|
specific_tp = types[0]
|
||||||
|
|
||||||
|
@ -143,7 +161,7 @@ class APIModelBase:
|
||||||
specific_tp = tp
|
specific_tp = tp
|
||||||
|
|
||||||
if isinstance(specific_tp, EnumMeta) and not attr_gotten:
|
if isinstance(specific_tp, EnumMeta) and not attr_gotten:
|
||||||
attr_value = None
|
attr_value = UNDEFINED
|
||||||
elif tp == list and attr_gotten and (classes := get_args(types[0])):
|
elif tp == list and attr_gotten and (classes := get_args(types[0])):
|
||||||
attr_value = [
|
attr_value = [
|
||||||
self.__attr_convert(attr_item, classes[0])
|
self.__attr_convert(attr_item, classes[0])
|
||||||
|
@ -179,23 +197,30 @@ class APIModelBase:
|
||||||
return super().__str__()
|
return super().__str__()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Generic[T], data: Dict[str, Union[str, bool, int, Any]]) -> T:
|
def from_dict(
|
||||||
|
cls: Generic[T], data: Dict[str, Union[str, bool, int, Any]]
|
||||||
|
) -> T:
|
||||||
"""
|
"""
|
||||||
Parse an API object from a dictionary.
|
Parse an API object from a dictionary.
|
||||||
"""
|
"""
|
||||||
if isinstance(data, cls):
|
if isinstance(data, cls):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
# Disable inspection for IDE because this is valid code for the
|
||||||
|
# inherited classes:
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
return cls(
|
return cls(
|
||||||
**dict(
|
**dict(
|
||||||
map(
|
map(
|
||||||
lambda key: (
|
lambda key: (
|
||||||
key,
|
key,
|
||||||
data[key].value if isinstance(data[key], Enum) else data[key],
|
data[key].value
|
||||||
|
if isinstance(data[key], Enum)
|
||||||
|
else data[key],
|
||||||
),
|
),
|
||||||
filter(
|
filter(
|
||||||
lambda object_argument: data.get(object_argument) is not None,
|
lambda object_argument: data.get(object_argument)
|
||||||
|
is not None,
|
||||||
getfullargspec(cls.__init__).args,
|
getfullargspec(cls.__init__).args,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -203,4 +228,8 @@ class APIModelBase:
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_dict(self) -> Dict:
|
def to_dict(self) -> Dict:
|
||||||
return to_dict_without_none(self)
|
"""
|
||||||
|
Transform the current object to a dictionary representation. Parameters that
|
||||||
|
start with an underscore are not serialized.
|
||||||
|
"""
|
||||||
|
return _asdict_ignore_none(self)
|
||||||
|
|
|
@ -35,9 +35,7 @@ T = TypeVar("T")
|
||||||
|
|
||||||
Coro = TypeVar("Coro", bound=Callable[..., Coroutine[Any, Any, Any]])
|
Coro = TypeVar("Coro", bound=Callable[..., Coroutine[Any, Any, Any]])
|
||||||
|
|
||||||
APINullable = Union[T, None]
|
APINullable = Union[T, UndefinedType]
|
||||||
|
|
||||||
UndefinedOr = Union[T, UndefinedType]
|
|
||||||
|
|
||||||
|
|
||||||
class Singleton(type):
|
class Singleton(type):
|
||||||
|
|
15
tests/test_colors.py
Normal file
15
tests/test_colors.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from melisa import Color
|
||||||
|
|
||||||
|
|
||||||
|
rgb_right_example = (3, 217, 147)
|
||||||
|
|
||||||
|
|
||||||
|
class TestColor:
|
||||||
|
def test_from_rgb_converting(self):
|
||||||
|
assert Color.from_rgb(3, 217, 147).to_rgb() == rgb_right_example
|
||||||
|
|
||||||
|
def test_from_hex_code_converting(self):
|
||||||
|
assert Color.from_hex_code("#03d993").to_rgb() == rgb_right_example
|
||||||
|
|
||||||
|
def test_from_decimal_converting(self):
|
||||||
|
assert Color(252307).to_rgb() == rgb_right_example
|
|
@ -1,10 +1,11 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from melisa import Embed, Timestamp
|
from melisa import Embed, Timestamp, Color
|
||||||
|
|
||||||
dict_embed = {
|
dict_embed = {
|
||||||
'title': 'my title',
|
'title': 'my title',
|
||||||
'description': 'simple description',
|
'description': 'simple description',
|
||||||
|
'color': 252307,
|
||||||
'timestamp': datetime.datetime.utcfromtimestamp(1649748784).isoformat(),
|
'timestamp': datetime.datetime.utcfromtimestamp(1649748784).isoformat(),
|
||||||
'footer': {
|
'footer': {
|
||||||
'text': 'cool footer text'
|
'text': 'cool footer text'
|
||||||
|
@ -14,6 +15,16 @@ dict_embed = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMBED = Embed(title="my title", description="simple description")
|
||||||
|
EMBED.set_author(name="best author")
|
||||||
|
EMBED.set_footer(text="cool footer text")
|
||||||
|
EMBED.set_color(Color.from_hex_code("#03d993"))
|
||||||
|
EMBED.set_timestamp(Timestamp.parse(1649748784))
|
||||||
|
|
||||||
|
|
||||||
|
def has_key_vals(actual, required):
|
||||||
|
return all(actual.get(key) == val for key, val in required.items())
|
||||||
|
|
||||||
|
|
||||||
class TestEmbed:
|
class TestEmbed:
|
||||||
def test_total_length_when_embed_is_empty(self):
|
def test_total_length_when_embed_is_empty(self):
|
||||||
|
@ -54,9 +65,15 @@ class TestEmbed:
|
||||||
embed.set_footer(text="cool footer text")
|
embed.set_footer(text="cool footer text")
|
||||||
assert embed.total_length() == 53
|
assert embed.total_length() == 53
|
||||||
|
|
||||||
def test_comparing_embeds(self):
|
def test_embed_to_dict(self):
|
||||||
embed = Embed(title="my title", description="simple description")
|
"""
|
||||||
embed.set_author(name="best author")
|
Tests whether or not the dispatch class its string conversion
|
||||||
embed.set_footer(text="cool footer text")
|
is correct.
|
||||||
embed.set_timestamp(Timestamp.parse(1649748784))
|
"""
|
||||||
assert embed.to_dict() == dict_embed
|
assert has_key_vals(EMBED.to_dict(), dict_embed)
|
||||||
|
|
||||||
|
def test_embed_from_dict(self):
|
||||||
|
assert has_key_vals(
|
||||||
|
Embed.from_dict(dict_embed).to_dict(),
|
||||||
|
dict_embed
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue