From 0cc784068d5acff9d15dba59e293733946ed854c Mon Sep 17 00:00:00 2001 From: 0xbyt4 <35742124+0xbyt4@users.noreply.github.com> Date: Sun, 15 Mar 2026 00:49:38 +0300 Subject: [PATCH] fix(voice): add UDP keepalive to prevent Discord dropping voice after silence Discord drops the UDP voice route after ~60s of silence - no packets arrive even when users start speaking again. Send an Opus silence frame every 15s to keep the UDP session alive. --- gateway/platforms/discord.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index 203f6535ed..8bb6f59ac0 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -1002,14 +1002,32 @@ class DiscordAdapter(BasePlatformAdapter): # Voice listening (Phase 2) # ------------------------------------------------------------------ + # UDP keepalive interval in seconds — prevents Discord from dropping + # the UDP route after ~60s of silence. + _KEEPALIVE_INTERVAL = 15 + async def _voice_listen_loop(self, guild_id: int): """Periodically check for completed utterances and process them.""" receiver = self._voice_receivers.get(guild_id) if not receiver: return + last_keepalive = time.monotonic() try: while receiver._running: await asyncio.sleep(0.2) + + # Send periodic UDP keepalive to prevent Discord from + # dropping the UDP session after ~60s of silence. + now = time.monotonic() + if now - last_keepalive >= self._KEEPALIVE_INTERVAL: + last_keepalive = now + try: + vc = self._voice_clients.get(guild_id) + if vc and vc.is_connected(): + vc._connection.send_packet(b'\xf8\xff\xfe') + except Exception: + pass + completed = receiver.check_silence() for user_id, pcm_data in completed: if not self._is_allowed_user(str(user_id)):