@ -22,7 +22,10 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE .
"""
from __future__ import annotations
import datetime
from typing import Any , Dict , Final , List , Protocol , TYPE_CHECKING , Type , TypeVar , Union
from . import utils
from . colour import Colour
@ -31,32 +34,75 @@ __all__ = (
' Embed ' ,
)
class _EmptyEmbed :
def __bool__ ( self ) :
def __bool__ ( self ) - > bool :
return False
def __repr__ ( self ) :
def __repr__ ( self ) - > str :
return ' Embed.Empty '
def __len__ ( self ) :
def __len__ ( self ) - > int :
return 0
EmptyEmbed = _EmptyEmbed ( )
EmptyEmbed : Final = _EmptyEmbed ( )
class EmbedProxy :
def __init__ ( self , layer ) :
def __init__ ( self , layer : Dict [ str , Any ] ) :
self . __dict__ . update ( layer )
def __len__ ( self ) :
def __len__ ( self ) - > int :
return len ( self . __dict__ )
def __repr__ ( self ) :
def __repr__ ( self ) - > str :
inner = ' , ' . join ( ( f ' { k } = { v !r} ' for k , v in self . __dict__ . items ( ) if not k . startswith ( ' _ ' ) ) )
return f ' EmbedProxy( { inner } ) '
def __getattr__ ( self , attr ) :
def __getattr__ ( self , attr : str ) - > _EmptyEmbed :
return EmptyEmbed
E = TypeVar ( ' E ' , bound = ' Embed ' )
if TYPE_CHECKING :
from discord . types . common import Embed as EmbedData , EmbedType
T = TypeVar ( ' T ' )
MaybeEmpty = Union [ T , _EmptyEmbed ]
class _EmbedFooterProxy ( Protocol ) :
text : MaybeEmpty [ str ]
icon_url : MaybeEmpty [ str ]
class _EmbedFieldProxy ( Protocol ) :
name : MaybeEmpty [ str ]
value : MaybeEmpty [ str ]
inline : bool
class _EmbedMediaProxy ( Protocol ) :
url : MaybeEmpty [ str ]
proxy_url : MaybeEmpty [ str ]
height : MaybeEmpty [ int ]
width : MaybeEmpty [ int ]
class _EmbedVideoProxy ( Protocol ) :
url : MaybeEmpty [ str ]
height : MaybeEmpty [ int ]
width : MaybeEmpty [ int ]
class _EmbedProviderProxy ( Protocol ) :
name : MaybeEmpty [ str ]
url : MaybeEmpty [ str ]
class _EmbedAuthorProxy ( Protocol ) :
name : MaybeEmpty [ str ]
url : MaybeEmpty [ str ]
icon_url : MaybeEmpty [ str ]
proxy_icon_url : MaybeEmpty [ str ]
class Embed :
""" Represents a Discord embed.
@ -108,24 +154,41 @@ class Embed:
to denote that the value or attribute is empty .
"""
__slots__ = ( ' title ' , ' url ' , ' type ' , ' _timestamp ' , ' _colour ' , ' _footer ' ,
' _image ' , ' _thumbnail ' , ' _video ' , ' _provider ' , ' _author ' ,
' _fields ' , ' description ' )
Empty = EmptyEmbed
def __init__ ( self , * * kwargs ) :
# swap the colour/color aliases
try :
colour = kwargs [ ' colour ' ]
except KeyError :
colour = kwargs . get ( ' color ' , EmptyEmbed )
self . colour = colour
self . title = kwargs . get ( ' title ' , EmptyEmbed )
self . type = kwargs . get ( ' type ' , ' rich ' )
self . url = kwargs . get ( ' url ' , EmptyEmbed )
self . description = kwargs . get ( ' description ' , EmptyEmbed )
__slots__ = (
' title ' ,
' url ' ,
' type ' ,
' _timestamp ' ,
' _colour ' ,
' _footer ' ,
' _image ' ,
' _thumbnail ' ,
' _video ' ,
' _provider ' ,
' _author ' ,
' _fields ' ,
' description ' ,
)
Empty : Final = EmptyEmbed
def __init__ (
self ,
* ,
colour : Union [ int , Colour , _EmptyEmbed ] = EmptyEmbed ,
color : Union [ int , Colour , _EmptyEmbed ] = EmptyEmbed ,
title : MaybeEmpty [ str ] = EmptyEmbed ,
type : EmbedType = ' rich ' ,
url : MaybeEmpty [ str ] = EmptyEmbed ,
description : MaybeEmpty [ str ] = EmptyEmbed ,
timestamp : datetime . datetime = None ,
) :
self . colour = colour if colour is not EmptyEmbed else color
self . title = title
self . type = type
self . url = url
self . description = description
if self . title is not EmptyEmbed :
self . title = str ( self . title )
@ -136,17 +199,13 @@ class Embed:
if self . url is not EmptyEmbed :
self . url = str ( self . url )
try :
timestamp = kwargs [ ' timestamp ' ]
except KeyError :
pass
else :
if timestamp :
if timestamp . tzinfo is None :
timestamp = timestamp . astimezone ( )
self . timestamp = timestamp
@classmethod
def from_dict ( cls , data ) :
def from_dict ( cls : Type [ E ] , data : EmbedData ) - > E :
""" Converts a :class:`dict` to a :class:`Embed` provided it is in the
format that Discord expects it to be in .
@ -162,7 +221,7 @@ class Embed:
The dictionary to convert into an embed .
"""
# we are bypassing __init__ here since it doesn't apply here
self = cls . __new__ ( cls )
self : E = cls . __new__ ( cls )
# fill in the basic fields
@ -202,11 +261,11 @@ class Embed:
return self
def copy ( self ) :
def copy ( self : E ) - > E :
""" Returns a shallow copy of the embed. """
return Embed . from_dict ( self . to_dict ( ) )
return self . __class__ . from_dict ( self . to_dict ( ) )
def __len__ ( self ) :
def __len__ ( self ) - > int :
total = len ( self . title ) + len ( self . description )
for field in getattr ( self , ' _fields ' , [ ] ) :
total + = len ( field [ ' name ' ] ) + len ( field [ ' value ' ] )
@ -227,28 +286,30 @@ class Embed:
return total
def __bool__ ( self ) :
return any ( (
self . title ,
self . url ,
self . description ,
self . colour ,
self . fields ,
self . timestamp ,
self . author ,
self . thumbnail ,
self . footer ,
self . image ,
self . provider ,
self . video ,
) )
def __bool__ ( self ) - > bool :
return any (
(
self . title ,
self . url ,
self . description ,
self . colour ,
self . fields ,
self . timestamp ,
self . author ,
self . thumbnail ,
self . footer ,
self . image ,
self . provider ,
self . video ,
)
)
@property
def colour ( self ) :
def colour ( self ) - > MaybeEmpty [ Colour ] :
return getattr ( self , ' _colour ' , EmptyEmbed )
@colour . setter
def colour ( self , value ) :
def colour ( self , value : Union [ int , Colour , _EmptyEmbed ] ) : # type: ignore
if isinstance ( value , ( Colour , _EmptyEmbed ) ) :
self . _colour = value
elif isinstance ( value , int ) :
@ -259,27 +320,27 @@ class Embed:
color = colour
@property
def timestamp ( self ) :
def timestamp ( self ) - > MaybeEmpty [ datetime . datetime ] :
return getattr ( self , ' _timestamp ' , EmptyEmbed )
@timestamp . setter
def timestamp ( self , value ) :
def timestamp ( self , value : MaybeEmpty [ datetime . datetime ] ) :
if isinstance ( value , ( datetime . datetime , _EmptyEmbed ) ) :
self . _timestamp = value
else :
raise TypeError ( f " Expected datetime.datetime or Embed.Empty received { value . __class__ . __name__ } instead " )
@property
def footer ( self ) :
""" Union[:class:`EmbedProxy`, :attr:`Empty`]: Returns an ``EmbedProxy`` denoting the footer contents.
def footer ( self ) - > _EmbedFooterProxy :
""" Returns an ``EmbedProxy`` denoting the footer contents.
See : meth : ` set_footer ` for possible values you can access .
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return EmbedProxy ( getattr ( self , ' _footer ' , { } ) )
return EmbedProxy ( getattr ( self , ' _footer ' , { } ) ) # type: ignore
def set_footer ( self , * , text = EmptyEmbed , icon_url = EmptyEmbed ) :
def set_footer ( self : E , * , text : MaybeEmpty [ str ] = EmptyEmbed , icon_url : MaybeEmpty [ str ] = EmptyEmbed ) - > E :
""" Sets the footer for the embed content.
This function returns the class instance to allow for fluent - style
@ -303,8 +364,8 @@ class Embed:
return self
@property
def image ( self ) :
""" Union[:class:`EmbedProxy`, :attr:`Empty`]: Returns an ``EmbedProxy`` denoting the image contents.
def image ( self ) - > _EmbedMediaProxy :
""" Returns an ``EmbedProxy`` denoting the image contents.
Possible attributes you can access are :
@ -315,9 +376,9 @@ class Embed:
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return EmbedProxy ( getattr ( self , ' _image ' , { } ) )
return EmbedProxy ( getattr ( self , ' _image ' , { } ) ) # type: ignore
def set_image ( self , * , url ) :
def set_image ( self : E , * , url : MaybeEmpty [ str ] ) - > E :
""" Sets the image for the embed content.
This function returns the class instance to allow for fluent - style
@ -339,14 +400,14 @@ class Embed:
pass
else :
self . _image = {
' url ' : str ( url )
' url ' : str ( url ) ,
}
return self
@property
def thumbnail ( self ) :
""" Union[:class:`EmbedProxy`, :attr:`Empty`]: Returns an ``EmbedProxy`` denoting the thumbnail contents.
def thumbnail ( self ) - > _EmbedMediaProxy :
""" Returns an ``EmbedProxy`` denoting the thumbnail contents.
Possible attributes you can access are :
@ -357,9 +418,9 @@ class Embed:
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return EmbedProxy ( getattr ( self , ' _thumbnail ' , { } ) )
return EmbedProxy ( getattr ( self , ' _thumbnail ' , { } ) ) # type: ignore
def set_thumbnail ( self , * , url ) :
def set_thumbnail ( self : E , * , url : MaybeEmpty [ str ] ) - > E :
""" Sets the thumbnail for the embed content.
This function returns the class instance to allow for fluent - style
@ -381,14 +442,14 @@ class Embed:
pass
else :
self . _thumbnail = {
' url ' : str ( url )
' url ' : str ( url ) ,
}
return self
@property
def video ( self ) :
""" Union[:class:`EmbedProxy`, :attr:`Empty`]: Returns an ``EmbedProxy`` denoting the video contents.
def video ( self ) - > _EmbedVideoProxy :
""" Returns an ``EmbedProxy`` denoting the video contents.
Possible attributes include :
@ -398,29 +459,29 @@ class Embed:
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return EmbedProxy ( getattr ( self , ' _video ' , { } ) )
return EmbedProxy ( getattr ( self , ' _video ' , { } ) ) # type: ignore
@property
def provider ( self ) :
""" Union[:class:`EmbedProxy`, :attr:`Empty`]: Returns an ``EmbedProxy`` denoting the provider contents.
def provider ( self ) - > _EmbedProviderProxy :
""" Returns an ``EmbedProxy`` denoting the provider contents.
The only attributes that might be accessed are ` ` name ` ` and ` ` url ` ` .
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return EmbedProxy ( getattr ( self , ' _provider ' , { } ) )
return EmbedProxy ( getattr ( self , ' _provider ' , { } ) ) # type: ignore
@property
def author ( self ) :
""" Union[:class:`EmbedProxy`, :attr:`Empty`]: Returns an ``EmbedProxy`` denoting the author contents.
def author ( self ) - > _EmbedAuthorProxy :
""" Returns an ``EmbedProxy`` denoting the author contents.
See : meth : ` set_author ` for possible values you can access .
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return EmbedProxy ( getattr ( self , ' _author ' , { } ) )
return EmbedProxy ( getattr ( self , ' _author ' , { } ) ) # type: ignore
def set_author ( self , * , name , url = EmptyEmbed , icon_url = EmptyEmbed ) :
def set_author ( self : E , * , name : str , url : MaybeEmpty [ str ] = EmptyEmbed , icon_url : MaybeEmpty [ str ] = EmptyEmbed ) - > E :
""" Sets the author for the embed content.
This function returns the class instance to allow for fluent - style
@ -437,7 +498,7 @@ class Embed:
"""
self . _author = {
' name ' : str ( name )
' name ' : str ( name ) ,
}
if url is not EmptyEmbed :
@ -448,7 +509,7 @@ class Embed:
return self
def remove_author ( self ) :
def remove_author ( self : E ) - > E :
""" Clears embed ' s author information.
This function returns the class instance to allow for fluent - style
@ -464,16 +525,16 @@ class Embed:
return self
@property
def fields ( self ) :
def fields ( self ) - > List [ _EmbedFieldProxy ] :
""" Union[List[:class:`EmbedProxy`], :attr:`Empty`]: Returns a :class:`list` of ``EmbedProxy`` denoting the field contents.
See : meth : ` add_field ` for possible values you can access .
If the attribute has no value then : attr : ` Empty ` is returned .
"""
return [ EmbedProxy ( d ) for d in getattr ( self , ' _fields ' , [ ] ) ]
return [ EmbedProxy ( d ) for d in getattr ( self , ' _fields ' , [ ] ) ] # type: ignore
def add_field ( self , * , name , value , inline = True ) :
def add_field ( self : E , * , name : str , value : str , inline : bool = True ) - > E :
""" Adds a field to the embed object.
This function returns the class instance to allow for fluent - style
@ -492,7 +553,7 @@ class Embed:
field = {
' inline ' : inline ,
' name ' : str ( name ) ,
' value ' : str ( value )
' value ' : str ( value ) ,
}
try :
@ -502,7 +563,7 @@ class Embed:
return self
def insert_field_at ( self , index , * , name , value , inline = True ) :
def insert_field_at ( self : E , index : int , * , name : str , value : str , inline : bool = True ) - > E :
""" Inserts a field before a specified index to the embed.
This function returns the class instance to allow for fluent - style
@ -525,7 +586,7 @@ class Embed:
field = {
' inline ' : inline ,
' name ' : str ( name ) ,
' value ' : str ( value )
' value ' : str ( value ) ,
}
try :
@ -535,14 +596,14 @@ class Embed:
return self
def clear_fields ( self ) :
def clear_fields ( self ) - > None :
""" Removes all fields from this embed. """
try :
self . _fields . clear ( )
except AttributeError :
self . _fields = [ ]
def remove_field ( self , index ) :
def remove_field ( self , index : int ) - > None :
""" Removes a field at a specified index.
If the index is invalid or out of bounds then the error is
@ -563,7 +624,7 @@ class Embed:
except ( AttributeError , IndexError ) :
pass
def set_field_at ( self , index , * , name , value , inline = True ) :
def set_field_at ( self : E , index : int , * , name : str , value : str , inline : bool = True ) - > E :
""" Modifies a field to the embed object.
The index must point to a valid pre - existing field .
@ -598,15 +659,17 @@ class Embed:
field [ ' inline ' ] = inline
return self
def to_dict ( self ) :
def to_dict ( self ) - > EmbedData :
""" Converts this embed object into a dict. """
# add in the raw data into the dict
# fmt: off
result = {
key [ 1 : ] : getattr ( self , key )
for key in self . __slots__
if key [ 0 ] == ' _ ' and hasattr ( self , key )
}
# fmt: on
# deal with basic convenience wrappers
@ -642,4 +705,4 @@ class Embed:
if self . title :
result [ ' title ' ] = self . title
return result
return result # type: ignore