aboutsummaryrefslogtreecommitdiff
path: root/discord/embeds.py
diff options
context:
space:
mode:
authorRapptz <[email protected]>2016-11-22 19:30:21 -0500
committerRapptz <[email protected]>2016-11-22 19:30:21 -0500
commitc4ee4c1db4d7997f4b847bb4dd4ad3388db74242 (patch)
treed9f616684bdb431c9474738d8fe0dbe0b029d1b3 /discord/embeds.py
parentAdd PermissionOverwrite.is_empty to query empty state of an overwrite. (diff)
downloaddiscord.py-c4ee4c1db4d7997f4b847bb4dd4ad3388db74242.tar.xz
discord.py-c4ee4c1db4d7997f4b847bb4dd4ad3388db74242.zip
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.
Diffstat (limited to 'discord/embeds.py')
-rw-r--r--discord/embeds.py217
1 files changed, 147 insertions, 70 deletions
diff --git a/discord/embeds.py b/discord/embeds.py
index d06ef9b9..e47b814e 100644
--- a/discord/embeds.py
+++ b/discord/embeds.py
@@ -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')
-
- if colour is not None:
- self.colour = colour
+ colour = kwargs.get('color', EmptyEmbed)
- 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:
+
+ - ``url``
+ - ``proxy_url``
+ - ``width``
+ - ``height``
- 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, '_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:
+
+ - ``url``
+ - ``proxy_url``
+ - ``width``
+ - ``height``
- 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, '_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: