From c5cf8f048895a433463f0e247c6c096bd3ac138c Mon Sep 17 00:00:00 2001 From: Mikolaj Wojciech Gorski Date: Sat, 26 Jul 2025 18:29:01 +0200 Subject: [PATCH] feat: add voice connection retry logic and diagnostic tools - Add 3-attempt retry logic with exponential backoff for voice connections - Add connection health checking during stream creation - Add specific error messages for different connection failure types - Add 'test voice' command for connectivity diagnostics - Better error handling for VOICE_CONNECTION_TIMEOUT scenarios - Should significantly improve connection reliability --- discord.js-selfbot-v13 | 2 +- src/services/commandHandler.js | 69 ++++++++++++++++++++++++++ src/services/webcamRecording.js | 88 ++++++++++++++++++++++++++++----- 3 files changed, 147 insertions(+), 12 deletions(-) diff --git a/discord.js-selfbot-v13 b/discord.js-selfbot-v13 index 943f9e5..5014823 160000 --- a/discord.js-selfbot-v13 +++ b/discord.js-selfbot-v13 @@ -1 +1 @@ -Subproject commit 943f9e54add4eda8983d83f78e0942ed9e137387 +Subproject commit 5014823f695ddefdeba332450a59dfbc23b98b1b diff --git a/src/services/commandHandler.js b/src/services/commandHandler.js index 06dd863..0041d26 100644 --- a/src/services/commandHandler.js +++ b/src/services/commandHandler.js @@ -398,6 +398,74 @@ class CommandHandler { }, }); + // Test voice connectivity command + this.commands.set("test_voice", { + trigger: (msg) => msg.content.toLowerCase() === "test voice", + execute: async (msg, context) => { + const member = msg.guild?.members.cache.get(msg.author.id); + const voiceChannel = member?.voice?.channel; + + if (!voiceChannel) { + msg.channel.send( + "❌ You need to be in a voice channel to test voice connectivity!" + ); + return null; + } + + msg.channel.send("🔧 Testing voice server connectivity..."); + + try { + const testStart = Date.now(); + const connection = await context.client.voice.joinChannel( + voiceChannel.id, + { + selfMute: true, + selfDeaf: true, + selfVideo: false, + } + ); + + const connectTime = Date.now() - testStart; + + msg.channel.send( + `✅ Voice connection successful!\n` + + `• Connect time: ${connectTime}ms\n` + + `• Voice server: ${connection.voiceServerURL || "Unknown"}\n` + + `• Status: ${connection.status}\n` + + `• Channel: ${voiceChannel.name}` + ); + + // Disconnect after test + setTimeout(() => { + connection.disconnect(); + }, 2000); + + return { + action: { + message: `Voice connectivity test by ${msg.author.tag}`, + channel: `#${msg.channel.name}`, + icon: "🔧", + }, + }; + } catch (error) { + msg.channel.send( + `❌ Voice connection failed!\n` + + `• Error: ${error.message}\n` + + `• This may indicate Discord voice server issues\n` + + `• Try again in a few minutes` + ); + + return { + action: { + message: `Voice connectivity test failed by ${msg.author.tag}`, + channel: `#${msg.channel.name}`, + icon: "❌", + }, + }; + } + }, + }); + // Help command this.commands.set("help", { trigger: (msg) => @@ -419,6 +487,7 @@ class CommandHandler { "`teto status` - Show overall recording status", "`webcam status` - Show detailed webcam status", "`debug voice` - Show voice state debug info", + "`test voice` - Test voice server connectivity", "`teto help` or `help` - Show this help message", "", "**💬 General:**", diff --git a/src/services/webcamRecording.js b/src/services/webcamRecording.js index 384c801..8e76e27 100644 --- a/src/services/webcamRecording.js +++ b/src/services/webcamRecording.js @@ -44,15 +44,43 @@ class WebcamRecordingService { `[Docker] Attempting to join voice channel for webcam recording: ${voiceChannel.name}` ); - // Join the voice channel - const connection = await client.voice.joinChannel( - voiceChannel.id, - VIDEO_CONFIG.VOICE_SETTINGS - ); + // Join the voice channel with retry logic + let connection; + let connectionRetries = 0; + const maxConnectionRetries = 3; - console.log( - `[Docker] Successfully joined voice channel for webcam recording` - ); + while (connectionRetries < maxConnectionRetries) { + try { + connection = await client.voice.joinChannel( + voiceChannel.id, + VIDEO_CONFIG.VOICE_SETTINGS + ); + console.log( + `[Docker] Successfully joined voice channel for webcam recording` + ); + break; + } catch (error) { + connectionRetries++; + console.log( + `[Docker] Voice connection failed (attempt ${connectionRetries}/${maxConnectionRetries}):`, + error.message + ); + + if (connectionRetries === maxConnectionRetries) { + throw new Error( + `Failed to join voice channel after ${maxConnectionRetries} attempts: ${error.message}` + ); + } + + // Wait before retry with exponential backoff + const delay = Math.min( + 5000 * Math.pow(2, connectionRetries - 1), + 30000 + ); + console.log(`[Docker] Retrying voice connection in ${delay}ms...`); + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } // Wait longer for connection and SSRC mapping to be fully established await new Promise((resolve) => setTimeout(resolve, 8000)); @@ -142,8 +170,8 @@ class WebcamRecordingService { // Wait before retry and check connection health await new Promise((resolve) => setTimeout(resolve, 2000)); - // Check if connection is still alive - if (connection.status !== "connected") { + // Check if connection is still healthy + if (!this._isConnectionHealthy(connection)) { console.log( `[Docker] Connection status changed to: ${connection.status}` ); @@ -197,7 +225,7 @@ class WebcamRecordingService { } catch (error) { console.error("[Docker] Error starting webcam recording:", error); - // Handle specific webcam errors + // Handle specific error types if (error.message === "VOICE_USER_NO_WEBCAM") { return { success: false, @@ -206,6 +234,32 @@ class WebcamRecordingService { }; } + if (error.message.includes("VOICE_CONNECTION_TIMEOUT")) { + return { + success: false, + message: + "❌ Voice connection timed out. Discord voice servers may be slow or unavailable.", + error: "VOICE_CONNECTION_TIMEOUT", + }; + } + + if (error.message.includes("Failed to join voice channel")) { + return { + success: false, + message: + "❌ Could not connect to voice channel after multiple attempts. Try again later.", + error: "VOICE_CONNECTION_FAILED", + }; + } + + if (error.message.includes("Voice connection lost")) { + return { + success: false, + message: "❌ Voice connection was lost during setup.", + error: "VOICE_CONNECTION_LOST", + }; + } + return { success: false, message: "Failed to start webcam recording", @@ -595,6 +649,18 @@ class WebcamRecordingService { ); } } + + /** + * Check if voice connection is healthy + * @param {Object} connection - Voice connection object + * @returns {boolean} Whether connection is healthy + */ + _isConnectionHealthy(connection) { + if (!connection) return false; + + const healthyStatuses = ["connected", "connecting", "ready"]; + return healthyStatuses.includes(connection.status); + } } // Export singleton instance