aboutsummaryrefslogtreecommitdiff
path: root/discord/gateway.py
diff options
context:
space:
mode:
Diffstat (limited to 'discord/gateway.py')
-rw-r--r--discord/gateway.py49
1 files changed, 38 insertions, 11 deletions
diff --git a/discord/gateway.py b/discord/gateway.py
index eb17c2ef..83699a44 100644
--- a/discord/gateway.py
+++ b/discord/gateway.py
@@ -38,6 +38,7 @@ import websockets
from . import utils
from .activity import _ActivityTag
+from .enums import SpeakingState
from .errors import ConnectionClosed, InvalidArgument
log = logging.getLogger(__name__)
@@ -547,6 +548,10 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
Receive only. Tells you that your websocket connection was acknowledged.
INVALIDATE_SESSION
Sent only. Tells you that your RESUME request has failed and to re-IDENTIFY.
+ CLIENT_CONNECT
+ Indicates a user has connected to voice.
+ CLIENT_DISCONNECT
+ Receive only. Indicates a user has disconnected from voice.
"""
IDENTIFY = 0
@@ -559,6 +564,8 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
RESUME = 7
HELLO = 8
INVALIDATE_SESSION = 9
+ CLIENT_CONNECT = 12
+ CLIENT_DISCONNECT = 13
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -597,7 +604,7 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
@classmethod
async def from_client(cls, client, *, resume=False):
"""Creates a voice websocket for the :class:`VoiceClient`."""
- gateway = 'wss://' + client.endpoint + '/?v=3'
+ gateway = 'wss://' + client.endpoint + '/?v=4'
ws = await websockets.connect(gateway, loop=client.loop, klass=cls, compression=None)
ws.gateway = gateway
ws._connection = client
@@ -610,7 +617,7 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
return ws
- async def select_protocol(self, ip, port):
+ async def select_protocol(self, ip, port, mode):
payload = {
'op': self.SELECT_PROTOCOL,
'd': {
@@ -618,18 +625,28 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
'data': {
'address': ip,
'port': port,
- 'mode': 'xsalsa20_poly1305'
+ 'mode': mode
}
}
}
await self.send_as_json(payload)
- async def speak(self, is_speaking=True):
+ async def client_connect(self):
+ payload = {
+ 'op': self.CLIENT_CONNECT,
+ 'd': {
+ 'audio_ssrc': self._connection.ssrc
+ }
+ }
+
+ await self.send_as_json(payload)
+
+ async def speak(self, state=SpeakingState.voice):
payload = {
'op': self.SPEAKING,
'd': {
- 'speaking': is_speaking,
+ 'speaking': int(state),
'delay': 0
}
}
@@ -642,9 +659,6 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
data = msg.get('d')
if op == self.READY:
- interval = data['heartbeat_interval'] / 1000.0
- self._keep_alive = VoiceKeepAliveHandler(ws=self, interval=interval)
- self._keep_alive.start()
await self.initial_connection(data)
elif op == self.HEARTBEAT_ACK:
self._keep_alive.ack()
@@ -652,7 +666,12 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
log.info('Voice RESUME failed.')
await self.identify()
elif op == self.SESSION_DESCRIPTION:
+ self._connection.mode = data['mode']
await self.load_secret_key(data)
+ elif op == self.HELLO:
+ interval = data['heartbeat_interval'] / 1000.0
+ self._keep_alive = VoiceKeepAliveHandler(ws=self, interval=interval)
+ self._keep_alive.start()
async def initial_connection(self, data):
state = self._connection
@@ -673,15 +692,23 @@ class DiscordVoiceWebSocket(websockets.client.WebSocketClientProtocol):
# the port is a little endian unsigned short in the last two bytes
# yes, this is different endianness from everything else
state.port = struct.unpack_from('<H', recv, len(recv) - 2)[0]
-
log.debug('detected ip: %s port: %s', state.ip, state.port)
- await self.select_protocol(state.ip, state.port)
- log.info('selected the voice protocol for use')
+
+ # there *should* always be at least one supported mode (xsalsa20_poly1305)
+ modes = [mode for mode in data['modes'] if mode in self._connection.supported_modes]
+ log.debug('received supported encryption modes: %s', ", ".join(modes))
+
+ mode = modes[0]
+ await self.select_protocol(state.ip, state.port, mode)
+ log.info('selected the voice protocol for use (%s)', mode)
+
+ await self.client_connect()
async def load_secret_key(self, data):
log.info('received secret key for voice connection')
self._connection.secret_key = data.get('secret_key')
await self.speak()
+ await self.speak(False)
async def poll_event(self):
try: