@getsolara/solara.voice
Version:
Optional voice functionality for @getsolara/solara.js using @discordjs/voice
68 lines (65 loc) • 4.02 kB
JavaScript
const { joinVoiceChannel, VoiceConnectionStatus, entersState } = require('@discordjs/voice');
module.exports = {
name: "$voiceJoin",
description: "Makes the bot join the user's current voice channel or a specified channel. Args: [channelID]",
takesBrackets: true,
execute: async (context, args) => {
if (context.client.voiceInitialized === false) {
return "[Error: $voiceJoin requires voice features to be enabled. Ensure @getsolara/solara.voice is installed and configured correctly.]";
}
if (!context.guild) return "[Error: $voiceJoin can only be used in a server.]";
const targetChannelId = args[0]?.trim() || context.member?.voice?.channelId;
if (!targetChannelId) return "[Error: You are not in a voice channel, and no channel ID was provided to $voiceJoin.]";
let voiceChannel;
try {
voiceChannel = await context.client.channels.fetch(targetChannelId);
} catch {
return `[Error: Could not find channel with ID ${targetChannelId} for $voiceJoin.]`;
}
if (!voiceChannel || !voiceChannel.isVoiceBased()) return `[Error: Channel ${targetChannelId} is not a voice channel for $voiceJoin.]`;
if (!voiceChannel.joinable) return `[Error: Bot does not have permission to join voice channel: ${voiceChannel.name}]`;
if (!voiceChannel.speakable && context.client.solaraOptions?.verboseStartupLogging) {
console.warn(`Solara.voice ($voiceJoin): Bot might lack SPEAK permissions in channel ${voiceChannel.name}.`);
}
try {
const existingConnection = context.client.solaraVoiceConnections?.get(context.guild.id);
if (existingConnection) {
if (existingConnection.joinConfig.channelId === voiceChannel.id && existingConnection.state.status === VoiceConnectionStatus.Ready) {
return `Already connected to: ${voiceChannel.name}`;
}
existingConnection.destroy();
}
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: context.guild.id,
adapterCreator: context.guild.voiceAdapterCreator,
selfDeaf: true,
});
connection.on(VoiceConnectionStatus.Disconnected, async () => {
try {
await Promise.race([
entersState(connection, VoiceConnectionStatus.Signalling, 5_000),
entersState(connection, VoiceConnectionStatus.Connecting, 5_000),
]);
} catch (error) {
if (connection.state.status !== VoiceConnectionStatus.Destroyed) connection.destroy();
}
});
connection.on(VoiceConnectionStatus.Destroyed, () => {
context.client.solaraVoiceConnections?.delete(context.guild.id);
const player = context.client.solaraAudioPlayers?.get(context.guild.id);
if (player) { player.stop(true); context.client.solaraAudioPlayers.delete(context.guild.id); }
context.client.solaraVoiceQueues?.delete(context.guild.id);
});
await entersState(connection, VoiceConnectionStatus.Ready, 20_000);
context.client.solaraVoiceConnections?.set(context.guild.id, connection);
return `Joined voice channel: ${voiceChannel.name}`;
} catch (error) {
console.error(`Solara.voice Error ($voiceJoin) for guild ${context.guild.id}:`, error);
const conn = context.client.solaraVoiceConnections?.get(context.guild.id);
if (conn && conn.state.status !== VoiceConnectionStatus.Destroyed) conn.destroy();
context.client.solaraVoiceConnections?.delete(context.guild.id);
return `[Error joining voice channel: ${error.message}]`;
}
}
};