From 75aa17c18819823b57c5436f358dda3f0204a496 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:07:41 +0200 Subject: [PATCH 1/7] fix voice issues --- CHANGELOG.md | 2 ++ discord/gateway.py | 19 ++++++++++++++++--- discord/voice_client.py | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdfdc6d896..e1e63d69ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2781](https://github.com/Pycord-Development/pycord/pull/2781)) - Fixed `VoiceClient` crashing randomly while receiving audio ([#2800](https://github.com/Pycord-Development/pycord/pull/2800)) +- Fixed `VoiceClient.connect` failing to do initial connection. + ([#](https://github.com/Pycord-Development/pycord/pull/)) ### Changed diff --git a/discord/gateway.py b/discord/gateway.py index 4af59f3864..65942f67cc 100644 --- a/discord/gateway.py +++ b/discord/gateway.py @@ -35,6 +35,7 @@ import traceback import zlib from collections import deque, namedtuple +from typing import TYPE_CHECKING import aiohttp @@ -208,6 +209,9 @@ def ack(self): class VoiceKeepAliveHandler(KeepAliveHandler): + if TYPE_CHECKING: + ws: DiscordVoiceWebSocket + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.recent_ack_latencies = deque(maxlen=20) @@ -216,7 +220,7 @@ def __init__(self, *args, **kwargs): self.behind_msg = "High socket latency, shard ID %s heartbeat is %.1fs behind" def get_payload(self): - return {"op": self.ws.HEARTBEAT, "d": int(time.time() * 1000)} + return {"op": self.ws.HEARTBEAT, "d": {"t": int(time.time() * 1000), "seq_ack": self.ws.seq_ack}} def ack(self): ack_time = time.perf_counter() @@ -784,6 +788,7 @@ def __init__(self, socket, loop, *, hook=None): self._close_code = None self.secret_key = None self.ssrc_map = {} + self.seq_ack: int = -1 if hook: self._hook = hook @@ -824,7 +829,7 @@ async def identify(self): @classmethod async def from_client(cls, client, *, resume=False, hook=None): """Creates a voice websocket for the :class:`VoiceClient`.""" - gateway = f"wss://{client.endpoint}/?v=4" + gateway = f"wss://{client.endpoint}/?v=8" http = client._state.http socket = await http.ws_connect(gateway, compress=15) ws = cls(socket, loop=client.loop, hook=hook) @@ -860,7 +865,14 @@ async def client_connect(self): await self.send_as_json(payload) async def speak(self, state=SpeakingState.voice): - payload = {"op": self.SPEAKING, "d": {"speaking": int(state), "delay": 0}} + payload = { + "op": self.SPEAKING, + "d": { + "speaking": int(state), + "delay": 0, + }, + "seq": self.seq_ack, + } await self.send_as_json(payload) @@ -868,6 +880,7 @@ async def received_message(self, msg): _log.debug("Voice websocket frame received: %s", msg) op = msg["op"] data = msg.get("d") + self.seq_ack = data.get("seq", self.seq_ack) if op == self.READY: await self.initial_connection(data) diff --git a/discord/voice_client.py b/discord/voice_client.py index 75902ecbe2..c93ea085a4 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -325,7 +325,7 @@ async def on_voice_server_update(self, data: VoiceServerUpdatePayload) -> None: ) return - self.endpoint, _, _ = endpoint.rpartition(":") + self.endpoint = endpoint if self.endpoint.startswith("wss://"): # Just in case, strip it off since we're going to add it later self.endpoint = self.endpoint[6:] From 526447f6a6b272acd995800fc175984d1400223f Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:08:45 +0200 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e63d69ee..400e3606d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,7 +120,7 @@ These changes are available on the `master` branch, but have not yet been releas - Fixed `VoiceClient` crashing randomly while receiving audio ([#2800](https://github.com/Pycord-Development/pycord/pull/2800)) - Fixed `VoiceClient.connect` failing to do initial connection. - ([#](https://github.com/Pycord-Development/pycord/pull/)) + ([#2812](https://github.com/Pycord-Development/pycord/pull/2812)) ### Changed From 57945dcd9452da4ecf6d0a2037fcd06e317b2bb8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:08:54 +0000 Subject: [PATCH 3/7] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/gateway.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/discord/gateway.py b/discord/gateway.py index 65942f67cc..c9f254ab65 100644 --- a/discord/gateway.py +++ b/discord/gateway.py @@ -220,7 +220,10 @@ def __init__(self, *args, **kwargs): self.behind_msg = "High socket latency, shard ID %s heartbeat is %.1fs behind" def get_payload(self): - return {"op": self.ws.HEARTBEAT, "d": {"t": int(time.time() * 1000), "seq_ack": self.ws.seq_ack}} + return { + "op": self.ws.HEARTBEAT, + "d": {"t": int(time.time() * 1000), "seq_ack": self.ws.seq_ack}, + } def ack(self): ack_time = time.perf_counter() From c2ba7218482875c8d705a237a067d1415791c621 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:23:19 +0200 Subject: [PATCH 4/7] replace if block with str.removeprefix --- discord/voice_client.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/discord/voice_client.py b/discord/voice_client.py index c93ea085a4..0dfefc0293 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -325,11 +325,7 @@ async def on_voice_server_update(self, data: VoiceServerUpdatePayload) -> None: ) return - self.endpoint = endpoint - if self.endpoint.startswith("wss://"): - # Just in case, strip it off since we're going to add it later - self.endpoint = self.endpoint[6:] - + self.endpoint = endpoint.removeprefix("wss://") # This gets set later self.endpoint_ip = MISSING From b7c5714472441323ad3b57d4941c59820290421a Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Wed, 18 Jun 2025 12:39:34 +0200 Subject: [PATCH 5/7] add buffered resume --- discord/gateway.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/gateway.py b/discord/gateway.py index c9f254ab65..e968bc9858 100644 --- a/discord/gateway.py +++ b/discord/gateway.py @@ -812,6 +812,9 @@ async def resume(self): "token": state.token, "server_id": str(state.server_id), "session_id": state.session_id, + # this seq_ack will allow for us to do buffered resume, which is, receive the + # lost voice packets while trying to resume the reconnection + "seq_ack": self.seq_ack, }, } await self.send_as_json(payload) @@ -874,7 +877,6 @@ async def speak(self, state=SpeakingState.voice): "speaking": int(state), "delay": 0, }, - "seq": self.seq_ack, } await self.send_as_json(payload) From d8eff4f3734c4d7ce33e43722945103bf266e211 Mon Sep 17 00:00:00 2001 From: DA-344 <108473820+DA-344@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:32:53 +0200 Subject: [PATCH 6/7] chore: add buffered resume --- discord/voice_client.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/discord/voice_client.py b/discord/voice_client.py index 0dfefc0293..9179b2891b 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -467,8 +467,8 @@ async def poll_voice_ws(self, reconnect: bool) -> None: # The following close codes are undocumented, so I will document them here. # 1000 - normal closure (obviously) # 4014 - voice channel has been deleted. - # 4015 - voice server has crashed - if exc.code in (1000, 4015): + # 4015 - voice server has crashed, we should resume + if exc.code == 1000: _log.info( "Disconnecting from voice normally, close code %d.", exc.code, @@ -490,6 +490,19 @@ async def poll_voice_ws(self, reconnect: bool) -> None: ) await self.disconnect() break + if exc.code == 4015: + _log.info("Disconnected from voice, trying to resume...") + + try: + await self.ws.resume() + except asyncio.TimeoutError: + _log.info("Could not resume the voice connection... Disconnection...") + if self._connected.is_set(): + await self.disconnect(force=True) + else: + _log.info("Successfully resumed voice connection") + continue + if not reconnect: await self.disconnect() raise From 32c2da3d08e622ff01eb6a75d22eedbbe9320289 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:34:44 +0000 Subject: [PATCH 7/7] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/voice_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/voice_client.py b/discord/voice_client.py index 9179b2891b..3ed983e2b6 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -496,7 +496,9 @@ async def poll_voice_ws(self, reconnect: bool) -> None: try: await self.ws.resume() except asyncio.TimeoutError: - _log.info("Could not resume the voice connection... Disconnection...") + _log.info( + "Could not resume the voice connection... Disconnection..." + ) if self._connected.is_set(): await self.disconnect(force=True) else: