Browse Source

Typehint Activity

pull/7122/head
Rapptz 4 years ago
parent
commit
cd6b453cb3
  1. 198
      discord/activity.py

198
discord/activity.py

@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
import datetime
from typing import List, TYPE_CHECKING
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union
from .asset import Asset
from .enums import ActivityType, try_enum
@ -121,10 +121,10 @@ class BaseActivity:
__slots__ = ('_created_at',)
def __init__(self, **kwargs):
self._created_at = kwargs.pop('created_at', None)
self._created_at: Optional[float] = kwargs.pop('created_at', None)
@property
def created_at(self):
def created_at(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user started doing this activity in UTC.
.. versionadded:: 1.3
@ -147,17 +147,17 @@ class Activity(BaseActivity):
Attributes
------------
application_id: :class:`int`
application_id: Optional[:class:`int`]
The application ID of the game.
name: :class:`str`
name: Optional[:class:`str`]
The name of the activity.
url: :class:`str`
url: Optional[:class:`str`]
A stream URL that the activity could be doing.
type: :class:`ActivityType`
The type of activity currently being done.
state: :class:`str`
state: Optional[:class:`str`]
The user's current state. For example, "In Game".
details: :class:`str`
details: Optional[:class:`str`]
The detail of the user's current activity.
timestamps: :class:`dict`
A dictionary of timestamps. It contains the following optional keys:
@ -214,29 +214,28 @@ class Activity(BaseActivity):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.state = kwargs.pop('state', None)
self.details = kwargs.pop('details', None)
self.state: Optional[str] = kwargs.pop('state', None)
self.details: Optional[str] = kwargs.pop('details', None)
self.timestamps: ActivityTimestamps = kwargs.pop('timestamps', {})
self.assets: ActivityAssets = kwargs.pop('assets', {})
self.party: ActivityParty = kwargs.pop('party', {})
self.application_id = _get_as_snowflake(kwargs, 'application_id')
self.name = kwargs.pop('name', None)
self.url = kwargs.pop('url', None)
self.flags = kwargs.pop('flags', 0)
self.sync_id = kwargs.pop('sync_id', None)
self.session_id = kwargs.pop('session_id', None)
self.application_id: Optional[int] = _get_as_snowflake(kwargs, 'application_id')
self.name: Optional[str] = kwargs.pop('name', None)
self.url: Optional[str] = kwargs.pop('url', None)
self.flags: int = kwargs.pop('flags', 0)
self.sync_id: Optional[str] = kwargs.pop('sync_id', None)
self.session_id: Optional[str] = kwargs.pop('session_id', None)
self.buttons: List[ActivityButton] = kwargs.pop('buttons', [])
activity_type = kwargs.pop('type', -1)
self.type = activity_type if isinstance(activity_type, ActivityType) else try_enum(ActivityType, activity_type)
self.type: ActivityType = (
activity_type if isinstance(activity_type, ActivityType) else try_enum(ActivityType, activity_type)
)
emoji = kwargs.pop('emoji', None)
if emoji is not None:
self.emoji = PartialEmoji.from_dict(emoji)
else:
self.emoji = None
self.emoji: Optional[PartialEmoji] = PartialEmoji.from_dict(emoji) if emoji is not None else None
def __repr__(self):
def __repr__(self) -> str:
attrs = (
('type', self.type),
('name', self.name),
@ -249,8 +248,8 @@ class Activity(BaseActivity):
inner = ' '.join('%s=%r' % t for t in attrs)
return f'<Activity {inner}>'
def to_dict(self):
ret = {}
def to_dict(self) -> Dict[str, Any]:
ret: Dict[str, Any] = {}
for attr in self.__slots__:
value = getattr(self, attr, None)
if value is None:
@ -266,7 +265,7 @@ class Activity(BaseActivity):
return ret
@property
def start(self):
def start(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user started doing this activity in UTC, if applicable."""
try:
timestamp = self.timestamps['start'] / 1000
@ -276,7 +275,7 @@ class Activity(BaseActivity):
return datetime.datetime.utcfromtimestamp(timestamp).replace(tzinfo=datetime.timezone.utc)
@property
def end(self):
def end(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user will stop doing this activity in UTC, if applicable."""
try:
timestamp = self.timestamps['end'] / 1000
@ -286,7 +285,7 @@ class Activity(BaseActivity):
return datetime.datetime.utcfromtimestamp(timestamp).replace(tzinfo=datetime.timezone.utc)
@property
def large_image_url(self):
def large_image_url(self) -> Optional[str]:
"""Optional[:class:`str`]: Returns a URL pointing to the large image asset of this activity if applicable."""
if self.application_id is None:
return None
@ -299,7 +298,7 @@ class Activity(BaseActivity):
return Asset.BASE + f'/app-assets/{self.application_id}/{large_image}.png'
@property
def small_image_url(self):
def small_image_url(self) -> Optional[str]:
"""Optional[:class:`str`]: Returns a URL pointing to the small image asset of this activity if applicable."""
if self.application_id is None:
return None
@ -312,12 +311,12 @@ class Activity(BaseActivity):
return Asset.BASE + f'/app-assets/{self.application_id}/{small_image}.png'
@property
def large_image_text(self):
def large_image_text(self) -> Optional[str]:
"""Optional[:class:`str`]: Returns the large image asset hover text of this activity if applicable."""
return self.assets.get('large_text', None)
@property
def small_image_text(self):
def small_image_text(self) -> Optional[str]:
"""Optional[:class:`str`]: Returns the small image asset hover text of this activity if applicable."""
return self.assets.get('small_text', None)
@ -358,9 +357,9 @@ class Game(BaseActivity):
__slots__ = ('name', '_end', '_start')
def __init__(self, name, **extra):
def __init__(self, name: str, **extra):
super().__init__(**extra)
self.name = name
self.name: str = name
try:
timestamps: ActivityTimestamps = extra['timestamps']
@ -372,7 +371,7 @@ class Game(BaseActivity):
self._end = timestamps.get('end', 0)
@property
def type(self):
def type(self) -> ActivityType:
""":class:`ActivityType`: Returns the game's type. This is for compatibility with :class:`Activity`.
It always returns :attr:`ActivityType.playing`.
@ -380,27 +379,27 @@ class Game(BaseActivity):
return ActivityType.playing
@property
def start(self):
def start(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user started playing this game in UTC, if applicable."""
if self._start:
return datetime.datetime.utcfromtimestamp(self._start / 1000).replace(tzinfo=datetime.timezone.utc)
return None
@property
def end(self):
def end(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user will stop playing this game in UTC, if applicable."""
if self._end:
return datetime.datetime.utcfromtimestamp(self._end / 1000).replace(tzinfo=datetime.timezone.utc)
return None
def __str__(self):
def __str__(self) -> str:
return str(self.name)
def __repr__(self):
def __repr__(self) -> str:
return f'<Game name={self.name!r}>'
def to_dict(self):
timestamps = {}
def to_dict(self) -> Dict[str, Any]:
timestamps: Dict[str, Any] = {}
if self._start:
timestamps['start'] = self._start
@ -415,13 +414,13 @@ class Game(BaseActivity):
}
# fmt: on
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
return isinstance(other, Game) and other.name == self.name
def __ne__(self, other):
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self):
def __hash__(self) -> int:
return hash(self.name)
@ -450,7 +449,7 @@ class Streaming(BaseActivity):
Attributes
-----------
platform: :class:`str`
platform: Optional[:class:`str`]
Where the user is streaming from (ie. YouTube, Twitch).
.. versionadded:: 1.3
@ -472,27 +471,27 @@ class Streaming(BaseActivity):
__slots__ = ('platform', 'name', 'game', 'url', 'details', 'assets')
def __init__(self, *, name, url, **extra):
def __init__(self, *, name: Optional[str], url: str, **extra: Any):
super().__init__(**extra)
self.platform = name
self.name = extra.pop('details', name)
self.game = extra.pop('state', None)
self.url = url
self.details = extra.pop('details', self.name) # compatibility
self.platform: Optional[str] = name
self.name: Optional[str] = extra.pop('details', name)
self.game: Optional[str] = extra.pop('state', None)
self.url: str = url
self.details: Optional[str] = extra.pop('details', self.name) # compatibility
self.assets: ActivityAssets = extra.pop('assets', {})
@property
def type(self):
def type(self) -> ActivityType:
""":class:`ActivityType`: Returns the game's type. This is for compatibility with :class:`Activity`.
It always returns :attr:`ActivityType.streaming`.
"""
return ActivityType.streaming
def __str__(self):
def __str__(self) -> str:
return str(self.name)
def __repr__(self):
def __repr__(self) -> str:
return f'<Streaming name={self.name!r}>'
@property
@ -510,9 +509,9 @@ class Streaming(BaseActivity):
else:
return name[7:] if name[:7] == 'twitch:' else None
def to_dict(self):
def to_dict(self) -> Dict[str, Any]:
# fmt: off
ret = {
ret: Dict[str, Any] = {
'type': ActivityType.streaming.value,
'name': str(self.name),
'url': str(self.url),
@ -523,13 +522,13 @@ class Streaming(BaseActivity):
ret['details'] = self.details
return ret
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
return isinstance(other, Streaming) and other.name == self.name and other.url == self.url
def __ne__(self, other):
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self):
def __hash__(self) -> int:
return hash(self.name)
@ -559,17 +558,17 @@ class Spotify:
__slots__ = ('_state', '_details', '_timestamps', '_assets', '_party', '_sync_id', '_session_id', '_created_at')
def __init__(self, **data):
self._state = data.pop('state', None)
self._details = data.pop('details', None)
self._timestamps = data.pop('timestamps', {})
self._assets = data.pop('assets', {})
self._party = data.pop('party', {})
self._sync_id = data.pop('sync_id')
self._session_id = data.pop('session_id')
self._created_at = data.pop('created_at', None)
self._state: str = data.pop('state', '')
self._details: str = data.pop('details', '')
self._timestamps: Dict[str, int] = data.pop('timestamps', {})
self._assets: ActivityAssets = data.pop('assets', {})
self._party: ActivityParty = data.pop('party', {})
self._sync_id: str = data.pop('sync_id')
self._session_id: str = data.pop('session_id')
self._created_at: Optional[float] = data.pop('created_at', None)
@property
def type(self):
def type(self) -> ActivityType:
""":class:`ActivityType`: Returns the activity's type. This is for compatibility with :class:`Activity`.
It always returns :attr:`ActivityType.listening`.
@ -577,7 +576,7 @@ class Spotify:
return ActivityType.listening
@property
def created_at(self):
def created_at(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user started listening in UTC.
.. versionadded:: 1.3
@ -586,20 +585,20 @@ class Spotify:
return datetime.datetime.utcfromtimestamp(self._created_at / 1000).replace(tzinfo=datetime.timezone.utc)
@property
def colour(self):
def colour(self) -> Colour:
""":class:`Colour`: Returns the Spotify integration colour, as a :class:`Colour`.
There is an alias for this named :attr:`color`"""
return Colour(0x1DB954)
@property
def color(self):
def color(self) -> Colour:
""":class:`Colour`: Returns the Spotify integration colour, as a :class:`Colour`.
There is an alias for this named :attr:`colour`"""
return self.colour
def to_dict(self):
def to_dict(self) -> Dict[str, Any]:
return {
'flags': 48, # SYNC | PLAY
'name': 'Spotify',
@ -613,11 +612,11 @@ class Spotify:
}
@property
def name(self):
def name(self) -> str:
""":class:`str`: The activity's name. This will always return "Spotify"."""
return 'Spotify'
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
return (
isinstance(other, Spotify)
and other._session_id == self._session_id
@ -625,30 +624,30 @@ class Spotify:
and other.start == self.start
)
def __ne__(self, other):
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self):
def __hash__(self) -> int:
return hash(self._session_id)
def __str__(self):
def __str__(self) -> str:
return 'Spotify'
def __repr__(self):
def __repr__(self) -> str:
return f'<Spotify title={self.title!r} artist={self.artist!r} track_id={self.track_id!r}>'
@property
def title(self):
def title(self) -> str:
""":class:`str`: The title of the song being played."""
return self._details
@property
def artists(self):
def artists(self) -> List[str]:
"""List[:class:`str`]: The artists of the song being played."""
return self._state.split('; ')
@property
def artist(self):
def artist(self) -> str:
""":class:`str`: The artist of the song being played.
This does not attempt to split the artist information into
@ -657,12 +656,12 @@ class Spotify:
return self._state
@property
def album(self):
def album(self) -> str:
""":class:`str`: The album that the song being played belongs to."""
return self._assets.get('large_text', '')
@property
def album_cover_url(self):
def album_cover_url(self) -> str:
""":class:`str`: The album cover image URL from Spotify's CDN."""
large_image = self._assets.get('large_image', '')
if large_image[:8] != 'spotify:':
@ -671,35 +670,35 @@ class Spotify:
return 'https://i.scdn.co/image/' + album_image_id
@property
def track_id(self):
def track_id(self) -> str:
""":class:`str`: The track ID used by Spotify to identify this song."""
return self._sync_id
@property
def track_url(self) -> str:
""":class:`str`: The track URL to listen on Spotify.
.. versionadded:: 2.0
"""
return f'https://open.spotify.com/track/{self.track_id}'
@property
def start(self):
def start(self) -> datetime.datetime:
""":class:`datetime.datetime`: When the user started playing this song in UTC."""
return datetime.datetime.utcfromtimestamp(self._timestamps['start'] / 1000).replace(tzinfo=datetime.timezone.utc)
@property
def end(self):
def end(self) -> datetime.datetime:
""":class:`datetime.datetime`: When the user will stop playing this song in UTC."""
return datetime.datetime.utcfromtimestamp(self._timestamps['end'] / 1000).replace(tzinfo=datetime.timezone.utc)
@property
def duration(self):
def duration(self) -> datetime.timedelta:
""":class:`datetime.timedelta`: The duration of the song being played."""
return self.end - self.start
@property
def party_id(self):
def party_id(self) -> str:
""":class:`str`: The party ID of the listening party."""
return self._party.get('id', '')
@ -737,13 +736,14 @@ class CustomActivity(BaseActivity):
__slots__ = ('name', 'emoji', 'state')
def __init__(self, name, *, emoji=None, **extra):
def __init__(self, name: Optional[str], *, emoji: Optional[PartialEmoji] = None, **extra: Any):
super().__init__(**extra)
self.name = name
self.state = extra.pop('state', None)
self.name: Optional[str] = name
self.state: Optional[str] = extra.pop('state', None)
if self.name == 'Custom Status':
self.name = self.state
self.emoji: Optional[PartialEmoji]
if emoji is None:
self.emoji = emoji
elif isinstance(emoji, dict):
@ -756,14 +756,14 @@ class CustomActivity(BaseActivity):
raise TypeError(f'Expected str, PartialEmoji, or None, received {type(emoji)!r} instead.')
@property
def type(self):
def type(self) -> ActivityType:
""":class:`ActivityType`: Returns the activity's type. This is for compatibility with :class:`Activity`.
It always returns :attr:`ActivityType.custom`.
"""
return ActivityType.custom
def to_dict(self):
def to_dict(self) -> Dict[str, Any]:
if self.name == self.state:
o = {
'type': ActivityType.custom.value,
@ -780,16 +780,16 @@ class CustomActivity(BaseActivity):
o['emoji'] = self.emoji.to_dict()
return o
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
return isinstance(other, CustomActivity) and other.name == self.name and other.emoji == self.emoji
def __ne__(self, other):
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self):
def __hash__(self) -> int:
return hash((self.name, str(self.emoji)))
def __str__(self):
def __str__(self) -> str:
if self.emoji:
if self.name:
return f'{self.emoji} {self.name}'
@ -797,11 +797,11 @@ class CustomActivity(BaseActivity):
else:
return str(self.name)
def __repr__(self):
def __repr__(self) -> str:
return f'<CustomActivity name={self.name!r} emoji={self.emoji!r}>'
def create_activity(data):
def create_activity(data: Optional[Dict[str, Any]]) -> Optional[Union[Activity, Game, CustomActivity, Streaming, Spotify]]:
if not data:
return None

Loading…
Cancel
Save