aboutsummaryrefslogtreecommitdiffstats
path: root/cogs/voice.py
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-05-20 17:08:40 +0200
committersalmonik <raisersoft@proton.me>2026-05-20 17:08:40 +0200
commit8d35930871db37b1009275c423ce95ed72954613 (patch)
tree1372be685f4eb8764c316c06586b18d20732bc40 /cogs/voice.py
parentRedirect play_commands imports to cogs.voice, remove audio_playback shim (diff)
downloadsenator-sperma-8d35930871db37b1009275c423ce95ed72954613.tar.gz
senator-sperma-8d35930871db37b1009275c423ce95ed72954613.zip
Fix stale-connection reconnect hang and TTS temp-file collision
- _ensure_voice_client: call _remove_voice_client() when cleaning up stale or disconnected clients so the fresh connect doesn't hang for 30s waiting for Discord's stale state to clear - Reduce connect timeout from 30s to 10s for faster failure recovery - vclogs.py: use a unique temp filename per TTS call (include timestamp) instead of relying on PID alone, preventing concurrent TTS calls from clobbering each other's files
Diffstat (limited to 'cogs/voice.py')
-rw-r--r--cogs/voice.py30
1 files changed, 26 insertions, 4 deletions
diff --git a/cogs/voice.py b/cogs/voice.py
index 86416ef..f079c6a 100644
--- a/cogs/voice.py
+++ b/cogs/voice.py
@@ -84,6 +84,12 @@ class VoiceManager:
84 await vc.disconnect() 84 await vc.disconnect()
85 except Exception: 85 except Exception:
86 pass 86 pass
87 # Forcefully clear stale internal state so the fresh
88 # connect doesn't hang for 30s waiting for Discord.
89 try:
90 channel._state._remove_voice_client(guild.id)
91 except Exception:
92 pass
87 vc = None 93 vc = None
88 else: 94 else:
89 try: 95 try:
@@ -103,6 +109,10 @@ class VoiceManager:
103 await vc.disconnect() 109 await vc.disconnect()
104 except Exception: 110 except Exception:
105 pass 111 pass
112 try:
113 channel._state._remove_voice_client(guild.id)
114 except Exception:
115 pass
106 vc = None 116 vc = None
107 117
108 # --- create fresh connection -------------------------------------- 118 # --- create fresh connection --------------------------------------
@@ -110,7 +120,7 @@ class VoiceManager:
110 try: 120 try:
111 await asyncio.sleep(0.3) 121 await asyncio.sleep(0.3)
112 try: 122 try:
113 vc = await channel.connect(timeout=30.0, reconnect=False) 123 vc = await channel.connect(timeout=10.0, reconnect=False)
114 logger.info(f"Connected to voice channel: {channel}") 124 logger.info(f"Connected to voice channel: {channel}")
115 return vc 125 return vc
116 except discord.ClientException as e: 126 except discord.ClientException as e:
@@ -129,7 +139,7 @@ class VoiceManager:
129 pass 139 pass
130 await asyncio.sleep(0.3) 140 await asyncio.sleep(0.3)
131 try: 141 try:
132 vc = await channel.connect(timeout=30.0, reconnect=False) 142 vc = await channel.connect(timeout=10.0, reconnect=False)
133 logger.info(f"Reconnected to voice channel: {channel}") 143 logger.info(f"Reconnected to voice channel: {channel}")
134 return vc 144 return vc
135 except Exception as e2: 145 except Exception as e2:
@@ -346,6 +356,9 @@ class VoiceManager:
346 async def play(self, ctx, source, delete_after_play=False, interrupt=True): 356 async def play(self, ctx, source, delete_after_play=False, interrupt=True):
347 """High-level playback: validate context, prepare source, connect, play, cleanup. 357 """High-level playback: validate context, prepare source, connect, play, cleanup.
348 358
359 Sends ``grom: {name}`` immediately on start, and an error follow-up
360 if playback fails.
361
349 Args: 362 Args:
350 ctx: Discord application context (used for channel resolution & error messages). 363 ctx: Discord application context (used for channel resolution & error messages).
351 source: Local file path or URL to stream. 364 source: Local file path or URL to stream.
@@ -377,14 +390,23 @@ class VoiceManager:
377 prepared_source, is_url, display_name 390 prepared_source, is_url, display_name
378 ) 391 )
379 392
393 try:
394 await ctx.followup.send(f"grom: {final_display_name}")
395 except Exception as e:
396 logger.error(e)
397
380 success = await self._play_await(vc, audio_source) 398 success = await self._play_await(vc, audio_source)
381 399
382 if success: 400 if success:
383 logger.info(f"Playback completed: {final_display_name}") 401 logger.info(f"Playback completed: {final_display_name}")
384 else: 402 else:
385 logger.error(f"Playback failed: {prepared_source}") 403 logger.error(f"Playback failed: {prepared_source}")
386 404 try:
387 if not success: 405 await ctx.followup.send(
406 "eror: Timeout - nie udało się połączyć ze streamem", ephemeral=True
407 )
408 except Exception:
409 pass
388 await VoiceManager.cleanup_if_needed(prepared_source, delete_after_play, is_url) 410 await VoiceManager.cleanup_if_needed(prepared_source, delete_after_play, is_url)
389 411
390 return success 412 return success