mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 12:18:59 +00:00
Split Game object to separate Activity subtypes for Rich Presences.
This is a massive breaking change. * All references to "game" have been renamed to "activity" * Activity objects contain a majority of the rich presence information * Game and Streaming are subtypes for memory optimisation purposes for the more common cases. * Introduce a more specialised read-only type, Spotify, for the official Spotify integration to make it easier to use.
This commit is contained in:
parent
2f562bd695
commit
f8f8f418f3
@ -19,8 +19,8 @@ __version__ = '1.0.0a'
|
|||||||
|
|
||||||
from .client import Client, AppInfo
|
from .client import Client, AppInfo
|
||||||
from .user import User, ClientUser, Profile
|
from .user import User, ClientUser, Profile
|
||||||
from .game import Game
|
|
||||||
from .emoji import Emoji, PartialEmoji
|
from .emoji import Emoji, PartialEmoji
|
||||||
|
from .activity import *
|
||||||
from .channel import *
|
from .channel import *
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .relationship import Relationship
|
from .relationship import Relationship
|
||||||
|
565
discord/activity.py
Normal file
565
discord/activity.py
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2017 Rapptz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .enums import ActivityType, try_enum
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
__all__ = ('Activity', 'Streaming', 'Game', 'Spotify')
|
||||||
|
|
||||||
|
"""If curious, this is the current schema for an activity.
|
||||||
|
|
||||||
|
It's fairly long so I will document it here:
|
||||||
|
|
||||||
|
All keys are optional.
|
||||||
|
|
||||||
|
state: str (max: 128),
|
||||||
|
details: str (max: 128)
|
||||||
|
timestamps: dict
|
||||||
|
start: int (min: 1)
|
||||||
|
end: int (min: 1)
|
||||||
|
assets: dict
|
||||||
|
large_image: str (max: 32)
|
||||||
|
large_text: str (max: 128)
|
||||||
|
small_image: str (max: 32)
|
||||||
|
small_text: str (max: 128)
|
||||||
|
party: dict
|
||||||
|
id: str (max: 128),
|
||||||
|
size: List[int] (max-length: 2)
|
||||||
|
elem: int (min: 1)
|
||||||
|
secrets: dict
|
||||||
|
match: str (max: 128)
|
||||||
|
join: str (max: 128)
|
||||||
|
spectate: str (max: 128)
|
||||||
|
instance: bool
|
||||||
|
application_id: str
|
||||||
|
name: str (max: 128)
|
||||||
|
url: str
|
||||||
|
type: int
|
||||||
|
sync_id: str
|
||||||
|
session_id: str
|
||||||
|
flags: int
|
||||||
|
|
||||||
|
There are also activity flags which are mostly uninteresting for the library atm.
|
||||||
|
|
||||||
|
t.ActivityFlags = {
|
||||||
|
INSTANCE: 1,
|
||||||
|
JOIN: 2,
|
||||||
|
SPECTATE: 4,
|
||||||
|
JOIN_REQUEST: 8,
|
||||||
|
SYNC: 16,
|
||||||
|
PLAY: 32
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
class _ActivityTag:
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
class Activity(_ActivityTag):
|
||||||
|
"""Represents an activity in Discord.
|
||||||
|
|
||||||
|
This could be an activity such as streaming, playing, listening
|
||||||
|
or watching.
|
||||||
|
|
||||||
|
For memory optimisation purposes, some activities are offered in slimmed
|
||||||
|
down versions:
|
||||||
|
|
||||||
|
- :class:`Game`
|
||||||
|
- :class:`Streaming`
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
------------
|
||||||
|
application_id: :class:`str`
|
||||||
|
The application ID of the game.
|
||||||
|
name: :class:`str`
|
||||||
|
The name of the activity.
|
||||||
|
url: :class:`str`
|
||||||
|
A stream URL that the activity could be doing.
|
||||||
|
type: :class:`ActivityType`
|
||||||
|
The type of activity currently being done.
|
||||||
|
state: :class:`str`
|
||||||
|
The user's current state. For example, "In Game".
|
||||||
|
details: :class:`str`
|
||||||
|
The detail of the user's current activity.
|
||||||
|
timestamps: :class:`dict`
|
||||||
|
A dictionary of timestamps. It contains the following optional keys:
|
||||||
|
|
||||||
|
- ``start``: Corresponds to when the user started doing the
|
||||||
|
activity in milliseconds since Unix epoch.
|
||||||
|
- ``end``: Corresponds to when the user will finish doing the
|
||||||
|
activity in milliseconds since Unix epoch.
|
||||||
|
|
||||||
|
assets: :class:`dict`
|
||||||
|
A dictionary representing the images and their hover text of an activity.
|
||||||
|
It contains the following optional keys:
|
||||||
|
|
||||||
|
- ``large_image``: A string representing the ID for the large image asset.
|
||||||
|
- ``large_text``: A string representing the text when hovering over the large image asset.
|
||||||
|
- ``small_image``: A string representing the ID for the small image asset.
|
||||||
|
- ``small_text``: A string representing the text when hovering over the small image asset.
|
||||||
|
|
||||||
|
party: :class:`dict`
|
||||||
|
A dictionary representing the activity party. It contains the following optional keys:
|
||||||
|
|
||||||
|
- ``id``: A string representing the party ID.
|
||||||
|
- ``size``: A list of up to two integer elements denoting (current_size, maximum_size).
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('state', 'details', 'timestamps', 'assets', 'party',
|
||||||
|
'flags', 'sync_id', 'session_id', 'type', 'name', 'url', 'application_id')
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.state = kwargs.pop('state', None)
|
||||||
|
self.details = kwargs.pop('details', None)
|
||||||
|
self.timestamps = kwargs.pop('timestamps', {})
|
||||||
|
self.assets = kwargs.pop('assets', {})
|
||||||
|
self.party = kwargs.pop('party', {})
|
||||||
|
self.application_id = kwargs.pop('application_id', None)
|
||||||
|
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.type = try_enum(ActivityType, kwargs.pop('type', -1))
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
ret = {}
|
||||||
|
for attr in self.__slots__:
|
||||||
|
value = getattr(self, attr, None)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(value, dict) and len(value) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ret[attr] = value
|
||||||
|
ret['type'] = int(self.type)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start(self):
|
||||||
|
"""Optional[:class:`datetime.datetime`]: When the user started doing this activity in UTC, if applicable."""
|
||||||
|
try:
|
||||||
|
return datetime.datetime.utcfromtimestamp(self.timestamps['start'] / 1000)
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end(self):
|
||||||
|
"""Optional[:class:`datetime.datetime`]: When the user will stop doing this activity in UTC, if applicable."""
|
||||||
|
try:
|
||||||
|
return datetime.datetime.utcfromtimestamp(self.timestamps['end'] / 1000)
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def large_image_url(self):
|
||||||
|
"""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
|
||||||
|
|
||||||
|
try:
|
||||||
|
large_image = self.assets['large_image']
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return 'htps://cdn.discordapp.com/app-assets/{0}/{1}.png'.format(self.application_id, large_image)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def small_image_url(self):
|
||||||
|
"""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
|
||||||
|
|
||||||
|
try:
|
||||||
|
small_image = self.assets['small_image']
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return 'htps://cdn.discordapp.com/app-assets/{0}/{1}.png'.format(self.application_id, small_image)
|
||||||
|
@property
|
||||||
|
def large_image_text(self):
|
||||||
|
"""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):
|
||||||
|
"""Optional[:class:`str`]: Returns the small image asset hover text of this activity if applicable."""
|
||||||
|
return self.assets.get('small_text', None)
|
||||||
|
|
||||||
|
|
||||||
|
class Game(_ActivityTag):
|
||||||
|
"""A slimmed down version of :class:`Activity` that represents a Discord game.
|
||||||
|
|
||||||
|
This is typically displayed via **Playing** on the official Discord client.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two games are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two games are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the game's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the game's name.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The game's name.
|
||||||
|
start: Optional[:class:`datetime.datetime`]
|
||||||
|
A naive UTC timestamp representing when the game started. Keyword-only parameter. Ignored for bots.
|
||||||
|
end: Optional[:class:`datetime.datetime`]
|
||||||
|
A naive UTC timestamp representing when the game started. Keyword-only parameter. Ignored for bots.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The game's name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('name', '_end', '_start')
|
||||||
|
|
||||||
|
def __init__(self, name, **extra):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
try:
|
||||||
|
timestamps = extra['timestamps']
|
||||||
|
except KeyError:
|
||||||
|
self._extract_timestamp(extra, 'start')
|
||||||
|
self._extract_timestamp(extra, 'end')
|
||||||
|
else:
|
||||||
|
self._start = timestamps.get('start', 0)
|
||||||
|
self._end = timestamps.get('end', 0)
|
||||||
|
|
||||||
|
def _extract_timestamp(self, data, key):
|
||||||
|
try:
|
||||||
|
dt = data[key]
|
||||||
|
except KeyError:
|
||||||
|
setattr(self, '_' + key, 0)
|
||||||
|
else:
|
||||||
|
setattr(self, '_' + key, dt.timestamp() * 1000.0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""Returns the game's type. This is for compatibility with :class:`Activity`.
|
||||||
|
|
||||||
|
It always returns :attr:`ActivityType.playing`.
|
||||||
|
"""
|
||||||
|
return ActivityType.playing
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start(self):
|
||||||
|
"""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)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end(self):
|
||||||
|
"""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)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Game name={0.name!r}>'.format(self)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
timestamps = {}
|
||||||
|
if self._start:
|
||||||
|
timestamps['start'] = self._start
|
||||||
|
|
||||||
|
if self._end:
|
||||||
|
timestamps['end'] = self._end
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': ActivityType.playing.value,
|
||||||
|
'name': str(self.name),
|
||||||
|
'timestamps': timestamps
|
||||||
|
}
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Game) and other.name == self.name
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
class Streaming(_ActivityTag):
|
||||||
|
"""A slimmed down version of :class:`Activity` that represents a Discord streaming status.
|
||||||
|
|
||||||
|
This is typically displayed via **Streaming** on the official Discord client.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two streams are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two streams are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the stream's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the stream's name.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The stream's name.
|
||||||
|
url: :class:`str`
|
||||||
|
The stream's URL. Currently only twitch.tv URLs are supported. Anything else is silently
|
||||||
|
discarded.
|
||||||
|
details: Optional[:class:`str`]
|
||||||
|
If provided, typically the game the streamer is playing.
|
||||||
|
assets: :class:`dict`
|
||||||
|
A dictionary comprising of similar keys than those in :attr:`Activity.assets`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('name', 'url', 'details', 'assets')
|
||||||
|
|
||||||
|
def __init__(self, *, name, url, **extra):
|
||||||
|
self.name = name
|
||||||
|
self.url = url
|
||||||
|
self.details = extra.pop('details', None)
|
||||||
|
self.assets = extra.pop('assets', {})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""Returns the game's type. This is for compatibility with :class:`Activity`.
|
||||||
|
|
||||||
|
It always returns :attr:`ActivityType.streaming`.
|
||||||
|
"""
|
||||||
|
return ActivityType.streaming
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Streaming name={0.name!r}>'.format(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def twitch_name(self):
|
||||||
|
"""Optional[:class:`str`]: If provided, the twitch name of the user streaming.
|
||||||
|
|
||||||
|
This corresponds to the ``large_image`` key of the :attr:`Streaming.assets`
|
||||||
|
dictionary if it starts with ``twitch:``. Typically set by the Discord client.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
name = self.assets['large_image']
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return name[7:] if name[:7] == 'twitch:' else None
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
ret = {
|
||||||
|
'type': ActivityType.streaming.value,
|
||||||
|
'name': str(self.name),
|
||||||
|
'url': str(self.url),
|
||||||
|
'assets': self.assets
|
||||||
|
}
|
||||||
|
if self.details:
|
||||||
|
ret['details'] = self.details
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Streaming) and other.name == self.name and other.url == self.url
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
class Spotify:
|
||||||
|
"""Represents a Spotify listening activity from Discord. This is a special case of
|
||||||
|
:class:`Activity` that makes it easier to work with the Spotify integration.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two activities are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two activities are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the activity's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the string 'Spotify'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('_state', '_details', '_timestamps', '_assets', '_party', '_sync_id', '_session_id')
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""Returns the activity's type. This is for compatibility with :class:`Activity`.
|
||||||
|
|
||||||
|
It always returns :attr:`ActivityType.listening`.
|
||||||
|
"""
|
||||||
|
return ActivityType.listening
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
'flags': 48, # SYNC | PLAY
|
||||||
|
'name': 'Spotify',
|
||||||
|
'assets': self._assets,
|
||||||
|
'party': self._party,
|
||||||
|
'sync_id': self._sync_id,
|
||||||
|
'session_id': self.session_id,
|
||||||
|
'timestamps': self._timestamps,
|
||||||
|
'details': self._details,
|
||||||
|
'state': self._state
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""":class:`str`: The activity's name. This will always return "Spotify"."""
|
||||||
|
return 'Spotify'
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Spotify) and other._session_id == self._session_id
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self._session_id)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'Spotify'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Spotify title={0.title!r} artist={0.artist!r} track_id={0.track_id!r}>'.format(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self):
|
||||||
|
""":class:`str`: The title of the song being played."""
|
||||||
|
return self._details
|
||||||
|
|
||||||
|
@property
|
||||||
|
def artists(self):
|
||||||
|
"""List[:class:`str`]: The artists of the song being played."""
|
||||||
|
return self._state.split(';')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def artist(self):
|
||||||
|
""":class:`str`: The artist of the song being played.
|
||||||
|
|
||||||
|
This does not attempt to split the artist information into
|
||||||
|
multiple artists. Useful if there's only a single artist.
|
||||||
|
"""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def album(self):
|
||||||
|
""":class:`str`: The album that the song being played belongs to."""
|
||||||
|
return self._assets.get('large_text', '')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def album_cover_url(self):
|
||||||
|
""":class:`str`: The album cover image URL from Spotify's CDN."""
|
||||||
|
large_image = self._assets.get('large_image', '')
|
||||||
|
if large_image[:8] != 'spotify:':
|
||||||
|
return ''
|
||||||
|
album_image_id = large_image[8:]
|
||||||
|
return 'https://i.scdn.co/image/' + album_image_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def track_id(self):
|
||||||
|
""":class:`str`: The track ID used by Spotify to identify this song."""
|
||||||
|
return self._sync_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start(self):
|
||||||
|
""":class:`datetime.datetime`: When the user started playing this song in UTC."""
|
||||||
|
return datetime.datetime.utcfromtimestamp(self._timestamps['start'] / 1000)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end(self):
|
||||||
|
""":class:`datetime.datetime`: When the user will stop playing this song in UTC."""
|
||||||
|
return datetime.datetime.utcfromtimestamp(self._timestamps['end'] / 1000)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def duration(self):
|
||||||
|
""":class:`datetime.timedelta`: The duration of the song being played."""
|
||||||
|
return self.end - self.start
|
||||||
|
|
||||||
|
@property
|
||||||
|
def party_id(self):
|
||||||
|
""":class:`str`: The party ID of the listening party."""
|
||||||
|
return self._party.get('id', '')
|
||||||
|
|
||||||
|
def create_activity(data):
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
game_type = try_enum(ActivityType, data.get('type', -1))
|
||||||
|
if game_type is ActivityType.playing:
|
||||||
|
if 'application_id' in data or 'session_id' in data:
|
||||||
|
return Activity(**data)
|
||||||
|
return Game(**data)
|
||||||
|
elif game_type is ActivityType.streaming:
|
||||||
|
if 'url' in data:
|
||||||
|
return Streaming(**data)
|
||||||
|
return Activity(**data)
|
||||||
|
elif game_type is ActivityType.listening and 'sync_id' in data and 'session_id' in data:
|
||||||
|
return Spotify(**data)
|
||||||
|
return Activity(**data)
|
@ -72,8 +72,8 @@ class Client:
|
|||||||
.. _ProxyConnector: http://aiohttp.readthedocs.org/en/stable/client_reference.html#proxyconnector
|
.. _ProxyConnector: http://aiohttp.readthedocs.org/en/stable/client_reference.html#proxyconnector
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
-----------
|
||||||
max_messages : Optional[int]
|
max_messages : Optional[:class:`int`]
|
||||||
The maximum number of messages to store in the internal message cache.
|
The maximum number of messages to store in the internal message cache.
|
||||||
This defaults to 5000. Passing in `None` or a value less than 100
|
This defaults to 5000. Passing in `None` or a value less than 100
|
||||||
will use the default instead of the passed in value.
|
will use the default instead of the passed in value.
|
||||||
@ -82,24 +82,24 @@ class Client:
|
|||||||
in which case the default event loop is used via ``asyncio.get_event_loop()``.
|
in which case the default event loop is used via ``asyncio.get_event_loop()``.
|
||||||
connector : aiohttp.BaseConnector
|
connector : aiohttp.BaseConnector
|
||||||
The `connector`_ to use for connection pooling.
|
The `connector`_ to use for connection pooling.
|
||||||
proxy : Optional[str]
|
proxy : Optional[:class:`str`]
|
||||||
Proxy URL.
|
Proxy URL.
|
||||||
proxy_auth : Optional[aiohttp.BasicAuth]
|
proxy_auth : Optional[aiohttp.BasicAuth]
|
||||||
An object that represents proxy HTTP Basic Authorization.
|
An object that represents proxy HTTP Basic Authorization.
|
||||||
shard_id : Optional[int]
|
shard_id : Optional[:class:`int`]
|
||||||
Integer starting at 0 and less than shard_count.
|
Integer starting at 0 and less than shard_count.
|
||||||
shard_count : Optional[int]
|
shard_count : Optional[:class:`int`]
|
||||||
The total number of shards.
|
The total number of shards.
|
||||||
fetch_offline_members: bool
|
fetch_offline_members: :class:`bool`
|
||||||
Indicates if :func:`on_ready` should be delayed to fetch all offline
|
Indicates if :func:`on_ready` should be delayed to fetch all offline
|
||||||
members from the guilds the bot belongs to. If this is ``False``\, then
|
members from the guilds the bot belongs to. If this is ``False``\, then
|
||||||
no offline members are received and :meth:`request_offline_members`
|
no offline members are received and :meth:`request_offline_members`
|
||||||
must be used to fetch the offline members of the guild.
|
must be used to fetch the offline members of the guild.
|
||||||
game: Optional[:class:`Game`]
|
|
||||||
A game to start your presence with upon logging on to Discord.
|
|
||||||
status: Optional[:class:`Status`]
|
status: Optional[:class:`Status`]
|
||||||
A status to start your presence with upon logging on to Discord.
|
A status to start your presence with upon logging on to Discord.
|
||||||
heartbeat_timeout: float
|
activity: Optional[Union[:class:`Activity`, :class:`Game`, :class:`Streaming`]]
|
||||||
|
An activity to start your presence with upon logging on to Discord.
|
||||||
|
heartbeat_timeout: :class:`float`
|
||||||
The maximum numbers of seconds before timing out and restarting the
|
The maximum numbers of seconds before timing out and restarting the
|
||||||
WebSocket in the case of not receiving a HEARTBEAT_ACK. Useful if
|
WebSocket in the case of not receiving a HEARTBEAT_ACK. Useful if
|
||||||
processing the initial packets take too long to the point of disconnecting
|
processing the initial packets take too long to the point of disconnecting
|
||||||
@ -794,23 +794,24 @@ class Client:
|
|||||||
return self.event(coro)
|
return self.event(coro)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def change_presence(self, *, game=None, status=None, afk=False):
|
def change_presence(self, *, activity=None, status=None, afk=False):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Changes the client's presence.
|
Changes the client's presence.
|
||||||
|
|
||||||
The game parameter is a Game object (not a string) that represents
|
The activity parameter is a :class:`Activity` object (not a string) that represents
|
||||||
a game being played currently.
|
the activity being done currently. This could also be the slimmed down versions,
|
||||||
|
:class:`Game` and :class:`Streaming`.
|
||||||
|
|
||||||
Example: ::
|
Example: ::
|
||||||
|
|
||||||
game = discord.Game(name="with the API")
|
game = discord.Game("with the API")
|
||||||
await client.change_presence(status=discord.Status.idle, game=game)
|
await client.change_presence(status=discord.Status.idle, activity=game)
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
game: Optional[:class:`Game`]
|
activity: Optional[Union[:class:`Game`, :class:`Streaming`, :class:`Activity`]]
|
||||||
The game being played. None if no game is being played.
|
The activity being done. ``None`` if no currently active activity is done.
|
||||||
status: Optional[:class:`Status`]
|
status: Optional[:class:`Status`]
|
||||||
Indicates what status to change to. If None, then
|
Indicates what status to change to. If None, then
|
||||||
:attr:`Status.online` is used.
|
:attr:`Status.online` is used.
|
||||||
@ -822,7 +823,7 @@ class Client:
|
|||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
InvalidArgument
|
InvalidArgument
|
||||||
If the ``game`` parameter is not :class:`Game` or None.
|
If the ``activity`` parameter is not the proper type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if status is None:
|
if status is None:
|
||||||
@ -835,14 +836,14 @@ class Client:
|
|||||||
status_enum = status
|
status_enum = status
|
||||||
status = str(status)
|
status = str(status)
|
||||||
|
|
||||||
yield from self.ws.change_presence(game=game, status=status, afk=afk)
|
yield from self.ws.change_presence(activity=activity, status=status, afk=afk)
|
||||||
|
|
||||||
for guild in self._connection.guilds:
|
for guild in self._connection.guilds:
|
||||||
me = guild.me
|
me = guild.me
|
||||||
if me is None:
|
if me is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
me.game = game
|
me.activity = activity
|
||||||
me.status = status_enum
|
me.status = status_enum
|
||||||
|
|
||||||
# Guild stuff
|
# Guild stuff
|
||||||
|
@ -28,7 +28,8 @@ from enum import Enum, IntEnum
|
|||||||
|
|
||||||
__all__ = ['ChannelType', 'MessageType', 'VoiceRegion', 'VerificationLevel',
|
__all__ = ['ChannelType', 'MessageType', 'VoiceRegion', 'VerificationLevel',
|
||||||
'ContentFilter', 'Status', 'DefaultAvatar', 'RelationshipType',
|
'ContentFilter', 'Status', 'DefaultAvatar', 'RelationshipType',
|
||||||
'AuditLogAction', 'AuditLogActionCategory', 'UserFlags', ]
|
'AuditLogAction', 'AuditLogActionCategory', 'UserFlags',
|
||||||
|
'ActivityType', ]
|
||||||
|
|
||||||
class ChannelType(Enum):
|
class ChannelType(Enum):
|
||||||
text = 0
|
text = 0
|
||||||
@ -212,6 +213,14 @@ class UserFlags(Enum):
|
|||||||
partner = 2
|
partner = 2
|
||||||
hypesquad = 4
|
hypesquad = 4
|
||||||
|
|
||||||
|
class ActivityType(IntEnum):
|
||||||
|
unknown = -1
|
||||||
|
playing = 0
|
||||||
|
streaming = 1
|
||||||
|
listening = 2
|
||||||
|
watching = 3
|
||||||
|
|
||||||
|
|
||||||
def try_enum(cls, val):
|
def try_enum(cls, val):
|
||||||
"""A function that tries to turn the value into enum ``cls``.
|
"""A function that tries to turn the value into enum ``cls``.
|
||||||
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015-2017 Rapptz
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the "Software"),
|
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Game:
|
|
||||||
"""Represents a Discord game.
|
|
||||||
|
|
||||||
.. container:: operations
|
|
||||||
|
|
||||||
.. describe:: x == y
|
|
||||||
|
|
||||||
Checks if two games are equal.
|
|
||||||
|
|
||||||
.. describe:: x != y
|
|
||||||
|
|
||||||
Checks if two games are not equal.
|
|
||||||
|
|
||||||
.. describe:: hash(x)
|
|
||||||
|
|
||||||
Returns the game's hash.
|
|
||||||
|
|
||||||
.. describe:: str(x)
|
|
||||||
|
|
||||||
Returns the game's name.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
-----------
|
|
||||||
name: :class:`str`
|
|
||||||
The game's name.
|
|
||||||
url: :class:`str`
|
|
||||||
The game's URL. Usually used for twitch streaming.
|
|
||||||
type: :class:`int`
|
|
||||||
The type of game being played. 1 indicates "Streaming".
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ('name', 'type', 'url')
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.name = kwargs.get('name')
|
|
||||||
self.url = kwargs.get('url')
|
|
||||||
self.type = kwargs.get('type', 0)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self.name)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<Game name={0.name!r} type={0.type!r} url={0.url!r}>'.format(self)
|
|
||||||
|
|
||||||
def _iterator(self):
|
|
||||||
for attr in self.__slots__:
|
|
||||||
value = getattr(self, attr, None)
|
|
||||||
if value is not None:
|
|
||||||
yield (attr, value)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self._iterator()
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return isinstance(other, Game) and other.name == self.name
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return not self.__eq__(other)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self.name)
|
|
@ -30,7 +30,7 @@ import websockets
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from . import utils, compat
|
from . import utils, compat
|
||||||
from .game import Game
|
from .activity import create_activity, _ActivityTag
|
||||||
from .errors import ConnectionClosed, InvalidArgument
|
from .errors import ConnectionClosed, InvalidArgument
|
||||||
import logging
|
import logging
|
||||||
import zlib, json
|
import zlib, json
|
||||||
@ -283,10 +283,10 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
payload['d']['shard'] = [self.shard_id, self.shard_count]
|
payload['d']['shard'] = [self.shard_id, self.shard_count]
|
||||||
|
|
||||||
state = self._connection
|
state = self._connection
|
||||||
if state._game is not None or state._status is not None:
|
if state._activity is not None or state._status is not None:
|
||||||
payload['d']['presence'] = {
|
payload['d']['presence'] = {
|
||||||
'status': state._status,
|
'status': state._status,
|
||||||
'game': state._game,
|
'game': state._activity,
|
||||||
'since': 0,
|
'since': 0,
|
||||||
'afk': False
|
'afk': False
|
||||||
}
|
}
|
||||||
@ -469,19 +469,19 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
raise ConnectionClosed(e, shard_id=self.shard_id) from e
|
raise ConnectionClosed(e, shard_id=self.shard_id) from e
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def change_presence(self, *, game=None, status=None, afk=False, since=0.0):
|
def change_presence(self, *, activity=None, status=None, afk=False, since=0.0):
|
||||||
if game is not None and not isinstance(game, Game):
|
if activity is not None:
|
||||||
raise InvalidArgument('game must be of type Game or None')
|
if not isinstance(activity, _ActivityTag):
|
||||||
|
raise InvalidArgument('activity must be one of Game, Streaming, or Activity.')
|
||||||
|
activity = activity.to_dict()
|
||||||
|
|
||||||
if status == 'idle':
|
if status == 'idle':
|
||||||
since = int(time.time() * 1000)
|
since = int(time.time() * 1000)
|
||||||
|
|
||||||
sent_game = dict(game) if game else None
|
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
'op': self.PRESENCE,
|
'op': self.PRESENCE,
|
||||||
'd': {
|
'd': {
|
||||||
'game': sent_game,
|
'game': activity,
|
||||||
'afk': afk,
|
'afk': afk,
|
||||||
'since': since,
|
'since': since,
|
||||||
'status': status
|
'status': status
|
||||||
|
@ -32,7 +32,7 @@ from collections import namedtuple, defaultdict
|
|||||||
from . import utils
|
from . import utils
|
||||||
from .role import Role
|
from .role import Role
|
||||||
from .member import Member, VoiceState
|
from .member import Member, VoiceState
|
||||||
from .game import Game
|
from .activity import create_activity
|
||||||
from .permissions import PermissionOverwrite
|
from .permissions import PermissionOverwrite
|
||||||
from .colour import Colour
|
from .colour import Colour
|
||||||
from .errors import InvalidArgument, ClientException
|
from .errors import InvalidArgument, ClientException
|
||||||
@ -243,8 +243,7 @@ class Guild(Hashable):
|
|||||||
member = self.get_member(user_id)
|
member = self.get_member(user_id)
|
||||||
if member is not None:
|
if member is not None:
|
||||||
member.status = try_enum(Status, presence['status'])
|
member.status = try_enum(Status, presence['status'])
|
||||||
game = presence.get('game', {})
|
member.activity = create_activity(presence.get('game'))
|
||||||
member.game = Game(**game) if game else None
|
|
||||||
|
|
||||||
if 'channels' in data:
|
if 'channels' in data:
|
||||||
channels = data['channels']
|
channels = data['channels']
|
||||||
|
@ -32,7 +32,7 @@ import discord.abc
|
|||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .user import BaseUser, User
|
from .user import BaseUser, User
|
||||||
from .game import Game
|
from .activity import create_activity
|
||||||
from .permissions import Permissions
|
from .permissions import Permissions
|
||||||
from .enums import Status, try_enum
|
from .enums import Status, try_enum
|
||||||
from .colour import Colour
|
from .colour import Colour
|
||||||
@ -137,25 +137,25 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
roles
|
roles: List[:class:`Role`]
|
||||||
A :class:`list` of :class:`Role` that the member belongs to. Note that the first element of this
|
A :class:`list` of :class:`Role` that the member belongs to. Note that the first element of this
|
||||||
list is always the default '@everyone' role. These roles are sorted by their position
|
list is always the default '@everyone' role. These roles are sorted by their position
|
||||||
in the role hierarchy.
|
in the role hierarchy.
|
||||||
joined_at : `datetime.datetime`
|
joined_at: `datetime.datetime`
|
||||||
A datetime object that specifies the date and time in UTC that the member joined the guild for
|
A datetime object that specifies the date and time in UTC that the member joined the guild for
|
||||||
the first time.
|
the first time.
|
||||||
status : :class:`Status`
|
status : :class:`Status`
|
||||||
The member's status. There is a chance that the status will be a :class:`str`
|
The member's status. There is a chance that the status will be a :class:`str`
|
||||||
if it is a value that is not recognised by the enumerator.
|
if it is a value that is not recognised by the enumerator.
|
||||||
game : :class:`Game`
|
activity: Union[:class:`Game`, :class:`Streaming`, :class:`Activity`]
|
||||||
The game that the user is currently playing. Could be None if no game is being played.
|
The activity that the user is currently doing. Could be None if no activity is being done.
|
||||||
guild : :class:`Guild`
|
guild: :class:`Guild`
|
||||||
The guild that the member belongs to.
|
The guild that the member belongs to.
|
||||||
nick : Optional[:class:`str`]
|
nick: Optional[:class:`str`]
|
||||||
The guild specific nickname of the user.
|
The guild specific nickname of the user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('roles', 'joined_at', 'status', 'game', 'guild', 'nick', '_user', '_state')
|
__slots__ = ('roles', 'joined_at', 'status', 'activity', 'guild', 'nick', '_user', '_state')
|
||||||
|
|
||||||
def __init__(self, *, data, guild, state):
|
def __init__(self, *, data, guild, state):
|
||||||
self._state = state
|
self._state = state
|
||||||
@ -164,8 +164,7 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
self.joined_at = utils.parse_time(data.get('joined_at'))
|
self.joined_at = utils.parse_time(data.get('joined_at'))
|
||||||
self._update_roles(data)
|
self._update_roles(data)
|
||||||
self.status = Status.offline
|
self.status = Status.offline
|
||||||
game = data.get('game', {})
|
self.activity = create_activity(data.get('game'))
|
||||||
self.game = Game(**game) if game else None
|
|
||||||
self.nick = data.get('nick', None)
|
self.nick = data.get('nick', None)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -218,8 +217,8 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
|
|
||||||
def _presence_update(self, data, user):
|
def _presence_update(self, data, user):
|
||||||
self.status = try_enum(Status, data['status'])
|
self.status = try_enum(Status, data['status'])
|
||||||
game = data.get('game', {})
|
self.activity = create_activity(data.get('game'))
|
||||||
self.game = Game(**game) if game else None
|
|
||||||
u = self._user
|
u = self._user
|
||||||
u.name = user.get('username', u.name)
|
u.name = user.get('username', u.name)
|
||||||
u.avatar = user.get('avatar', u.avatar)
|
u.avatar = user.get('avatar', u.avatar)
|
||||||
|
@ -175,6 +175,24 @@ class Message:
|
|||||||
Specifies if the message is currently pinned.
|
Specifies if the message is currently pinned.
|
||||||
reactions : List[:class:`Reaction`]
|
reactions : List[:class:`Reaction`]
|
||||||
Reactions to a message. Reactions can be either custom emoji or standard unicode emoji.
|
Reactions to a message. Reactions can be either custom emoji or standard unicode emoji.
|
||||||
|
activity: Optional[:class:`dict`]
|
||||||
|
The activity associated with this message. Sent with Rich-Presence related messages that for
|
||||||
|
example, request joining, spectating, or listening to or with another member.
|
||||||
|
|
||||||
|
It is a dictionary with the following optional keys:
|
||||||
|
|
||||||
|
- ``type``: An integer denoting the type of message activity being requested.
|
||||||
|
- ``party_id``: The party ID associated with the party.
|
||||||
|
application: Optional[:class:`dict`]
|
||||||
|
The rich presence enabled application associated with this message.
|
||||||
|
|
||||||
|
It is a dictionary with the following keys:
|
||||||
|
|
||||||
|
- ``id``: A string representing the application's ID.
|
||||||
|
- ``name``: A string representing the application's name.
|
||||||
|
- ``description``: A string representing the application's description.
|
||||||
|
- ``icon``: A string representing the icon ID of the application.
|
||||||
|
- ``cover_image``: A string representing the embed's image asset ID.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ( '_edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
|
__slots__ = ( '_edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
|
||||||
@ -182,13 +200,16 @@ class Message:
|
|||||||
'_cs_channel_mentions', '_cs_raw_mentions', 'attachments',
|
'_cs_channel_mentions', '_cs_raw_mentions', 'attachments',
|
||||||
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
||||||
'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
|
'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
|
||||||
'_cs_system_content', '_cs_guild', '_state', 'reactions' )
|
'_cs_system_content', '_cs_guild', '_state', 'reactions',
|
||||||
|
'application', 'activity' )
|
||||||
|
|
||||||
def __init__(self, *, state, channel, data):
|
def __init__(self, *, state, channel, data):
|
||||||
self._state = state
|
self._state = state
|
||||||
self.id = int(data['id'])
|
self.id = int(data['id'])
|
||||||
self.webhook_id = utils._get_as_snowflake(data, 'webhook_id')
|
self.webhook_id = utils._get_as_snowflake(data, 'webhook_id')
|
||||||
self.reactions = [Reaction(message=self, data=d) for d in data.get('reactions', [])]
|
self.reactions = [Reaction(message=self, data=d) for d in data.get('reactions', [])]
|
||||||
|
self.application = data.get('application')
|
||||||
|
self.activity = data.get('activity')
|
||||||
self._update(channel, data)
|
self._update(channel, data)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -242,6 +263,8 @@ class Message:
|
|||||||
self.channel = channel
|
self.channel = channel
|
||||||
self._edited_timestamp = utils.parse_time(data.get('edited_timestamp'))
|
self._edited_timestamp = utils.parse_time(data.get('edited_timestamp'))
|
||||||
self._try_patch(data, 'pinned')
|
self._try_patch(data, 'pinned')
|
||||||
|
self._try_patch(data, 'application')
|
||||||
|
self._try_patch(data, 'activity')
|
||||||
self._try_patch(data, 'mention_everyone')
|
self._try_patch(data, 'mention_everyone')
|
||||||
self._try_patch(data, 'tts')
|
self._try_patch(data, 'tts')
|
||||||
self._try_patch(data, 'type', lambda x: try_enum(MessageType, x))
|
self._try_patch(data, 'type', lambda x: try_enum(MessageType, x))
|
||||||
|
@ -307,18 +307,24 @@ class AutoShardedClient(Client):
|
|||||||
yield from self.http.close()
|
yield from self.http.close()
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def change_presence(self, *, game=None, status=None, afk=False, shard_id=None):
|
def change_presence(self, *, activity=None, status=None, afk=False, shard_id=None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Changes the client's presence.
|
Changes the client's presence.
|
||||||
|
|
||||||
The game parameter is a Game object (not a string) that represents
|
The activity parameter is a :class:`Activity` object (not a string) that represents
|
||||||
a game being played currently.
|
the activity being done currently. This could also be the slimmed down versions,
|
||||||
|
:class:`Game` and :class:`Streaming`.
|
||||||
|
|
||||||
|
Example: ::
|
||||||
|
|
||||||
|
game = discord.Game("with the API")
|
||||||
|
await client.change_presence(status=discord.Status.idle, activity=game)
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
game: Optional[:class:`Game`]
|
activity: Optional[Union[:class:`Game`, :class:`Streaming`, :class:`Activity`]]
|
||||||
The game being played. None if no game is being played.
|
The activity being done. ``None`` if no currently active activity is done.
|
||||||
status: Optional[:class:`Status`]
|
status: Optional[:class:`Status`]
|
||||||
Indicates what status to change to. If None, then
|
Indicates what status to change to. If None, then
|
||||||
:attr:`Status.online` is used.
|
:attr:`Status.online` is used.
|
||||||
@ -334,7 +340,7 @@ class AutoShardedClient(Client):
|
|||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
InvalidArgument
|
InvalidArgument
|
||||||
If the ``game`` parameter is not :class:`Game` or None.
|
If the ``activity`` parameter is not of proper type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if status is None:
|
if status is None:
|
||||||
@ -349,12 +355,12 @@ class AutoShardedClient(Client):
|
|||||||
|
|
||||||
if shard_id is None:
|
if shard_id is None:
|
||||||
for shard in self.shards.values():
|
for shard in self.shards.values():
|
||||||
yield from shard.ws.change_presence(game=game, status=status, afk=afk)
|
yield from shard.ws.change_presence(activity=activity, status=status, afk=afk)
|
||||||
|
|
||||||
guilds = self._connection.guilds
|
guilds = self._connection.guilds
|
||||||
else:
|
else:
|
||||||
shard = self.shards[shard_id]
|
shard = self.shards[shard_id]
|
||||||
yield from shard.ws.change_presence(game=game, status=status, afk=afk)
|
yield from shard.ws.change_presence(activity=activity, status=status, afk=afk)
|
||||||
guilds = [g for g in self._connection.guilds if g.shard_id == shard_id]
|
guilds = [g for g in self._connection.guilds if g.shard_id == shard_id]
|
||||||
|
|
||||||
for guild in guilds:
|
for guild in guilds:
|
||||||
@ -362,5 +368,5 @@ class AutoShardedClient(Client):
|
|||||||
if me is None:
|
if me is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
me.game = game
|
me.activity = activity
|
||||||
me.status = status_enum
|
me.status = status_enum
|
||||||
|
@ -25,6 +25,7 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
|
from .activity import _ActivityTag
|
||||||
from .user import User, ClientUser
|
from .user import User, ClientUser
|
||||||
from .emoji import Emoji, PartialEmoji
|
from .emoji import Emoji, PartialEmoji
|
||||||
from .message import Message
|
from .message import Message
|
||||||
@ -67,9 +68,12 @@ class ConnectionState:
|
|||||||
self.heartbeat_timeout = options.get('heartbeat_timeout', 60.0)
|
self.heartbeat_timeout = options.get('heartbeat_timeout', 60.0)
|
||||||
self._listeners = []
|
self._listeners = []
|
||||||
|
|
||||||
game = options.get('game', None)
|
activity = options.get('activity', None)
|
||||||
if game:
|
if activity:
|
||||||
game = dict(game)
|
if not isinstance(activity, _ActivityTag):
|
||||||
|
raise TypeError('activity parameter must be one of Game, Streaming, or Activity.')
|
||||||
|
|
||||||
|
activity = activity.to_dict()
|
||||||
|
|
||||||
status = options.get('status', None)
|
status = options.get('status', None)
|
||||||
if status:
|
if status:
|
||||||
@ -78,7 +82,7 @@ class ConnectionState:
|
|||||||
else:
|
else:
|
||||||
status = str(status)
|
status = str(status)
|
||||||
|
|
||||||
self._game = game
|
self._activity = activity
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
|
47
docs/api.rst
47
docs/api.rst
@ -687,6 +687,27 @@ All enumerations are subclasses of `enum`_.
|
|||||||
|
|
||||||
The system message denoting that a new member has joined a Guild.
|
The system message denoting that a new member has joined a Guild.
|
||||||
|
|
||||||
|
.. class:: ActivityType
|
||||||
|
|
||||||
|
Specifies the type of :class:`Activity`. This is used to check how to
|
||||||
|
interpret the activity itself.
|
||||||
|
|
||||||
|
.. attribute:: unknown
|
||||||
|
|
||||||
|
An unknown activity type. This should generally not happen.
|
||||||
|
.. attribute:: playing
|
||||||
|
|
||||||
|
A "Playing" activity type.
|
||||||
|
.. attribute:: streaming
|
||||||
|
|
||||||
|
A "Streaming" activity type.
|
||||||
|
.. attribute:: listening
|
||||||
|
|
||||||
|
A "Listening" activity type.
|
||||||
|
.. attribute:: watching
|
||||||
|
|
||||||
|
A "Watching" activity type.
|
||||||
|
|
||||||
.. class:: VoiceRegion
|
.. class:: VoiceRegion
|
||||||
|
|
||||||
Specifies the region a voice server belongs to.
|
Specifies the region a voice server belongs to.
|
||||||
@ -698,7 +719,7 @@ All enumerations are subclasses of `enum`_.
|
|||||||
|
|
||||||
The US East region.
|
The US East region.
|
||||||
.. attribute:: us_south
|
.. attribute:: us_south
|
||||||
|
|
||||||
The US South region.
|
The US South region.
|
||||||
.. attribute:: us_central
|
.. attribute:: us_central
|
||||||
|
|
||||||
@ -729,10 +750,10 @@ All enumerations are subclasses of `enum`_.
|
|||||||
|
|
||||||
The Brazil region.
|
The Brazil region.
|
||||||
.. attribute:: hongkong
|
.. attribute:: hongkong
|
||||||
|
|
||||||
The Hong Kong region.
|
The Hong Kong region.
|
||||||
.. attribute:: russia
|
.. attribute:: russia
|
||||||
|
|
||||||
The Russia region.
|
The Russia region.
|
||||||
.. attribute:: vip_us_east
|
.. attribute:: vip_us_east
|
||||||
|
|
||||||
@ -1880,6 +1901,12 @@ Member
|
|||||||
.. autocomethod:: typing
|
.. autocomethod:: typing
|
||||||
:async-with:
|
:async-with:
|
||||||
|
|
||||||
|
Spotify
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: Spotify()
|
||||||
|
:members:
|
||||||
|
|
||||||
VoiceState
|
VoiceState
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
@ -2011,12 +2038,24 @@ Colour
|
|||||||
.. autoclass:: Colour
|
.. autoclass:: Colour
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Activity
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: Activity
|
||||||
|
:members:
|
||||||
|
|
||||||
Game
|
Game
|
||||||
~~~~
|
~~~~~
|
||||||
|
|
||||||
.. autoclass:: Game
|
.. autoclass:: Game
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Streaming
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: Streaming
|
||||||
|
:members:
|
||||||
|
|
||||||
Permissions
|
Permissions
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user