From 8d35930871db37b1009275c423ce95ed72954613 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 20 May 2026 17:08:40 +0200 Subject: 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 --- cogs/vclogs.py | 2 +- cogs/voice.py | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cogs/vclogs.py b/cogs/vclogs.py index 2e853ad..01a428d 100644 --- a/cogs/vclogs.py +++ b/cogs/vclogs.py @@ -116,7 +116,7 @@ class VcLogs(commands.Cog): voice_mgr = self.get_voice_manager() temp_dir = get_temp_dir() - tts_file = os.path.join(temp_dir, f"temp_tts_{os.getpid()}.mp3") + tts_file = os.path.join(temp_dir, f"temp_tts_{os.getpid()}_{asyncio.get_event_loop().time()}.mp3") tts = gTTS(text=text, lang='pl') logger.info("saving file") 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: await vc.disconnect() except Exception: pass + # Forcefully clear stale internal state so the fresh + # connect doesn't hang for 30s waiting for Discord. + try: + channel._state._remove_voice_client(guild.id) + except Exception: + pass vc = None else: try: @@ -103,6 +109,10 @@ class VoiceManager: await vc.disconnect() except Exception: pass + try: + channel._state._remove_voice_client(guild.id) + except Exception: + pass vc = None # --- create fresh connection -------------------------------------- @@ -110,7 +120,7 @@ class VoiceManager: try: await asyncio.sleep(0.3) try: - vc = await channel.connect(timeout=30.0, reconnect=False) + vc = await channel.connect(timeout=10.0, reconnect=False) logger.info(f"Connected to voice channel: {channel}") return vc except discord.ClientException as e: @@ -129,7 +139,7 @@ class VoiceManager: pass await asyncio.sleep(0.3) try: - vc = await channel.connect(timeout=30.0, reconnect=False) + vc = await channel.connect(timeout=10.0, reconnect=False) logger.info(f"Reconnected to voice channel: {channel}") return vc except Exception as e2: @@ -346,6 +356,9 @@ class VoiceManager: async def play(self, ctx, source, delete_after_play=False, interrupt=True): """High-level playback: validate context, prepare source, connect, play, cleanup. + Sends ``grom: {name}`` immediately on start, and an error follow-up + if playback fails. + Args: ctx: Discord application context (used for channel resolution & error messages). source: Local file path or URL to stream. @@ -377,14 +390,23 @@ class VoiceManager: prepared_source, is_url, display_name ) + try: + await ctx.followup.send(f"grom: {final_display_name}") + except Exception as e: + logger.error(e) + success = await self._play_await(vc, audio_source) if success: logger.info(f"Playback completed: {final_display_name}") else: logger.error(f"Playback failed: {prepared_source}") - - if not success: + try: + await ctx.followup.send( + "eror: Timeout - nie udało się połączyć ze streamem", ephemeral=True + ) + except Exception: + pass await VoiceManager.cleanup_if_needed(prepared_source, delete_after_play, is_url) return success -- cgit v1.2.3