const { SlashCommandBuilder } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, StreamType, } = require('@discordjs/voice'); const { spawn } = require('node:child_process'); module.exports = { data: new SlashCommandBuilder() .setName('play') .setDescription('Proof-of-life music command'), async execute(interaction) { const voiceChannel = interaction.member.voice.channel; if (!voiceChannel) { return interaction.reply({ content: '❌ You need to join a voice channel first!', ephemeral: true, }); } // 1️⃣ Defer reply immediately to prevent 3s timeout await interaction.deferReply(); try { // 2️⃣ Spawn yt-dlp → ffmpeg → Discord const ytdlp = spawn('yt-dlp', [ '-f', 'bestaudio', '-o', '-', 'https://www.youtube.com/watch?v=rPwUh0gqv7Y', // test URL ]); const ffmpeg = spawn('ffmpeg', [ '-i', 'pipe:0', '-f', 's16le', // raw PCM for Discord '-ar', '48000', // sample rate '-ac', '2', // stereo 'pipe:1', ]); ytdlp.stdout.pipe(ffmpeg.stdin); const resource = createAudioResource(ffmpeg.stdout, { inputType: StreamType.Raw, }); const player = createAudioPlayer(); player.play(resource); const connection = joinVoiceChannel({ channelId: voiceChannel.id, guildId: interaction.guild.id, adapterCreator: interaction.guild.voiceAdapterCreator, }); connection.subscribe(player); // 3️⃣ Edit deferred reply await interaction.editReply('▶️ Now playing: Rick Astley!'); // Optional logging ytdlp.stderr.on('data', (data) => console.error('yt-dlp:', data.toString())); ffmpeg.stderr.on('data', (data) => console.error('ffmpeg:', data.toString())); player.on('error', (err) => console.error('AudioPlayer error:', err)); } catch (err) { console.error(err); // 4️⃣ Safe error reply if (interaction.deferred || interaction.replied) { await interaction.editReply('❌ Failed to play audio!'); } else { await interaction.reply('❌ Failed to play audio!'); } } }, };