fix(lavalink): Update join, play, and voice state handling for Shoukaku integration
This commit is contained in:
parent
854cf12d64
commit
0d0125bf55
@ -1,4 +1,4 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, ChannelType } = require('discord.js');
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, ChannelType, MessageFlags } = require('discord.js'); // Import MessageFlags
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
module.exports = {
|
||||
@ -6,7 +6,8 @@ module.exports = {
|
||||
.setName('join')
|
||||
.setDescription('Joins your current voice channel'),
|
||||
async execute(interaction, client) { // Added client parameter
|
||||
await interaction.deferReply({ ephemeral: true }); // Defer reply as joining might take time
|
||||
// Use flags for ephemeral deferral
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
const member = interaction.member;
|
||||
const voiceChannel = member?.voice?.channel;
|
||||
@ -29,44 +30,57 @@ module.exports = {
|
||||
return interaction.editReply('I can only join standard voice channels.');
|
||||
}
|
||||
|
||||
// 3. Create or get the player and connect
|
||||
let player = client.manager.get(interaction.guildId);
|
||||
// Get the initialized Shoukaku player manager from the client object
|
||||
const musicPlayer = interaction.client.player;
|
||||
if (!musicPlayer) {
|
||||
logger.error('Music player not initialized on client object!');
|
||||
return interaction.editReply('The music player is not ready yet. Please try again shortly.');
|
||||
}
|
||||
|
||||
// 3. Get or create the player and connect using Shoukaku
|
||||
let player = musicPlayer.getPlayer(interaction.guildId);
|
||||
|
||||
if (!player) {
|
||||
try {
|
||||
player = client.manager.create({
|
||||
guild: interaction.guildId,
|
||||
voiceChannel: voiceChannel.id,
|
||||
textChannel: interaction.channelId, // Store the channel where command was used
|
||||
selfDeafen: true, // Automatically deafen the bot
|
||||
// selfMute: false, // Bot starts unmuted
|
||||
// Create player using the Shoukaku manager
|
||||
player = await musicPlayer.createPlayer({
|
||||
guildId: interaction.guildId,
|
||||
textChannel: interaction.channelId,
|
||||
voiceChannel: voiceChannel.id
|
||||
});
|
||||
player.connect();
|
||||
// Connection is handled within createPlayer
|
||||
logger.info(`Created player and connected to voice channel ${voiceChannel.name} (${voiceChannel.id}) in guild ${interaction.guild.name} (${interaction.guildId})`);
|
||||
await interaction.editReply(`Joined ${voiceChannel.name}! Ready to play music.`);
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create/connect player for guild ${interaction.guildId}: ${error.message}`, error);
|
||||
// Try to destroy player if partially created
|
||||
if (player) player.destroy();
|
||||
// Player destruction is handled internally if creation fails or via destroy method
|
||||
return interaction.editReply('An error occurred while trying to join the voice channel.');
|
||||
}
|
||||
} else {
|
||||
// If player exists but is not connected or in a different channel
|
||||
// If player exists but is in a different channel
|
||||
if (player.voiceChannel !== voiceChannel.id) {
|
||||
player.setVoiceChannel(voiceChannel.id);
|
||||
if (!player.playing && !player.paused && !player.queue.size) {
|
||||
player.connect(); // Connect if not already playing/paused/queued
|
||||
}
|
||||
logger.info(`Moved player to voice channel ${voiceChannel.name} (${voiceChannel.id}) in guild ${interaction.guildId}`);
|
||||
await interaction.editReply(`Moved to ${voiceChannel.name}!`);
|
||||
// Destroy the old player and create a new one in the correct channel
|
||||
player.destroy();
|
||||
try {
|
||||
player = await musicPlayer.createPlayer({
|
||||
guildId: interaction.guildId,
|
||||
textChannel: interaction.channelId,
|
||||
voiceChannel: voiceChannel.id
|
||||
});
|
||||
logger.info(`Moved player to voice channel ${voiceChannel.name} (${voiceChannel.id}) in guild ${interaction.guildId}`);
|
||||
await interaction.editReply(`Moved to ${voiceChannel.name}!`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to move player for guild ${interaction.guildId}: ${error.message}`, error);
|
||||
return interaction.editReply('An error occurred while trying to move to the voice channel.');
|
||||
}
|
||||
} else {
|
||||
// Already in the correct channel
|
||||
await interaction.editReply(`I'm already in ${voiceChannel.name}!`);
|
||||
}
|
||||
// Update text channel if needed
|
||||
// Update text channel if needed (Shoukaku player object stores textChannel)
|
||||
if (player.textChannel !== interaction.channelId) {
|
||||
player.setTextChannel(interaction.channelId);
|
||||
player.textChannel = interaction.channelId; // Directly update the property
|
||||
logger.debug(`Updated player text channel to ${interaction.channel.name} (${interaction.channelId}) in guild ${interaction.guildId}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, ChannelType, EmbedBuilder } = require('discord.js');
|
||||
const logger = require('../utils/logger');
|
||||
const { musicPlayer } = require('../structures/ShoukakuEvents');
|
||||
// Removed direct import of musicPlayer
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
@ -35,14 +35,21 @@ module.exports = {
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the initialized player from the client object
|
||||
const musicPlayer = interaction.client.player;
|
||||
if (!musicPlayer) {
|
||||
logger.error('Music player not initialized on client object!');
|
||||
return interaction.editReply('The music player is not ready yet. Please try again shortly.');
|
||||
}
|
||||
|
||||
// 3. Get or create player
|
||||
let player = musicPlayer.getPlayer(interaction.guildId);
|
||||
if (!player) {
|
||||
try {
|
||||
player = await musicPlayer.createPlayer({
|
||||
guildId: interaction.guildId,
|
||||
textChannel: interaction.channelId,
|
||||
voiceChannel: voiceChannel.id
|
||||
textChannel: interaction.channelId, // Use interaction.channelId directly
|
||||
voiceChannel: voiceChannel.id // Use voiceChannel.id directly
|
||||
});
|
||||
logger.info(`Created player and connected to voice channel ${voiceChannel.name} (${voiceChannel.id}) for play command.`);
|
||||
} catch (error) {
|
||||
@ -61,7 +68,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
// 4. Search for tracks
|
||||
const searchResults = await musicPlayer.search({
|
||||
const searchResults = await musicPlayer.search({ // Use the player instance from the client
|
||||
query: query,
|
||||
requester: interaction.user
|
||||
});
|
||||
|
||||
@ -4,46 +4,49 @@ const logger = require('../utils/logger');
|
||||
module.exports = {
|
||||
name: Events.VoiceStateUpdate,
|
||||
execute(oldState, newState, client) { // Added client parameter
|
||||
// Pass the event data to the Erela.js manager
|
||||
// It handles the logic for joining/leaving channels, server muting/deafening, etc.
|
||||
if (client.manager) {
|
||||
try {
|
||||
// Use newState primarily, as erela.js handles the diff internally
|
||||
client.manager.voiceStateUpdate(newState);
|
||||
// Optional: Add more specific logging if needed
|
||||
// logger.debug(`Voice state update processed for user ${newState.member?.user?.tag || 'Unknown'} in guild ${newState.guild.id}`);
|
||||
} catch (error) {
|
||||
logger.error(`Error processing voice state update: ${error.message}`, error);
|
||||
}
|
||||
} else {
|
||||
logger.warn('Voice state update received, but Erela.js manager is not initialized yet.');
|
||||
// Shoukaku handles voice state updates internally via its connector.
|
||||
// We don't need to manually pass the update like with Erela.js.
|
||||
// The warning about Erela.js manager not being initialized can be ignored/removed.
|
||||
|
||||
// Custom logic for player cleanup based on voice state changes.
|
||||
const musicPlayer = client.player;
|
||||
if (!musicPlayer) {
|
||||
// Player manager might not be ready yet, especially during startup.
|
||||
// logger.debug('Voice state update received, but Shoukaku player manager is not ready yet.');
|
||||
return;
|
||||
}
|
||||
|
||||
// You can add custom logic here if needed, for example:
|
||||
// - Check if the bot itself was disconnected and clean up the player.
|
||||
// - Check if the channel the bot was in becomes empty.
|
||||
const player = client.manager?.players.get(newState.guild.id);
|
||||
if (!player) return;
|
||||
const player = musicPlayer.getPlayer(newState.guild.id);
|
||||
if (!player) return; // No active player for this guild
|
||||
|
||||
// Check if the bot was disconnected
|
||||
if (newState.id === client.user.id && !newState.channelId && player) {
|
||||
logger.info(`Bot was disconnected from voice channel in guild ${newState.guild.id}. Destroying player.`);
|
||||
player.destroy();
|
||||
// Check if the bot was disconnected (newState has no channelId for the bot)
|
||||
if (newState.id === client.user.id && !newState.channelId && oldState.channelId === player.voiceChannel) {
|
||||
logger.info(`Bot was disconnected from voice channel ${oldState.channel?.name || oldState.channelId} in guild ${newState.guild.id}. Destroying player.`);
|
||||
player.destroy(); // Use Shoukaku player's destroy method
|
||||
return; // Exit early as the player is destroyed
|
||||
}
|
||||
|
||||
// Check if the bot's channel is now empty (excluding the bot itself)
|
||||
const channel = client.channels.cache.get(player.voiceChannel);
|
||||
if (channel && channel.members.size === 1 && channel.members.has(client.user.id)) {
|
||||
logger.info(`Voice channel ${channel.name} (${player.voiceChannel}) in guild ${newState.guild.id} is now empty (only bot left). Destroying player.`);
|
||||
// Optional: Add a timeout before destroying
|
||||
// setTimeout(() => {
|
||||
// const currentChannel = client.channels.cache.get(player.voiceChannel);
|
||||
// if (currentChannel && currentChannel.members.size === 1) {
|
||||
// player.destroy();
|
||||
// }
|
||||
// }, 60000); // e.g., 1 minute timeout
|
||||
player.destroy();
|
||||
// Ensure the channel exists and the update is relevant to the bot's channel
|
||||
if (channel && (newState.channelId === player.voiceChannel || oldState.channelId === player.voiceChannel)) {
|
||||
// Fetch members again to ensure freshness after the update
|
||||
const members = channel.members;
|
||||
if (members.size === 1 && members.has(client.user.id)) {
|
||||
logger.info(`Voice channel ${channel.name} (${player.voiceChannel}) in guild ${newState.guild.id} is now empty (only bot left). Destroying player.`);
|
||||
// Optional: Add a timeout before destroying
|
||||
// setTimeout(() => {
|
||||
// const currentChannel = client.channels.cache.get(player.voiceChannel);
|
||||
// const currentMembers = currentChannel?.members;
|
||||
// if (currentMembers && currentMembers.size === 1 && currentMembers.has(client.user.id)) {
|
||||
// logger.info(`Timeout finished: Destroying player in empty channel ${channel.name}.`);
|
||||
// player.destroy();
|
||||
// } else {
|
||||
// logger.info(`Timeout finished: Channel ${channel.name} is no longer empty. Player not destroyed.`);
|
||||
// }
|
||||
// }, 60000); // e.g., 1 minute timeout
|
||||
player.destroy(); // Destroy immediately for now
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user