Skip to content

Commit 2175bd5

Browse files
authored
Fix voice connection issues and upgrade to voice v8
1 parent 20055e7 commit 2175bd5

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

discord/gateway.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ def ack(self) -> None:
212212

213213

214214
class VoiceKeepAliveHandler(KeepAliveHandler):
215+
if TYPE_CHECKING:
216+
ws: DiscordVoiceWebSocket
217+
215218
def __init__(self, *args: Any, **kwargs: Any) -> None:
216219
name: str = kwargs.pop('name', f'voice-keep-alive-handler:{id(self):#x}')
217220
super().__init__(*args, name=name, **kwargs)
@@ -223,7 +226,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
223226
def get_payload(self) -> Dict[str, Any]:
224227
return {
225228
'op': self.ws.HEARTBEAT,
226-
'd': int(time.time() * 1000),
229+
'd': {
230+
't': int(time.time() * 1000),
231+
'seq_ack': self.ws.seq_ack,
232+
},
227233
}
228234

229235
def ack(self) -> None:
@@ -830,6 +836,8 @@ def __init__(
830836
self._keep_alive: Optional[VoiceKeepAliveHandler] = None
831837
self._close_code: Optional[int] = None
832838
self.secret_key: Optional[List[int]] = None
839+
# defaulting to -1
840+
self.seq_ack: int = -1
833841
if hook:
834842
self._hook = hook # type: ignore
835843

@@ -850,6 +858,7 @@ async def resume(self) -> None:
850858
'token': state.token,
851859
'server_id': str(state.server_id),
852860
'session_id': state.session_id,
861+
'seq_ack': self.seq_ack,
853862
},
854863
}
855864
await self.send_as_json(payload)
@@ -874,14 +883,16 @@ async def from_connection_state(
874883
*,
875884
resume: bool = False,
876885
hook: Optional[Callable[..., Coroutine[Any, Any, Any]]] = None,
886+
seq_ack: int = -1,
877887
) -> Self:
878888
"""Creates a voice websocket for the :class:`VoiceClient`."""
879-
gateway = f'wss://{state.endpoint}/?v=4'
889+
gateway = f'wss://{state.endpoint}/?v=8'
880890
client = state.voice_client
881891
http = client._state.http
882892
socket = await http.ws_connect(gateway, compress=15)
883893
ws = cls(socket, loop=client.loop, hook=hook)
884894
ws.gateway = gateway
895+
ws.seq_ack = seq_ack
885896
ws._connection = state
886897
ws._max_heartbeat_timeout = 60.0
887898
ws.thread_id = threading.get_ident()
@@ -934,6 +945,7 @@ async def received_message(self, msg: Dict[str, Any]) -> None:
934945
_log.debug('Voice websocket frame received: %s', msg)
935946
op = msg['op']
936947
data = msg['d'] # According to Discord this key is always given
948+
self.seq_ack = msg.get('seq', self.seq_ack) # this key could not be given
937949

938950
if op == self.READY:
939951
await self.initial_connection(data)

discord/voice_state.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ async def voice_server_update(self, data: VoiceServerUpdatePayload) -> None:
321321
)
322322
return
323323

324-
self.endpoint, _, _ = endpoint.rpartition(':')
324+
self.endpoint = endpoint
325325
if self.endpoint.startswith('wss://'):
326326
# Just in case, strip it off since we're going to add it later
327327
self.endpoint = self.endpoint[6:]
@@ -574,7 +574,10 @@ async def _voice_disconnect(self) -> None:
574574
self._disconnected.clear()
575575

576576
async def _connect_websocket(self, resume: bool) -> DiscordVoiceWebSocket:
577-
ws = await DiscordVoiceWebSocket.from_connection_state(self, resume=resume, hook=self.hook)
577+
seq_ack = -1
578+
if self.ws is not MISSING:
579+
seq_ack = self.ws.seq_ack
580+
ws = await DiscordVoiceWebSocket.from_connection_state(self, resume=resume, hook=self.hook, seq_ack=seq_ack)
578581
self.state = ConnectionFlowState.websocket_connected
579582
return ws
580583

@@ -603,15 +606,17 @@ async def _poll_voice_ws(self, reconnect: bool) -> None:
603606
# The following close codes are undocumented so I will document them here.
604607
# 1000 - normal closure (obviously)
605608
# 4014 - we were externally disconnected (voice channel deleted, we were moved, etc)
606-
# 4015 - voice server has crashed
607-
if exc.code in (1000, 4015):
609+
# 4015 - voice server has crashed, we should resume
610+
# 4021 - rate limited, we should not reconnect
611+
# 4022 - call terminated, similar to 4014
612+
if exc.code == 1000:
608613
# Don't call disconnect a second time if the websocket closed from a disconnect call
609614
if not self._expecting_disconnect:
610615
_log.info('Disconnecting from voice normally, close code %d.', exc.code)
611616
await self.disconnect()
612617
break
613618

614-
if exc.code == 4014:
619+
if exc.code in (4014, 4022):
615620
# We were disconnected by discord
616621
# This condition is a race between the main ws event and the voice ws closing
617622
if self._disconnected.is_set():
@@ -631,6 +636,31 @@ async def _poll_voice_ws(self, reconnect: bool) -> None:
631636
else:
632637
continue
633638

639+
if exc.code == 4021:
640+
_log.warning('We are being ratelimited while trying to connect to voice. Disconnecting...')
641+
if self.state is not ConnectionFlowState.disconnected:
642+
await self.disconnect()
643+
break
644+
645+
if exc.code == 4015:
646+
_log.info('Disconnected from voice, attempting a resume...')
647+
try:
648+
await self._connect(
649+
reconnect=reconnect,
650+
timeout=self.timeout,
651+
self_deaf=(self.self_voice_state or self).self_deaf,
652+
self_mute=(self.self_voice_state or self).self_mute,
653+
resume=True,
654+
)
655+
except asyncio.TimeoutError:
656+
_log.info('Could not resume the voice connection... Disconnecting...')
657+
if self.state is not ConnectionFlowState.disconnected:
658+
await self.disconnect()
659+
break
660+
else:
661+
_log.info('Successfully resumed voice connection')
662+
continue
663+
634664
_log.debug('Not handling close code %s (%s)', exc.code, exc.reason or 'no reason')
635665

636666
if not reconnect:

0 commit comments

Comments
 (0)