mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-22 08:44:10 +00:00
Make discord.Embed builder more strict and easier to use.
Allow for easier use when trying to "reuse" the same discord.Embed object by providing new methods such as Embed.clear_fields, Embed.set_field_at, and allowing you to set things to Embed.Empty to clear out an attribute. For ease of use, things are automatically casted to ``str`` to prevent the user from having HTTP 400 errors if they forgot to do so. The new embed builder also supports "fluent-style" interface to allow you to chain methods in a single line if necessary. Certain parameters were removed since they were ignored by Discord anyway such as `width` and `height` in Embed.set_image and Embed.set_thumbnail.
This commit is contained in:
parent
fb1f9ac659
commit
c4ee4c1db4
@ -60,6 +60,9 @@ class Embed:
|
||||
is invalid or empty, then a special sentinel value is returned,
|
||||
:attr:`Embed.Empty`.
|
||||
|
||||
For ease of use, all parameters that expect a ``str`` are implicitly
|
||||
casted to ``str`` for you.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
title: str
|
||||
@ -75,8 +78,8 @@ class Embed:
|
||||
colour: :class:`Colour` or int
|
||||
The colour code of the embed. Aliased to ``color`` as well.
|
||||
Empty
|
||||
A special sentinel value used by ``EmbedProxy`` to denote
|
||||
that the value or attribute is empty.
|
||||
A special sentinel value used by ``EmbedProxy`` and this class
|
||||
to denote that the value or attribute is empty.
|
||||
"""
|
||||
|
||||
__slots__ = ('title', 'url', 'type', '_timestamp', '_colour', '_footer',
|
||||
@ -90,15 +93,13 @@ class Embed:
|
||||
try:
|
||||
colour = kwargs['colour']
|
||||
except KeyError:
|
||||
colour = kwargs.get('color')
|
||||
colour = kwargs.get('color', EmptyEmbed)
|
||||
|
||||
if colour is not None:
|
||||
self.colour = colour
|
||||
|
||||
self.title = kwargs.get('title')
|
||||
self.colour = colour
|
||||
self.title = kwargs.get('title', EmptyEmbed)
|
||||
self.type = kwargs.get('type', 'rich')
|
||||
self.url = kwargs.get('url')
|
||||
self.description = kwargs.get('description')
|
||||
self.url = kwargs.get('url', EmptyEmbed)
|
||||
self.description = kwargs.get('description', EmptyEmbed)
|
||||
|
||||
try:
|
||||
timestamp = kwargs['timestamp']
|
||||
@ -114,10 +115,10 @@ class Embed:
|
||||
|
||||
# fill in the basic fields
|
||||
|
||||
self.title = data.get('title')
|
||||
self.type = data.get('type')
|
||||
self.description = data.get('description')
|
||||
self.url = data.get('url')
|
||||
self.title = data.get('title', EmptyEmbed)
|
||||
self.type = data.get('type', EmptyEmbed)
|
||||
self.description = data.get('description', EmptyEmbed)
|
||||
self.url = data.get('url', EmptyEmbed)
|
||||
|
||||
# try to fill in the more rich fields
|
||||
|
||||
@ -143,29 +144,29 @@ class Embed:
|
||||
|
||||
@property
|
||||
def colour(self):
|
||||
return getattr(self, '_colour', None)
|
||||
return getattr(self, '_colour', EmptyEmbed)
|
||||
|
||||
@colour.setter
|
||||
def colour(self, value):
|
||||
if isinstance(value, Colour):
|
||||
if isinstance(value, (Colour, _EmptyEmbed)):
|
||||
self._colour = value
|
||||
elif isinstance(value, int):
|
||||
self._colour = Colour(value=value)
|
||||
else:
|
||||
raise TypeError('Expected discord.Colour or int, received %s instead.' % value.__class__.__name__)
|
||||
raise TypeError('Expected discord.Colour, int, or Embed.Empty but received %s instead.' % value.__class__.__name__)
|
||||
|
||||
color = colour
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
return getattr(self, '_timestamp', None)
|
||||
return getattr(self, '_timestamp', EmptyEmbed)
|
||||
|
||||
@timestamp.setter
|
||||
def timestamp(self, value):
|
||||
if isinstance(value, datetime.datetime):
|
||||
if isinstance(value, (datetime.datetime, _EmptyEmbed)):
|
||||
self._timestamp = value
|
||||
else:
|
||||
raise TypeError("Expected datetime.datetime received %s instead" % value.__class__.__name__)
|
||||
raise TypeError("Expected datetime.datetime or Embed.Empty received %s instead" % value.__class__.__name__)
|
||||
|
||||
@property
|
||||
def footer(self):
|
||||
@ -173,13 +174,16 @@ class Embed:
|
||||
|
||||
See :meth:`set_footer` for possible values you can access.
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_footer', {}))
|
||||
|
||||
def set_footer(self, *, text=None, icon_url=None):
|
||||
def set_footer(self, *, text=EmptyEmbed, icon_url=EmptyEmbed):
|
||||
"""Sets the footer for the embed content.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
chaining.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
text: str
|
||||
@ -189,77 +193,79 @@ class Embed:
|
||||
"""
|
||||
|
||||
self._footer = {}
|
||||
if text is not None:
|
||||
self._footer['text'] = text
|
||||
if text is not EmptyEmbed:
|
||||
self._footer['text'] = str(text)
|
||||
|
||||
if icon_url is not None:
|
||||
self._footer['icon_url'] = icon_url
|
||||
if icon_url is not EmptyEmbed:
|
||||
self._footer['icon_url'] = str(icon_url)
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
"""Returns a ``EmbedProxy`` denoting the image contents.
|
||||
|
||||
See :meth:`set_image` for possible values you can access.
|
||||
Possible attributes you can access are:
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
- ``url``
|
||||
- ``proxy_url``
|
||||
- ``width``
|
||||
- ``height``
|
||||
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_image', {}))
|
||||
|
||||
def set_image(self, *, url, height=None, width=None):
|
||||
def set_image(self, *, url):
|
||||
"""Sets the image for the embed content.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
chaining.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
url: str
|
||||
The source URL for the image. Only HTTP(S) is supported.
|
||||
height: int
|
||||
The height of the image.
|
||||
width: int
|
||||
The width of the image.
|
||||
"""
|
||||
|
||||
self._image = {
|
||||
'url': url
|
||||
'url': str(url)
|
||||
}
|
||||
|
||||
if height is not None:
|
||||
self._image['height'] = height
|
||||
|
||||
if width is not None:
|
||||
self._image['width'] = width
|
||||
return self
|
||||
|
||||
@property
|
||||
def thumbnail(self):
|
||||
"""Returns a ``EmbedProxy`` denoting the thumbnail contents.
|
||||
|
||||
See :meth:`set_thumbnail` for possible values you can access.
|
||||
Possible attributes you can access are:
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
- ``url``
|
||||
- ``proxy_url``
|
||||
- ``width``
|
||||
- ``height``
|
||||
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_thumbnail', {}))
|
||||
|
||||
def set_thumbnail(self, *, url, height=None, width=None):
|
||||
def set_thumbnail(self, *, url):
|
||||
"""Sets the thumbnail for the embed content.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
chaining.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
url: str
|
||||
The source URL for the thumbnail. Only HTTP(S) is supported.
|
||||
height: int
|
||||
The height of the thumbnail.
|
||||
width: int
|
||||
The width of the thumbnail.
|
||||
"""
|
||||
|
||||
self._thumbnail = {
|
||||
'url': url
|
||||
'url': str(url)
|
||||
}
|
||||
|
||||
if height is not None:
|
||||
self._thumbnail['height'] = height
|
||||
|
||||
if width is not None:
|
||||
self._thumbnail['width'] = width
|
||||
return self
|
||||
|
||||
@property
|
||||
def video(self):
|
||||
@ -271,7 +277,7 @@ class Embed:
|
||||
- ``height`` for the video height.
|
||||
- ``width`` for the video width.
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_video', {}))
|
||||
|
||||
@ -281,7 +287,7 @@ class Embed:
|
||||
|
||||
The only attributes that might be accessed are ``name`` and ``url``.
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_provider', {}))
|
||||
|
||||
@ -291,13 +297,16 @@ class Embed:
|
||||
|
||||
See :meth:`set_author` for possible values you can access.
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_author', {}))
|
||||
|
||||
def set_author(self, *, name, url=None, icon_url=None):
|
||||
def set_author(self, *, name, url=EmptyEmbed, icon_url=EmptyEmbed):
|
||||
"""Sets the author for the embed content.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
chaining.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
name: str
|
||||
@ -309,15 +318,16 @@ class Embed:
|
||||
"""
|
||||
|
||||
self._author = {
|
||||
'name': name
|
||||
'name': str(name)
|
||||
}
|
||||
|
||||
if url is not None:
|
||||
self._author['url'] = url
|
||||
if url is not EmptyEmbed:
|
||||
self._author['url'] = str(url)
|
||||
|
||||
if icon_url is not None:
|
||||
self._author['icon_url'] = icon_url
|
||||
if icon_url is not EmptyEmbed:
|
||||
self._author['icon_url'] = str(icon_url)
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
@ -325,13 +335,16 @@ class Embed:
|
||||
|
||||
See :meth:`add_field` for possible values you can access.
|
||||
|
||||
If the attribute cannot be accessed then ``None`` is returned.
|
||||
If the attribute has no value then :attr:`Empty` is returned.
|
||||
"""
|
||||
return [EmbedProxy(d) for d in getattr(self, '_fields', [])]
|
||||
|
||||
def add_field(self, *, name=None, value=None, inline=True):
|
||||
def add_field(self, *, name, value, inline=True):
|
||||
"""Adds a field to the embed object.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
chaining.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
name: str
|
||||
@ -343,19 +356,81 @@ class Embed:
|
||||
"""
|
||||
|
||||
field = {
|
||||
'inline': inline
|
||||
'inline': inline,
|
||||
'name': str(name),
|
||||
'value': str(value)
|
||||
}
|
||||
if name is not None:
|
||||
field['name'] = name
|
||||
|
||||
if value is not None:
|
||||
field['value'] = value
|
||||
|
||||
try:
|
||||
self._fields.append(field)
|
||||
except AttributeError:
|
||||
self._fields = [field]
|
||||
|
||||
return self
|
||||
|
||||
def clear_fields(self):
|
||||
"""Removes all fields from this embed."""
|
||||
try:
|
||||
self._fields.clear()
|
||||
except AttributeError:
|
||||
self._fields = []
|
||||
|
||||
def remove_field(self, index):
|
||||
"""Removes a field at a specified index.
|
||||
|
||||
If the index is invalid or out of bounds then the error is
|
||||
silently swallowed.
|
||||
|
||||
.. note::
|
||||
|
||||
When deleting a field by index, the index of the other fields
|
||||
shift to fill the gap just like a regular list.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
index: int
|
||||
The index of the field to remove.
|
||||
"""
|
||||
try:
|
||||
del self._fields[index]
|
||||
except (AttributeError, IndexError):
|
||||
pass
|
||||
|
||||
def set_field_at(self, index, *, name, value, inline=True):
|
||||
"""Modifies a field to the embed object.
|
||||
|
||||
The index must point to a valid pre-existing field.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
chaining.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
index: int
|
||||
The index of the field to modify.
|
||||
name: str
|
||||
The name of the field.
|
||||
value: str
|
||||
The value of the field.
|
||||
inline: bool
|
||||
Whether the field should be displayed inline.
|
||||
|
||||
Raises
|
||||
-------
|
||||
IndexError
|
||||
An invalid index was provided.
|
||||
"""
|
||||
|
||||
try:
|
||||
field = self._fields[index]
|
||||
except (TypeError, IndexError, AttributeError):
|
||||
raise IndexError('field index out of range')
|
||||
|
||||
field['name'] = str(name)
|
||||
field['value'] = str(value)
|
||||
field['inline'] = inline
|
||||
return self
|
||||
|
||||
def to_dict(self):
|
||||
"""Converts this embed object into a dict."""
|
||||
|
||||
@ -373,14 +448,16 @@ class Embed:
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
result['color'] = colour.value
|
||||
if colour:
|
||||
result['color'] = colour.value
|
||||
|
||||
try:
|
||||
timestamp = result.pop('timestamp')
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
result['timestamp'] = timestamp.isoformat()
|
||||
if timestamp:
|
||||
result['timestamp'] = timestamp.isoformat()
|
||||
|
||||
# add in the non raw attribute ones
|
||||
if self.type:
|
||||
|
Loading…
x
Reference in New Issue
Block a user