process.env.FFMPEG_PATH = require('ffmpeg-static'); const path = require('path'); const fs = require('fs'); const { Client, GatewayIntentBits, Events } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, NoSubscriberBehavior } = require('@discordjs/voice'); const { CronJob } = require('cron'); const play = require('play-dl'); // Ensure .env is loaded early require('dotenv').config({ path: path.join(__dirname, '.env') }); // Tambahkan folder ffmpeg ke PATH const ffmpegDir = path.dirname(process.env.FFMPEG_PATH); if (!process.env.PATH.includes(ffmpegDir)) { process.env.PATH = `${ffmpegDir}${path.delimiter}${process.env.PATH}`; } const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); const VC_ID = process.env.VC_ID; const AUDIO_PATH = path.join(__dirname, 'assets/audio/sahur.mp3'); const PREFIX = '!'; // Global state const queues = new Map(); // guildId -> { queue: [], player, connection } function logToFile(message) { const logMessage = `[${new Date().toISOString()}] ${message}\n`; try { fs.appendFileSync(path.join(__dirname, 'bot.log'), logMessage); } catch (err) { console.error('Failed to write to log file:', err); } console.log(message); } client.once(Events.ClientReady, () => { console.log('Bot berhasil login!'); logToFile(`Ready! Logged in as ${client.user.tag}`); // Sahur Alarm const sahurTime = process.env.SAHUR_TIME || '30 03 * * *'; let cronTime = sahurTime; if (sahurTime.includes(':') && !sahurTime.includes('*')) { const [hour, minute] = sahurTime.split(':'); cronTime = `0 ${minute} ${hour} * * *`; } try { new CronJob(cronTime, async () => { logToFile('Sahur alarm triggered!'); const guild = client.guilds.cache.first(); if (!guild) return; const channel = guild.channels.cache.get(VC_ID); if (channel) { playLocal(channel, AUDIO_PATH); } }, null, true, 'Asia/Jakarta'); logToFile(`Alarm scheduled at ${cronTime}`); } catch (e) { logToFile(`Failed to schedule alarm: ${e.message}`); } }); async function playLocal(channel, filePath) { try { const connection = joinVoiceChannel({ channelId: channel.id, guildId: channel.guild.id, adapterCreator: channel.guild.voiceAdapterCreator, }); const player = createAudioPlayer(); const resource = createAudioResource(filePath); player.play(resource); connection.subscribe(player); player.once(AudioPlayerStatus.Idle, () => { connection.destroy(); }); } catch (err) { logToFile(`Error playing local file: ${err.message}`); } } client.on(Events.MessageCreate, async (message) => { if (message.author.bot || !message.content.startsWith(PREFIX)) return; const args = message.content.slice(PREFIX.length).trim().split(/ +/); const command = args.shift().toLowerCase(); const voiceChannel = message.member?.voice.channel; try { if (command === 'join') { if (!voiceChannel) return message.reply('Anda harus berada di voice channel!'); joinVoiceChannel({ channelId: voiceChannel.id, guildId: message.guild.id, adapterCreator: message.guild.voiceAdapterCreator, }); message.reply('Sudah join! 🎧'); } else if (command === 'testsahur') { if (!voiceChannel) return message.reply('Anda harus berada di voice channel!'); if (!fs.existsSync(AUDIO_PATH)) return message.reply(`File audio tidak ditemukan di ${AUDIO_PATH}`); playLocal(voiceChannel, AUDIO_PATH); message.reply('Memainkan suara sahur... πŸ“’'); } else if (command === 'play') { if (!voiceChannel) return message.reply('Anda harus berada di voice channel!'); const query = args.join(' '); if (!query) return message.reply('Berikan link atau nama lagu!'); let serverQueue = queues.get(message.guild.id); if (!serverQueue) { serverQueue = { songs: [], connection: null, player: createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Play } }), }; queues.set(message.guild.id, serverQueue); } message.reply('Mencari lagu... πŸ”'); let songInfo; if (play.sp_validate(query) === 'track') { const sp_data = await play.spotify(query); const search = await play.search(`${sp_data.name} ${sp_data.artists[0].name}`, { limit: 1 }); songInfo = { title: sp_data.name, url: search[0].url }; } else if (play.so_validate(query)) { const so_data = await play.soundcloud(query); songInfo = { title: so_data.name, url: so_data.url }; } else { const yt_info = await play.search(query, { limit: 1 }); if (yt_info.length === 0) return message.channel.send('Lagu tidak ditemukan!'); songInfo = { title: yt_info[0].title, url: yt_info[0].url }; } serverQueue.songs.push(songInfo); if (serverQueue.songs.length === 1) { playSong(message.guild.id, voiceChannel); message.channel.send(`🎡 Sekarang memutar: **${songInfo.title}**`); } else { message.channel.send(`βœ… **${songInfo.title}** ditambahkan ke antrean.`); } } else if (command === 'skip') { const serverQueue = queues.get(message.guild.id); if (!serverQueue) return message.reply('Tidak ada lagu yang sedang diputar!'); serverQueue.player.stop(); message.reply('Lagu dilewati! ⏭️'); } else if (command === 'stop') { const serverQueue = queues.get(message.guild.id); if (serverQueue) { serverQueue.songs = []; serverQueue.player.stop(); if (serverQueue.connection) serverQueue.connection.destroy(); queues.delete(message.guild.id); } message.reply('Musik dihentikan. πŸ‘‹'); } else if (command === 'pause') { const serverQueue = queues.get(message.guild.id); if (serverQueue) serverQueue.player.pause(); message.reply('Dipause. ⏸️'); } else if (command === 'resume') { const serverQueue = queues.get(message.guild.id); if (serverQueue) serverQueue.player.unpause(); message.reply('Dilanjutkan. ▢️'); } } catch (error) { logToFile(`Command Error (${command}): ${error.stack || error.message}`); message.reply(`❌ Terjadi kesalahan: ${error.message}`); } }); async function playSong(guildId, channel) { const serverQueue = queues.get(guildId); if (!serverQueue || serverQueue.songs.length === 0) { return; } try { const song = serverQueue.songs[0]; const stream = await play.stream(song.url); const resource = createAudioResource(stream.stream, { inputType: stream.type }); if (!serverQueue.connection) { serverQueue.connection = joinVoiceChannel({ channelId: channel.id, guildId: guildId, adapterCreator: channel.guild.voiceAdapterCreator, }); serverQueue.connection.subscribe(serverQueue.player); } serverQueue.player.play(resource); serverQueue.player.once(AudioPlayerStatus.Idle, () => { serverQueue.songs.shift(); playSong(guildId, channel); }); } catch (err) { logToFile(`Playback Error: ${err.message}`); serverQueue.songs.shift(); playSong(guildId, channel); } } // Login at the very bottom client.login(process.env.DISCORD_TOKEN);