mirror of
https://github.com/MelisaDev/melisa.git
synced 2024-09-22 19:22:01 +03:00
embed, but without fields yet and some other changes
This commit is contained in:
parent
1ca2b593e4
commit
cefbf9e7f7
7 changed files with 503 additions and 88 deletions
|
@ -43,7 +43,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||||
.. note::
|
.. note::
|
||||||
It will not be received by :meth:`Client.wait_for`.
|
It will not be received by :meth:`Client.wait_for`.
|
||||||
|
|
||||||
:param exception: Produced error.
|
:param exception: Exception.
|
||||||
:type exception: :class:`Exception`
|
:type exception: :class:`Exception`
|
||||||
|
|
||||||
.. function:: on_channel_create(channel)
|
.. function:: on_channel_create(channel)
|
||||||
|
@ -112,7 +112,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||||
See the docs of :attr:`Intents.MESSAGE_CONTENT` for more information.
|
See the docs of :attr:`Intents.MESSAGE_CONTENT` for more information.
|
||||||
|
|
||||||
:param message: The current message.
|
:param message: The current message.
|
||||||
:type message: :class:`models.message.message.Message
|
:type message: :class:`models.message.message.Message`
|
||||||
|
|
||||||
.. function:: on_shard_ready(shard_id)
|
.. function:: on_shard_ready(shard_id)
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,28 @@ class PrivilegedIntentsRequired(ClientException):
|
||||||
super().__init__(message.format(self.shard_id))
|
super().__init__(message.format(self.shard_id))
|
||||||
|
|
||||||
|
|
||||||
|
class EmbedFieldError(MelisaException, ValueError):
|
||||||
|
"""Occurs when an embed field is too large."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def characters_from_desc(cls, field_type: str, current_size: int, max_size: int):
|
||||||
|
"""Create an instance by description.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
field_type :class:`str`
|
||||||
|
The type/name of the field.
|
||||||
|
current_size :class:`int`
|
||||||
|
The current size of the field.
|
||||||
|
max_si :class:`int`
|
||||||
|
The maximum size of the field.
|
||||||
|
"""
|
||||||
|
return cls(
|
||||||
|
f"{field_type} can have maximum {max_size} characters."
|
||||||
|
f" (Current size: {current_size})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HTTPException(MelisaException):
|
class HTTPException(MelisaException):
|
||||||
"""Occurs when an HTTP request operation fails."""
|
"""Occurs when an HTTP request operation fails."""
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ from typing import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..message.message import Message
|
from ..message.message import Message
|
||||||
|
from ...exceptions import EmbedFieldError
|
||||||
|
from ...models.message.embed import Embed
|
||||||
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
|
||||||
|
@ -502,7 +504,9 @@ class MessageableChannel(Channel):
|
||||||
headers={"X-Audit-Log-Reason": reason},
|
headers={"X-Audit-Log-Reason": reason},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def send(self, content: str = None) -> Message:
|
async def send(
|
||||||
|
self, content: str = None, *, embed: Embed = None, embeds: List[Embed] = None
|
||||||
|
) -> Message:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Sends a message to the destination with the content given.
|
Sends a message to the destination with the content given.
|
||||||
|
@ -513,6 +517,10 @@ class MessageableChannel(Channel):
|
||||||
----------
|
----------
|
||||||
content: Optional[:class:`str`]
|
content: Optional[:class:`str`]
|
||||||
The content of the message to send.
|
The content of the message to send.
|
||||||
|
embed: Optional[:class:`~melisa.models.message.embed.Embed`]
|
||||||
|
Embed
|
||||||
|
embeds: Optional[List[:class:`~melisa.models.message.embed.Embed`]]
|
||||||
|
List of embeds
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
-------
|
-------
|
||||||
|
@ -526,11 +534,22 @@ class MessageableChannel(Channel):
|
||||||
|
|
||||||
# ToDo: Add other parameters
|
# ToDo: Add other parameters
|
||||||
|
|
||||||
|
if embeds is None:
|
||||||
|
embeds = []
|
||||||
|
|
||||||
content = str(content) if content is not None else None
|
content = str(content) if content is not None else None
|
||||||
|
embeds.append(embed.to_dict()) if embed is not None else None
|
||||||
|
|
||||||
|
for _embed in embeds:
|
||||||
|
if embed.total_length() > 6000:
|
||||||
|
raise EmbedFieldError.characters_from_desc(
|
||||||
|
"Embed", embed.total_length(), 6000
|
||||||
|
)
|
||||||
|
|
||||||
return Message.from_dict(
|
return Message.from_dict(
|
||||||
await self._http.post(
|
await self._http.post(
|
||||||
f"/channels/{self.id}/messages", data={"content": content}
|
f"/channels/{self.id}/messages",
|
||||||
|
data={"content": content, "embeds": embeds},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -169,85 +169,6 @@ class SystemChannelFlags(IntEnum):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class GuildFeatures(Enum):
|
|
||||||
"""Guild Features
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
ANIMATED_ICON:
|
|
||||||
Guild has access to set an animated guild icon
|
|
||||||
BANNER:
|
|
||||||
Guild has access to set a guild banner image
|
|
||||||
COMMERCE:
|
|
||||||
Guild has access to use commerce features (i.e. create store channels)
|
|
||||||
COMMUNITY:
|
|
||||||
Guild can enable welcome screen, Membership Screening,
|
|
||||||
stage channels and discovery, and receives community updates
|
|
||||||
DISCOVERABLE:
|
|
||||||
Guild is able to be discovered in the directory
|
|
||||||
FEATURABLE:
|
|
||||||
Guild is able to be featured in the directory
|
|
||||||
INVITE_SPLASH:
|
|
||||||
Guild has access to set an invite splash background
|
|
||||||
MEMBER_VERIFICATION_GATE_ENABLED:
|
|
||||||
Guild has enabled Membership Screening
|
|
||||||
MONETIZATION_ENABLED:
|
|
||||||
Guild has enabled monetization
|
|
||||||
MORE_STICKERS:
|
|
||||||
Guild has increased custom sticker slots
|
|
||||||
NEWS:
|
|
||||||
Guild has access to create news channels
|
|
||||||
PARTNERED:
|
|
||||||
Guild is partnered
|
|
||||||
PREVIEW_ENABLED:
|
|
||||||
Guild can be previewed before joining via Membership Screening or the directory
|
|
||||||
PRIVATE_THREADS:
|
|
||||||
Guild has access to create private threads
|
|
||||||
ROLE_ICONS:
|
|
||||||
Guild is able to set role icons
|
|
||||||
SEVEN_DAY_THREAD_ARCHIVE:
|
|
||||||
Guild has access to the seven day archive time for threads
|
|
||||||
THREE_DAY_THREAD_ARCHIVE:
|
|
||||||
Guild has access to the three day archive time for threads
|
|
||||||
TICKETED_EVENTS_ENABLED:
|
|
||||||
Guild has enabled ticketed events
|
|
||||||
VANITY_URL:
|
|
||||||
Guild has access to set a vanity URL
|
|
||||||
VERIFIED:
|
|
||||||
Guild is verified
|
|
||||||
VIP_REGIONS:
|
|
||||||
Guild has access to set 384kbps bitrate in voice (previously VIP voice servers)
|
|
||||||
WELCOME_SCREEN_ENABLED:
|
|
||||||
Guild has enabled the welcome screen
|
|
||||||
EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT:
|
|
||||||
Unkown. Found during testing. Not listed in Discord API docs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
ANIMATED_ICON = "ANIMATED_ICON"
|
|
||||||
BANNER = "BANNER"
|
|
||||||
COMMERCE = "COMMERCE"
|
|
||||||
COMMUNITY = "COMMUNITY"
|
|
||||||
DISCOVERABLE = "DISCOVERABLE"
|
|
||||||
FEATURABLE = "FEATURABLE"
|
|
||||||
INVITE_SPLASH = "INVITE_SPLASH"
|
|
||||||
MEMBER_VERIFICATION_GATE_ENABLED = "MEMBER_VERIFICATION_GATE_ENABLED"
|
|
||||||
MONETIZATION_ENABLED = "MONETIZATION_ENABLED"
|
|
||||||
MORE_STICKERS = "MORE_STICKERS"
|
|
||||||
NEWS = "NEWS"
|
|
||||||
PARTNERED = "PARTNERED"
|
|
||||||
PREVIEW_ENABLED = "PREVIEW_ENABLED"
|
|
||||||
PRIVATE_THREADS = "PRIVATE_THREADS"
|
|
||||||
ROLE_ICONS = "ROLE_ICONS"
|
|
||||||
SEVEN_DAY_THREAD_ARCHIVE = "SEVEN_DAY_THREAD_ARCHIVE"
|
|
||||||
THREE_DAY_THREAD_ARCHIVE = "THREE_DAY_THREAD_ARCHIVE"
|
|
||||||
TICKETED_EVENTS_ENABLED = "TICKETED_EVENTS_ENABLED"
|
|
||||||
VANITY_URL = "VANITY_URL"
|
|
||||||
VERIFIED = "VERIFIED"
|
|
||||||
VIP_REGIONS = "VIP_REGIONS"
|
|
||||||
WELCOME_SCREEN_ENABLED = "WELCOME_SCREEN_ENABLED"
|
|
||||||
EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT = "EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class Guild(APIModelBase):
|
class Guild(APIModelBase):
|
||||||
"""Guilds in Discord represent an isolated collection of users and channels,
|
"""Guilds in Discord represent an isolated collection of users and channels,
|
||||||
|
@ -289,7 +210,7 @@ class Guild(APIModelBase):
|
||||||
Default message notifications level
|
Default message notifications level
|
||||||
explicit_content_filter: :class:`int`
|
explicit_content_filter: :class:`int`
|
||||||
Explicit content filter level
|
Explicit content filter level
|
||||||
features: APINullable[:class:`typing.Any`]
|
features: APINullable[List[:class:`str`]]
|
||||||
Enabled guild features
|
Enabled guild features
|
||||||
roles: APINullable[:class:`typing.Any`]
|
roles: APINullable[:class:`typing.Any`]
|
||||||
Roles in the guild
|
Roles in the guild
|
||||||
|
@ -388,7 +309,7 @@ class Guild(APIModelBase):
|
||||||
verification_level: APINullable[int] = None
|
verification_level: APINullable[int] = None
|
||||||
default_message_notifications: APINullable[int] = None
|
default_message_notifications: APINullable[int] = None
|
||||||
explicit_content_filter: APINullable[int] = None
|
explicit_content_filter: APINullable[int] = None
|
||||||
features: APINullable[List[GuildFeatures]] = None
|
features: APINullable[List[str]] = None
|
||||||
roles: APINullable[List] = None
|
roles: APINullable[List] = None
|
||||||
emojis: APINullable[List] = None
|
emojis: APINullable[List] = None
|
||||||
# TODO: Make a structures of emoji and role
|
# TODO: Make a structures of emoji and role
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# Copyright MelisaDev 2022 - Present
|
# Copyright MelisaDev 2022 - Present
|
||||||
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
||||||
|
|
||||||
from .message import MessageActivityType, MessageFlags, MessageType, Message
|
from .message import *
|
||||||
|
from .embed import *
|
||||||
__all__ = ("MessageActivityType", "MessageFlags", "MessageType", "Message")
|
|
||||||
|
|
396
melisa/models/message/embed.py
Normal file
396
melisa/models/message/embed.py
Normal file
|
@ -0,0 +1,396 @@
|
||||||
|
# Copyright MelisaDev 2022 - Present
|
||||||
|
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from typing import List, Union, Optional
|
||||||
|
|
||||||
|
from melisa.exceptions import EmbedFieldError
|
||||||
|
from melisa.utils.api_model import APIModelBase, APINullable
|
||||||
|
from melisa.utils.timestamp import Timestamp
|
||||||
|
|
||||||
|
|
||||||
|
class EmbedType(Enum):
|
||||||
|
"""
|
||||||
|
Embed types are "loosely defined" and, for the most part,
|
||||||
|
are not used by our clients for rendering.
|
||||||
|
Embed attributes power what is rendered.
|
||||||
|
Embed types should be considered deprecated and might be removed in a future API version.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
__________
|
||||||
|
RICH:
|
||||||
|
Generic embed rendered from embed attributes
|
||||||
|
IMAGE
|
||||||
|
Image embed
|
||||||
|
VIDEO
|
||||||
|
Video embed
|
||||||
|
GIFV
|
||||||
|
Animated gif image embed rendered as a video embed
|
||||||
|
ARTICLE
|
||||||
|
Article embed
|
||||||
|
LINK
|
||||||
|
Link embed
|
||||||
|
"""
|
||||||
|
|
||||||
|
RICH = "rich"
|
||||||
|
IMAGE = "image"
|
||||||
|
VIDEO = "video"
|
||||||
|
GIFV = "gifv"
|
||||||
|
ARTICLE = "article"
|
||||||
|
LINK = "link"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedThumbnail(APIModelBase):
|
||||||
|
"""Representation of the Embed Thumbnail
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
url: :class:`str`
|
||||||
|
Source url of the thumbnail
|
||||||
|
proxy_url: Optional[:class:`str`]
|
||||||
|
A proxied url of the thumbnail
|
||||||
|
height: Optional[:class:`int`]
|
||||||
|
Height of the thumbnail
|
||||||
|
width: Optional[:class:`int`]
|
||||||
|
Width of the thumbnail
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
proxy_url: APINullable[str] = None
|
||||||
|
height: APINullable[int] = None
|
||||||
|
width: APINullable[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedVideo(APIModelBase):
|
||||||
|
"""Representation of the Embed Video
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
url: Optional[:class:`str`]
|
||||||
|
Source url of the video
|
||||||
|
proxy_url: Optional[:class:`str`]
|
||||||
|
A proxied url of the video
|
||||||
|
height: Optional[:class:`int`]
|
||||||
|
Height of the video
|
||||||
|
width: Optional[:class:`int`]
|
||||||
|
Width of the video
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
proxy_url: APINullable[str] = None
|
||||||
|
height: APINullable[int] = None
|
||||||
|
width: APINullable[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedImage(APIModelBase):
|
||||||
|
"""Representation of the Embed Image
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
url: :class:`str`
|
||||||
|
Source url of image (only supports http(s) and attachments)
|
||||||
|
proxy_url: Optional[:class:`str`]
|
||||||
|
A proxied url of the image
|
||||||
|
height: Optional[:class:`int`]
|
||||||
|
Height of the image
|
||||||
|
width: Optional[:class:`int`]
|
||||||
|
Width of the image
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
proxy_url: APINullable[str] = None
|
||||||
|
height: APINullable[int] = None
|
||||||
|
width: APINullable[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedProvider(APIModelBase):
|
||||||
|
"""Representation of the Embed Provider
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name: Optional[:class:`str`]
|
||||||
|
Name of provider
|
||||||
|
url: Optional[:class:`str`]
|
||||||
|
Url of provider
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: APINullable[str] = None
|
||||||
|
url: APINullable[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedAuthor(APIModelBase):
|
||||||
|
"""Representation of the Embed Author
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
Name of author
|
||||||
|
url: Optional[:class:`str`]
|
||||||
|
Url of author
|
||||||
|
icon_url: Optional[:class:`str`]
|
||||||
|
Url of author icon (only supports http(s) and attachments)
|
||||||
|
proxy_icon_url: Optional[:class:`str`]
|
||||||
|
A proxied url of author icon
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
url: APINullable[str] = None
|
||||||
|
icon_url: APINullable[str] = None
|
||||||
|
proxy_icon_url: APINullable[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedFooter(APIModelBase):
|
||||||
|
"""Representation of the Embed Footer
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
text: :class:`str`
|
||||||
|
Footer text
|
||||||
|
icon_url: Optional[:class:`str`]
|
||||||
|
Url of footer icon (only supports http(s) and attachments)
|
||||||
|
proxy_icon_url: Optional[:class:`str`]
|
||||||
|
A proxied url of footer icon
|
||||||
|
"""
|
||||||
|
|
||||||
|
text: str
|
||||||
|
icon_url: APINullable[str] = None
|
||||||
|
proxy_icon_url: APINullable[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class EmbedField(APIModelBase):
|
||||||
|
"""Representation of the Embed Field
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
Name of the field
|
||||||
|
value: :class:`str`
|
||||||
|
Value of the field
|
||||||
|
inline: Optional[:class:`bool`]
|
||||||
|
Whether or not this field should display inline
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
value: str
|
||||||
|
inline: APINullable[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class Embed(APIModelBase):
|
||||||
|
# ToDo: Add fields set method
|
||||||
|
|
||||||
|
"""Represents an embed sent in with message within Discord.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
|
||||||
|
title: Optional[:class:`str`]
|
||||||
|
Title of embed
|
||||||
|
type: Optional[:class:`~melisa.models.message.embed.EmbedType`]
|
||||||
|
Type of embed (always "rich" for webhook embeds)
|
||||||
|
description: Optional[:class:`str`]
|
||||||
|
Description of embed
|
||||||
|
color: Optional[:class:`int`]
|
||||||
|
Color code of the embed
|
||||||
|
fields: Optional[List[:class:`~melisa.models.message.embed.EmbedField`]]
|
||||||
|
Fields information.
|
||||||
|
footer: Optional[:class:`~melisa.models.message.embed.EmbedFooter`]
|
||||||
|
Footer information.
|
||||||
|
image: Optional[:class:`~melisa.models.message.embed.EmbedImage`]
|
||||||
|
Image information.
|
||||||
|
provider: Optional[:class:`~melisa.models.message.embed.EmbedProvider`]
|
||||||
|
Provider information.
|
||||||
|
thumbnail: Optional[:class:`~melisa.models.message.embed.EmbedThumbnail`]
|
||||||
|
Thumbnail information.
|
||||||
|
timestamp: Optional[:class:`~melisa.utils.timestamp.Timestamp`]
|
||||||
|
Timestamp of embed content
|
||||||
|
url: Optional[:class:`str`]
|
||||||
|
Url of embed
|
||||||
|
video: Optional[:class:`~melisa.models.message.embed.EmbedVideo`]
|
||||||
|
Video information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
title: APINullable[str] = None
|
||||||
|
type: APINullable[EmbedType] = None
|
||||||
|
description: APINullable[str] = None
|
||||||
|
url: APINullable[str] = None
|
||||||
|
timestamp: APINullable[Timestamp] = None
|
||||||
|
color: APINullable[int] = None
|
||||||
|
footer: APINullable[EmbedFooter] = None
|
||||||
|
image: APINullable[EmbedImage] = None
|
||||||
|
thumbnail: APINullable[EmbedThumbnail] = None
|
||||||
|
video: APINullable[EmbedVideo] = None
|
||||||
|
provider: APINullable[EmbedProvider] = None
|
||||||
|
author: APINullable[EmbedAuthor] = None
|
||||||
|
fields: APINullable[List[EmbedField]] = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.title and len(self.title) > 256:
|
||||||
|
raise EmbedFieldError.characters_from_desc(
|
||||||
|
"Embed Title",
|
||||||
|
len(self.title),
|
||||||
|
256,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.description and len(self.description) > 4096:
|
||||||
|
raise EmbedFieldError.characters_from_desc(
|
||||||
|
"Embed Description", len(self.description), 4096
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.fields and len(self.fields) > 25:
|
||||||
|
raise EmbedFieldError("""You can't set more than 25 embed fields!""")
|
||||||
|
|
||||||
|
def set_timestamp(self, time: Union[Timestamp, datetime]) -> Embed:
|
||||||
|
"""Sets timestamp in the supported by discord format.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
time: :class:`~melisa.utils.timestamp.Timestamp`
|
||||||
|
The datetime to set the timestamp to.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`~,e;osa.models.message.embed.Embed`
|
||||||
|
The new embed object.
|
||||||
|
"""
|
||||||
|
self.timestamp = time.isoformat()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_author(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
*,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
icon_url: Optional[str] = None,
|
||||||
|
proxy_icon_url: Optional[str] = None,
|
||||||
|
) -> Embed:
|
||||||
|
"""Set the author for the embed.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
Name of author
|
||||||
|
url: Optional[:class:`str`]
|
||||||
|
Url of author icon (only supports http(s) and attachments)
|
||||||
|
icon_url: Optional[:class:`str`]
|
||||||
|
Url of author
|
||||||
|
proxy_icon_url: Optional[:class:`str`]
|
||||||
|
A proxied url of author icon
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`~melisa.models.message.embed.Embed`
|
||||||
|
Updated embed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.author = EmbedAuthor(
|
||||||
|
name=name,
|
||||||
|
url=url,
|
||||||
|
icon_url=icon_url,
|
||||||
|
proxy_icon_url=proxy_icon_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_image(self, url: str, *, proxy_url: Optional[str] = None) -> Embed:
|
||||||
|
"""Set the image for the embed.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
url: :class:`str`
|
||||||
|
Source url of image (only supports http(s) and attachments)
|
||||||
|
proxy_url: Optional[:class:`str`]
|
||||||
|
A proxied url of the image
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`~melisa.models.message.embed.Embed`
|
||||||
|
Updated embed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.image = EmbedImage(url=url, proxy_url=proxy_url)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_thumbnail(self, url: str, *, proxy_url: Optional[str] = None) -> Embed:
|
||||||
|
"""Set the thumbnail for the embed.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
url: :class:`str`
|
||||||
|
Source url of thumbnail (only supports http(s) and attachments)
|
||||||
|
proxy_url: Optional[:class:`str`]
|
||||||
|
A proxied url of the thumbnail
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`~melisa.models.message.embed.Embed`
|
||||||
|
Updated embed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.thumbnail = EmbedThumbnail(url=url, proxy_url=proxy_url)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_footer(
|
||||||
|
self,
|
||||||
|
text: str,
|
||||||
|
*,
|
||||||
|
icon_url: Optional[str] = None,
|
||||||
|
proxy_icon_url: Optional[str] = None,
|
||||||
|
) -> Embed:
|
||||||
|
"""
|
||||||
|
Sets the embed footer.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text: :class:`str`
|
||||||
|
Footer text
|
||||||
|
icon_url: Optional[:class:`str`]
|
||||||
|
Url of footer icon (only supports http(s) and attachments)
|
||||||
|
proxy_icon_url: Optional[:class:`str`]
|
||||||
|
A proxied url of footer icon
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`~melisa.models.message.embed.Embed`
|
||||||
|
Updated embed.
|
||||||
|
"""
|
||||||
|
self.footer = EmbedFooter(
|
||||||
|
text=text, icon_url=icon_url, proxy_icon_url=proxy_icon_url
|
||||||
|
)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def total_length(self) -> int:
|
||||||
|
"""Get the total character count of the embed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`int`
|
||||||
|
The total character count of this embed, including title, description,
|
||||||
|
fields, footer, and author combined.
|
||||||
|
"""
|
||||||
|
total = len(self.title or "") + len(self.description or "")
|
||||||
|
|
||||||
|
if self.fields:
|
||||||
|
for field in self.fields:
|
||||||
|
total += len(field.name) + len(field.value)
|
||||||
|
|
||||||
|
if self.footer and self.footer.text:
|
||||||
|
total += len(self.footer.text)
|
||||||
|
|
||||||
|
if self.author and self.author.name:
|
||||||
|
total += len(self.author.name)
|
||||||
|
|
||||||
|
return total
|
58
tests/test_embeds.py
Normal file
58
tests/test_embeds.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from melisa import Embed, Timestamp
|
||||||
|
|
||||||
|
dict_embed = {
|
||||||
|
'title': 'my title',
|
||||||
|
'description': 'simple description',
|
||||||
|
'timestamp': datetime.datetime.utcfromtimestamp(1649748784).isoformat(),
|
||||||
|
'footer': {
|
||||||
|
'text': 'cool footer text'
|
||||||
|
},
|
||||||
|
'author': {
|
||||||
|
'name': 'best author'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestEmbed:
|
||||||
|
def test_total_length_when_embed_is_empty(self):
|
||||||
|
embed = Embed()
|
||||||
|
assert embed.total_length() == 0
|
||||||
|
|
||||||
|
def test_total_length_when_title_is_none(self):
|
||||||
|
embed = Embed(title=None)
|
||||||
|
assert embed.total_length() == 0
|
||||||
|
|
||||||
|
def test_total_length_title(self):
|
||||||
|
embed = Embed(title="my title")
|
||||||
|
assert embed.total_length() == 8
|
||||||
|
|
||||||
|
def test_total_length_when_description_is_none(self):
|
||||||
|
embed = Embed(description=None)
|
||||||
|
assert embed.total_length() == 0
|
||||||
|
|
||||||
|
def test_total_length_description(self):
|
||||||
|
embed = Embed(description="simple description")
|
||||||
|
assert embed.total_length() == 18
|
||||||
|
|
||||||
|
def test_total_length_author_name(self):
|
||||||
|
embed = Embed().set_author(name="best author")
|
||||||
|
assert embed.total_length() == 11
|
||||||
|
|
||||||
|
def test_total_length_footer_text(self):
|
||||||
|
embed = Embed().set_footer(text="cool footer text")
|
||||||
|
assert embed.total_length() == 16
|
||||||
|
|
||||||
|
def test_total_length_all(self):
|
||||||
|
embed = Embed(title="my title", description="simple description")
|
||||||
|
embed.set_author(name="best author")
|
||||||
|
embed.set_footer(text="cool footer text")
|
||||||
|
assert embed.total_length() == 53
|
||||||
|
|
||||||
|
def test_comparing_embeds(self):
|
||||||
|
embed = Embed(title="my title", description="simple description")
|
||||||
|
embed.set_author(name="best author")
|
||||||
|
embed.set_footer(text="cool footer text")
|
||||||
|
embed.set_timestamp(Timestamp.parse(1649748784))
|
||||||
|
assert embed.to_dict() == dict_embed
|
Loading…
Reference in a new issue