mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-06 11:57:17 +00:00
Add support for querying information about group calls.
This commit is contained in:
parent
33eba31aac
commit
1c8ab25917
@ -25,7 +25,7 @@ from .server import Server
|
|||||||
from .member import Member
|
from .member import Member
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .errors import *
|
from .errors import *
|
||||||
from .calls import CallMessage
|
from .calls import CallMessage, GroupCall
|
||||||
from .permissions import Permissions, PermissionOverwrite
|
from .permissions import Permissions, PermissionOverwrite
|
||||||
from .role import Role
|
from .role import Role
|
||||||
from .colour import Color, Colour
|
from .colour import Color, Colour
|
||||||
|
@ -25,9 +25,11 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
|
from .enums import ServerRegion, try_enum
|
||||||
|
from .member import VoiceState
|
||||||
|
|
||||||
class CallMessage:
|
class CallMessage:
|
||||||
"""Represents a group call from Discord.
|
"""Represents a group call message from Discord.
|
||||||
|
|
||||||
This is only received in cases where the message type is equivalent to
|
This is only received in cases where the message type is equivalent to
|
||||||
:attr:`MessageType.call`.
|
:attr:`MessageType.call`.
|
||||||
@ -46,3 +48,78 @@ class CallMessage:
|
|||||||
self.channel = channel
|
self.channel = channel
|
||||||
self.ended_timestamp = utils.parse_time(kwargs.get('ended_timestamp'))
|
self.ended_timestamp = utils.parse_time(kwargs.get('ended_timestamp'))
|
||||||
self.participants = kwargs.get('participants')
|
self.participants = kwargs.get('participants')
|
||||||
|
|
||||||
|
class GroupCall:
|
||||||
|
"""Represents the actual group call from Discord.
|
||||||
|
|
||||||
|
This is accompanied with a :class:`CallMessage` denoting the information.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
message: :class:`CallMessage`
|
||||||
|
The message associated with this group call.
|
||||||
|
unavailable: bool
|
||||||
|
Denotes if this group call is unavailable.
|
||||||
|
ringing: List[:class:`User`]
|
||||||
|
A list of users that are currently being rung to join the call.
|
||||||
|
region: :class:`ServerRegion`
|
||||||
|
The server region the group call is being hosted on.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.message = kwargs.get('message')
|
||||||
|
self.unavailable = kwargs.get('unavailable')
|
||||||
|
self._voice_states = {}
|
||||||
|
|
||||||
|
for state in kwargs.get('voice_states', []):
|
||||||
|
self._update_voice_state(state)
|
||||||
|
|
||||||
|
self._update(**kwargs)
|
||||||
|
|
||||||
|
def _update(self, **kwargs):
|
||||||
|
self.region = try_enum(ServerRegion, kwargs.get('region'))
|
||||||
|
lookup = {u.id: u for u in self.message.channel.recipients}
|
||||||
|
self.ringing = list(filter(None, map(lambda i: lookup.get(i), kwargs.get('ringing', []))))
|
||||||
|
|
||||||
|
def _update_voice_state(self, data):
|
||||||
|
user_id = data['user_id']
|
||||||
|
# left the voice channel?
|
||||||
|
if data['channel_id'] is None:
|
||||||
|
self._voice_states.pop(user_id, None)
|
||||||
|
else:
|
||||||
|
self._voice_states[user_id] = VoiceState(**data, voice_channel=self.channel)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connected(self):
|
||||||
|
"""A property that returns the list of :class:`User` that are currently in this call."""
|
||||||
|
ret = [u for u in self.channel.recipients if self.voice_state_for(u) is not None]
|
||||||
|
me = self.channel.me
|
||||||
|
if self.voice_state_for(me) is not None:
|
||||||
|
ret.append(me)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@property
|
||||||
|
def channel(self):
|
||||||
|
""":class:`PrivateChannel`\: Returns the channel the group call is in."""
|
||||||
|
return self.message.channel
|
||||||
|
|
||||||
|
def voice_state_for(self, user):
|
||||||
|
"""Retrieves the :class:`VoiceState` for a specified :class:`User`.
|
||||||
|
|
||||||
|
If the :class:`User` has no voice state then this function returns
|
||||||
|
``None``.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
user: :class:`User`
|
||||||
|
The user to retrieve the voice state for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
Optiona[:class:`VoiceState`]
|
||||||
|
The voice state associated with this user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._voice_states.get(user.id)
|
||||||
|
|
||||||
|
@ -2651,6 +2651,22 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
return self.connection._get_voice_client(server.id)
|
return self.connection._get_voice_client(server.id)
|
||||||
|
|
||||||
|
def group_call_in(self, channel):
|
||||||
|
"""Returns the :class:`GroupCall` associated with a private channel.
|
||||||
|
|
||||||
|
If no group call is found then ``None`` is returned.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
channel: :class:`PrivateChannel`
|
||||||
|
The group private channel to query the group call for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
Optional[:class:`GroupCall`]
|
||||||
|
The group call.
|
||||||
|
"""
|
||||||
|
return self.connection._calls.get(channel.id)
|
||||||
|
|
||||||
# Miscellaneous stuff
|
# Miscellaneous stuff
|
||||||
|
|
||||||
|
@ -27,9 +27,55 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
from .user import User
|
from .user import User
|
||||||
from .game import Game
|
from .game import Game
|
||||||
from . import utils
|
from . import utils
|
||||||
from .enums import Status
|
from .enums import Status, ChannelType
|
||||||
from .colour import Colour
|
from .colour import Colour
|
||||||
|
|
||||||
|
class VoiceState:
|
||||||
|
"""Represents a Discord user's voice state.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
------------
|
||||||
|
deaf: bool
|
||||||
|
Indicates if the user is currently deafened by the server.
|
||||||
|
mute: bool
|
||||||
|
Indicates if the user is currently muted by the server.
|
||||||
|
self_mute: bool
|
||||||
|
Indicates if the user is currently muted by their own accord.
|
||||||
|
self_deaf: bool
|
||||||
|
Indicates if the user is currently deafened by their own accord.
|
||||||
|
is_afk: bool
|
||||||
|
Indicates if the user is currently in the AFK channel in the server.
|
||||||
|
voice_channel: Optional[Union[:class:`Channel`, :class:`PrivateChannel`]]
|
||||||
|
The voice channel that the user is currently connected to. None if the user
|
||||||
|
is not currently in a voice channel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [ 'session_id', 'deaf', 'mute', 'self_mute',
|
||||||
|
'self_deaf', 'is_afk', 'voice_channel' ]
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.session_id = kwargs.get('session_id')
|
||||||
|
self._update_voice_state(**kwargs)
|
||||||
|
|
||||||
|
def _update_voice_state(self, **kwargs):
|
||||||
|
self.self_mute = kwargs.get('self_mute', False)
|
||||||
|
self.self_deaf = kwargs.get('self_deaf', False)
|
||||||
|
self.is_afk = kwargs.get('suppress', False)
|
||||||
|
self.mute = kwargs.get('mute', False)
|
||||||
|
self.deaf = kwargs.get('deaf', False)
|
||||||
|
self._handle_voice_channel(kwargs.get('voice_channel'), kwargs.get('user_id'))
|
||||||
|
|
||||||
|
def _handle_voice_channel(self, voice_channel, user_id):
|
||||||
|
self.voice_channel = voice_channel
|
||||||
|
|
||||||
|
def flatten_voice_states(cls):
|
||||||
|
for attr in VoiceState.__slots__:
|
||||||
|
def getter(self, x=attr):
|
||||||
|
return getattr(self.voice, x)
|
||||||
|
setattr(cls, attr, property(getter))
|
||||||
|
return cls
|
||||||
|
|
||||||
|
@flatten_voice_states
|
||||||
class Member(User):
|
class Member(User):
|
||||||
"""Represents a Discord member to a :class:`Server`.
|
"""Represents a Discord member to a :class:`Server`.
|
||||||
|
|
||||||
@ -38,19 +84,9 @@ class Member(User):
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
deaf : bool
|
voice: :class:`VoiceState`
|
||||||
Indicates if the member is currently deafened by the server.
|
The member's voice state. Properties are defined to mirror access of the attributes.
|
||||||
mute : bool
|
e.g. ``Member.is_afk`` is equivalent to `Member.voice.is_afk``.
|
||||||
Indicates if the member is currently muted by the server.
|
|
||||||
self_mute : bool
|
|
||||||
Indicates if the member is currently muted by their own accord.
|
|
||||||
self_deaf : bool
|
|
||||||
Indicates if the member is currently deafened by their own accord.
|
|
||||||
is_afk : bool
|
|
||||||
Indicates if the member is currently in the AFK channel in the server.
|
|
||||||
voice_channel : :class:`Channel`
|
|
||||||
The voice channel that the member is currently connected to. None if the member
|
|
||||||
is not currently in a voice channel.
|
|
||||||
roles
|
roles
|
||||||
A list of :class:`Role` that the member belongs to. Note that the first element of this
|
A list of :class:`Role` that the member belongs to. Note that the first element of this
|
||||||
list is always the default '@everyone' role.
|
list is always the default '@everyone' role.
|
||||||
@ -68,14 +104,11 @@ class Member(User):
|
|||||||
The server specific nickname of the user.
|
The server specific nickname of the user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = [ 'deaf', 'mute', 'self_mute', 'self_deaf', 'is_afk',
|
__slots__ = [ 'roles', 'joined_at', 'status', 'game', 'server', 'nick', 'voice' ]
|
||||||
'voice_channel', 'roles', 'joined_at', 'status', 'game',
|
|
||||||
'server', 'nick' ]
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs.get('user'))
|
super().__init__(**kwargs.get('user'))
|
||||||
self.deaf = kwargs.get('deaf')
|
self.voice = VoiceState(**kwargs)
|
||||||
self.mute = kwargs.get('mute')
|
|
||||||
self.joined_at = utils.parse_time(kwargs.get('joined_at'))
|
self.joined_at = utils.parse_time(kwargs.get('joined_at'))
|
||||||
self.roles = kwargs.get('roles', [])
|
self.roles = kwargs.get('roles', [])
|
||||||
self.status = Status.offline
|
self.status = Status.offline
|
||||||
@ -83,14 +116,33 @@ class Member(User):
|
|||||||
self.game = Game(**game) if game else None
|
self.game = Game(**game) if game else None
|
||||||
self.server = kwargs.get('server', None)
|
self.server = kwargs.get('server', None)
|
||||||
self.nick = kwargs.get('nick', None)
|
self.nick = kwargs.get('nick', None)
|
||||||
self._update_voice_state(mute=self.mute, deaf=self.deaf)
|
|
||||||
|
|
||||||
def _update_voice_state(self, **kwargs):
|
def _update_voice_state(self, **kwargs):
|
||||||
self.self_mute = kwargs.get('self_mute', False)
|
self.voice.self_mute = kwargs.get('self_mute', False)
|
||||||
self.self_deaf = kwargs.get('self_deaf', False)
|
self.voice.self_deaf = kwargs.get('self_deaf', False)
|
||||||
self.is_afk = kwargs.get('suppress', False)
|
self.voice.is_afk = kwargs.get('suppress', False)
|
||||||
self.mute = kwargs.get('mute', False)
|
self.voice.mute = kwargs.get('mute', False)
|
||||||
self.deaf = kwargs.get('deaf', False)
|
self.voice.deaf = kwargs.get('deaf', False)
|
||||||
|
old_channel = getattr(self, 'voice_channel', None)
|
||||||
|
vc = kwargs.get('voice_channel')
|
||||||
|
|
||||||
|
if old_channel is None and vc is not None:
|
||||||
|
# we joined a channel
|
||||||
|
vc.voice_members.append(self)
|
||||||
|
elif old_channel is not None:
|
||||||
|
try:
|
||||||
|
# we either left a channel or we switched channels
|
||||||
|
old_channel.voice_members.remove(self)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
# we switched channels
|
||||||
|
if vc is not None:
|
||||||
|
vc.voice_members.append(self)
|
||||||
|
|
||||||
|
self.voice.voice_channel = vc
|
||||||
|
|
||||||
|
def _handle_voice_channel(self, voice_channel, user_id):
|
||||||
old_channel = getattr(self, 'voice_channel', None)
|
old_channel = getattr(self, 'voice_channel', None)
|
||||||
self.voice_channel = kwargs.get('voice_channel')
|
self.voice_channel = kwargs.get('voice_channel')
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ from .member import Member
|
|||||||
from .role import Role
|
from .role import Role
|
||||||
from . import utils, compat
|
from . import utils, compat
|
||||||
from .enums import Status, ChannelType, try_enum
|
from .enums import Status, ChannelType, try_enum
|
||||||
|
from .calls import GroupCall
|
||||||
|
|
||||||
from collections import deque, namedtuple
|
from collections import deque, namedtuple
|
||||||
import copy, enum, math
|
import copy, enum, math
|
||||||
@ -63,6 +63,7 @@ class ConnectionState:
|
|||||||
self.user = None
|
self.user = None
|
||||||
self.sequence = None
|
self.sequence = None
|
||||||
self.session_id = None
|
self.session_id = None
|
||||||
|
self._calls = {}
|
||||||
self._servers = {}
|
self._servers = {}
|
||||||
self._voice_clients = {}
|
self._voice_clients = {}
|
||||||
self._private_channels = {}
|
self._private_channels = {}
|
||||||
@ -563,16 +564,21 @@ class ConnectionState:
|
|||||||
|
|
||||||
def parse_voice_state_update(self, data):
|
def parse_voice_state_update(self, data):
|
||||||
server = self._get_server(data.get('guild_id'))
|
server = self._get_server(data.get('guild_id'))
|
||||||
user_id = data.get('user_id')
|
|
||||||
if server is not None:
|
if server is not None:
|
||||||
if user_id == self.user.id:
|
channel = server.get_channel(data.get('channel_id'))
|
||||||
|
if data.get('user_id') == self.user.id:
|
||||||
voice = self._get_voice_client(server.id)
|
voice = self._get_voice_client(server.id)
|
||||||
if voice is not None:
|
if voice is not None:
|
||||||
voice.channel = server.get_channel(data.get('channel_id'))
|
voice.channel = channel
|
||||||
|
|
||||||
before, after = server._update_voice_state(data)
|
before, after = server._update_voice_state(data)
|
||||||
if after is not None:
|
if after is not None:
|
||||||
self.dispatch('voice_state_update', before, after)
|
self.dispatch('voice_state_update', before, after)
|
||||||
|
else:
|
||||||
|
# in here we're either at private or group calls
|
||||||
|
call = self._calls.get(data.get('channel_id'), None)
|
||||||
|
if call is not None:
|
||||||
|
call._update_voice_state(data)
|
||||||
|
|
||||||
def parse_typing_start(self, data):
|
def parse_typing_start(self, data):
|
||||||
channel = self.get_channel(data.get('channel_id'))
|
channel = self.get_channel(data.get('channel_id'))
|
||||||
@ -592,6 +598,25 @@ class ConnectionState:
|
|||||||
timestamp = datetime.datetime.utcfromtimestamp(data.get('timestamp'))
|
timestamp = datetime.datetime.utcfromtimestamp(data.get('timestamp'))
|
||||||
self.dispatch('typing', channel, member, timestamp)
|
self.dispatch('typing', channel, member, timestamp)
|
||||||
|
|
||||||
|
def parse_call_create(self, data):
|
||||||
|
message = self._get_message(data.get('message_id'))
|
||||||
|
if message is not None:
|
||||||
|
call = GroupCall(message=message, **data)
|
||||||
|
self._calls[data['channel_id']] = call
|
||||||
|
self.dispatch('call', call)
|
||||||
|
|
||||||
|
def parse_call_update(self, data):
|
||||||
|
call = self._calls.get(data.get('channel_id'), None)
|
||||||
|
if call is not None:
|
||||||
|
before = copy.copy(call)
|
||||||
|
call._update(**data)
|
||||||
|
self.dispatch('call_update', before, call)
|
||||||
|
|
||||||
|
def parse_call_delete(self, data):
|
||||||
|
call = self._calls.pop(data.get('channel_id'), None)
|
||||||
|
if call is not None:
|
||||||
|
self.dispatch('call_remove', call)
|
||||||
|
|
||||||
def get_channel(self, id):
|
def get_channel(self, id):
|
||||||
if id is None:
|
if id is None:
|
||||||
return None
|
return None
|
||||||
|
@ -543,6 +543,12 @@ CallMessage
|
|||||||
.. autoclass:: CallMessage
|
.. autoclass:: CallMessage
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
GroupCall
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: GroupCall
|
||||||
|
:members:
|
||||||
|
|
||||||
Server
|
Server
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user