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