import { SlashCommandBuilder, ChatInputCommandInteraction, // Import the specific interaction type GuildMember // Import GuildMember type } from 'discord.js'; import logger from '../utils/logger'; // Use default import import { BotClient } from '../index'; // Import the BotClient interface // No need to import Player explicitly if we just check connection export default { // Use export default for ES Modules data: new SlashCommandBuilder() .setName('leave') .setDescription('Leaves the current voice channel'), async execute(interaction: ChatInputCommandInteraction, client: BotClient) { // Add types // Ensure command is run in a guild if (!interaction.guildId || !interaction.guild) { return interaction.reply({ content: 'This command can only be used in a server.', ephemeral: true }).catch(() => {}); } // Ensure interaction.member is a GuildMember (optional, but good practice) if (!(interaction.member instanceof GuildMember)) { return interaction.reply({ content: 'Could not verify your membership.', ephemeral: true }).catch(() => {}); } // Use ephemeral deferral await interaction.deferReply({ ephemeral: true }); // Get the Shoukaku instance const shoukaku = client.shoukaku; if (!shoukaku) { logger.error('Shoukaku instance not found on client object!'); return interaction.editReply('The music player is not ready yet.'); } // Check if a connection exists for this guild const connection = shoukaku.connections.get(interaction.guildId); if (!connection || !connection.channelId) { return interaction.editReply('I am not currently in a voice channel!'); } // Optional: Check if the user is in the same channel as the bot // const memberVoiceChannelId = interaction.member.voice.channelId; // if (memberVoiceChannelId !== connection.channelId) { // return interaction.editReply('You need to be in the same voice channel as me to make me leave!'); // } try { const channelId = connection.channelId; // Get channel ID from connection const channel = await client.channels.fetch(channelId).catch(() => null); // Fetch channel for name const channelName = channel && channel.isVoiceBased() ? channel.name : `ID: ${channelId}`; // Get channel name if possible // Use Shoukaku's leave method - this destroys player and connection await shoukaku.leaveVoiceChannel(interaction.guildId); logger.info(`Left voice channel ${channelName} in guild ${interaction.guild.name} (${interaction.guildId}) by user ${interaction.user.tag}`); await interaction.editReply(`Left ${channelName}.`); } catch (error: unknown) { // Type error as unknown const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Error leaving voice channel for guild ${interaction.guildId}: ${errorMessage}`, error); // Attempt to reply even if leave failed partially await interaction.editReply('An error occurred while trying to leave the voice channel.').catch((e: unknown) => { // Type catch error const replyErrorMsg = e instanceof Error ? e.message : String(e); logger.error(`Failed to send error reply for leave command: ${replyErrorMsg}`); }); } }, };