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
This commit is contained in:
parent
6b5b3d1867
commit
c5cf8f0488
3 changed files with 147 additions and 12 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit 943f9e54add4eda8983d83f78e0942ed9e137387
|
Subproject commit 5014823f695ddefdeba332450a59dfbc23b98b1b
|
||||||
|
|
@ -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
|
// Help command
|
||||||
this.commands.set("help", {
|
this.commands.set("help", {
|
||||||
trigger: (msg) =>
|
trigger: (msg) =>
|
||||||
|
|
@ -419,6 +487,7 @@ class CommandHandler {
|
||||||
"`teto status` - Show overall recording status",
|
"`teto status` - Show overall recording status",
|
||||||
"`webcam status` - Show detailed webcam status",
|
"`webcam status` - Show detailed webcam status",
|
||||||
"`debug voice` - Show voice state debug info",
|
"`debug voice` - Show voice state debug info",
|
||||||
|
"`test voice` - Test voice server connectivity",
|
||||||
"`teto help` or `help` - Show this help message",
|
"`teto help` or `help` - Show this help message",
|
||||||
"",
|
"",
|
||||||
"**💬 General:**",
|
"**💬 General:**",
|
||||||
|
|
|
||||||
|
|
@ -44,15 +44,43 @@ class WebcamRecordingService {
|
||||||
`[Docker] Attempting to join voice channel for webcam recording: ${voiceChannel.name}`
|
`[Docker] Attempting to join voice channel for webcam recording: ${voiceChannel.name}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Join the voice channel
|
// Join the voice channel with retry logic
|
||||||
const connection = await client.voice.joinChannel(
|
let connection;
|
||||||
voiceChannel.id,
|
let connectionRetries = 0;
|
||||||
VIDEO_CONFIG.VOICE_SETTINGS
|
const maxConnectionRetries = 3;
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
while (connectionRetries < maxConnectionRetries) {
|
||||||
`[Docker] Successfully joined voice channel for webcam recording`
|
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
|
// Wait longer for connection and SSRC mapping to be fully established
|
||||||
await new Promise((resolve) => setTimeout(resolve, 8000));
|
await new Promise((resolve) => setTimeout(resolve, 8000));
|
||||||
|
|
@ -142,8 +170,8 @@ class WebcamRecordingService {
|
||||||
// Wait before retry and check connection health
|
// Wait before retry and check connection health
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
// Check if connection is still alive
|
// Check if connection is still healthy
|
||||||
if (connection.status !== "connected") {
|
if (!this._isConnectionHealthy(connection)) {
|
||||||
console.log(
|
console.log(
|
||||||
`[Docker] Connection status changed to: ${connection.status}`
|
`[Docker] Connection status changed to: ${connection.status}`
|
||||||
);
|
);
|
||||||
|
|
@ -197,7 +225,7 @@ class WebcamRecordingService {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[Docker] Error starting webcam recording:", error);
|
console.error("[Docker] Error starting webcam recording:", error);
|
||||||
|
|
||||||
// Handle specific webcam errors
|
// Handle specific error types
|
||||||
if (error.message === "VOICE_USER_NO_WEBCAM") {
|
if (error.message === "VOICE_USER_NO_WEBCAM") {
|
||||||
return {
|
return {
|
||||||
success: false,
|
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 {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Failed to start webcam recording",
|
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
|
// Export singleton instance
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue