Skip to content

Commit 7f2e278

Browse files
committed
Fix some connection woes
1 parent c474e6a commit 7f2e278

File tree

2 files changed

+52
-7
lines changed

2 files changed

+52
-7
lines changed

discord/gateway.py

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

221221

222222
class VoiceKeepAliveHandler(KeepAliveHandler):
223+
if TYPE_CHECKING:
224+
ws: DiscordVoiceWebSocket
225+
223226
def __init__(self, *args, **kwargs):
224227
super().__init__(*args, **kwargs)
225228
self.recent_ack_latencies: deque[float] = deque(maxlen=20)
@@ -232,7 +235,10 @@ def __init__(self, *args, **kwargs):
232235
def get_payload(self) -> Dict[str, Any]:
233236
return {
234237
'op': self.ws.HEARTBEAT,
235-
'd': int(time.time() * 1000),
238+
'd': {
239+
't': int(time.time() * 1000),
240+
'seq_ack': self.ws.seq_ack,
241+
},
236242
}
237243

238244
def ack(self) -> None:
@@ -657,6 +663,9 @@ async def poll_event(self) -> None:
657663
if isinstance(e, CurlError):
658664
reason = str(e)
659665

666+
if not socket.closed:
667+
await socket.close(code or 4000, (reason or 'Unknown error').encode('utf-8'))
668+
660669
_log.info(f'Gateway received close code {code} and reason {reason!r}.')
661670

662671
if self._can_handle_close(code or None):
@@ -926,6 +935,7 @@ def __init__(
926935
self._keep_alive: Optional[VoiceKeepAliveHandler] = None
927936
self._close_code: Optional[int] = None
928937
self.secret_key: Optional[List[int]] = None
938+
self.seq_ack: int = -1
929939
if hook:
930940
self._hook = hook # type: ignore
931941

@@ -956,6 +966,7 @@ async def resume(self) -> None:
956966
'token': state.token,
957967
'server_id': str(state.server_id),
958968
'session_id': state.session_id,
969+
'seq_ack': self.seq_ack,
959970
},
960971
}
961972
await self.send_as_json(payload)
@@ -980,14 +991,16 @@ async def from_connection_state(
980991
*,
981992
resume: bool = False,
982993
hook: Optional[Callable[..., Coroutine[Any, Any, Any]]] = None,
994+
seq_ack: int = -1
983995
) -> Self:
984996
"""Creates a voice websocket for the :class:`VoiceClient`."""
985-
gateway = f'wss://{state.endpoint}/?v=4'
997+
gateway = f'wss://{state.endpoint}/?v=8'
986998
client = state.voice_client
987999
http = client._state.http
9881000
socket = await http.ws_connect(gateway)
9891001
ws = cls(socket, loop=client.loop, hook=hook)
9901002
ws.gateway = gateway
1003+
ws.seq_ack = seq_ack
9911004
ws._connection = state
9921005
ws._max_heartbeat_timeout = 60.0
9931006
ws.thread_id = threading.get_ident()
@@ -1040,6 +1053,7 @@ async def received_message(self, msg: Dict[str, Any]) -> None:
10401053
_log.debug('Voice socket event: %s.', msg)
10411054
op = msg['op']
10421055
data = msg['d'] # According to Discord this key is always given
1056+
self.seq_ack = msg.get('seq', self.seq_ack)
10431057

10441058
if op == self.READY:
10451059
await self.initial_connection(data)

discord/voice_state.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ async def voice_server_update(self, data: VoiceServerUpdatePayload) -> None:
323323
)
324324
return
325325

326-
self.endpoint, _, _ = endpoint.rpartition(':')
326+
self.endpoint = endpoint
327327
if self.endpoint.startswith('wss://'):
328328
# Just in case, strip it off since we're going to add it later
329329
self.endpoint = self.endpoint[6:]
@@ -603,7 +603,10 @@ async def _voice_disconnect(self) -> None:
603603
self._disconnected.clear()
604604

605605
async def _connect_websocket(self, resume: bool) -> DiscordVoiceWebSocket:
606-
ws = await DiscordVoiceWebSocket.from_connection_state(self, resume=resume, hook=self.hook)
606+
seq_ack = -1
607+
if self.ws is not MISSING:
608+
seq_ack = self.ws.seq_ack
609+
ws = await DiscordVoiceWebSocket.from_connection_state(self, resume=resume, hook=self.hook, seq_ack=seq_ack)
607610
self.state = ConnectionFlowState.websocket_connected
608611
return ws
609612

@@ -632,18 +635,20 @@ async def _poll_voice_ws(self, reconnect: bool) -> None:
632635
# The following close codes are undocumented so I will document them here.
633636
# 1000 - normal closure (obviously)
634637
# 4014 - we were externally disconnected (voice channel deleted, we were moved, etc)
635-
# 4015 - voice server has crashed
638+
# 4015 - voice server has crashed, we should resume
639+
# 4021 - rate limited, we should not reconnect
640+
# 4022 - call terminated, similar to 4014
636641

637642
code = getattr(exc, 'code', self.ws._close_code)
638643

639-
if code in (1000, 4015):
644+
if code == 1000:
640645
# Don't call disconnect a second time if the websocket closed from a disconnect call
641646
if not self._expecting_disconnect:
642647
_log.info('Disconnecting from voice normally, close code %d.', code)
643648
await self.disconnect()
644649
break
645650

646-
if code == 4014:
651+
if code in (4014, 4022):
647652
# We were disconnected by discord
648653
# This condition is a race between the main ws event and the voice ws closing
649654
if self._disconnected.is_set():
@@ -663,6 +668,32 @@ async def _poll_voice_ws(self, reconnect: bool) -> None:
663668
else:
664669
continue
665670

671+
if code == 4021:
672+
_log.warning('We are being rate limited while trying to connect to voice. Disconnecting...')
673+
if self.state is not ConnectionFlowState.disconnected:
674+
await self.disconnect()
675+
break
676+
677+
if exc.code == 4015:
678+
_log.info('Disconnected from voice, attempting a resume...')
679+
try:
680+
await self._connect(
681+
reconnect=reconnect,
682+
timeout=self.timeout,
683+
self_deaf=(self.self_voice_state or self).self_deaf,
684+
self_mute=(self.self_voice_state or self).self_mute,
685+
self_video=(self.self_voice_state or self).self_video,
686+
resume=True,
687+
)
688+
except asyncio.TimeoutError:
689+
_log.info('Could not resume the voice connection. Disconnecting...')
690+
if self.state is not ConnectionFlowState.disconnected:
691+
await self.disconnect()
692+
break
693+
else:
694+
_log.info('Successfully resumed voice connection.')
695+
continue
696+
666697
_log.debug(
667698
'Not handling voice socket close code %s (reason: %s).',
668699
code,

0 commit comments

Comments
 (0)