require('dotenv').config(); const { Client, GatewayIntentBits, REST, Routes, SlashCommandBuilder } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, NoSubscriberBehavior, VoiceConnectionStatus, enterState } = require('@discordjs/voice'); const express = require('express'); const fs = require('fs'); const path = require('path'); // --- Configuration --- const DISCORD_TOKEN = process.env.DISCORD_TOKEN; const CLIENT_ID = process.env.CLIENT_ID; const GUILD_ID = process.env.GUILD_ID; // Keep-Alive Server const app = express(); const port = 8080; app.get('/', (req, res) => res.send('Bot Sahur is Alive! 🌙')); app.listen(port, () => console.log(`Keep-Alive server running on port ${port}`)); // Discord Client const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates ] }); let player = null; let connection = null; let isLooping = false; // Function to play local sahur.mp3 function playAudio() { try { if (!connection) { console.error('[Audio] Connection tidak ditemukan!'); return false; } const filePath = path.join(__dirname, 'sahur.mp3'); console.log(`[Audio] Memutar file dari: ${filePath}`); if (!fs.existsSync(filePath)) { console.error(`[Audio] File TIDAK ditemukan di: ${filePath}`); return false; } if (!player) { console.log('[Audio] Membuat player baru...'); player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Play, }, }); player.on(AudioPlayerStatus.Idle, () => { if (isLooping && connection) { console.log('[Audio] Looping: Memutar ulang sahur.mp3'); playAudio(); } }); player.on('error', error => { console.error('[Audio] Player Error:', error.message); console.error(error); }); } const resource = createAudioResource(filePath); player.play(resource); connection.subscribe(player); console.log('[Audio] Berhasil memutar resource.'); return true; } catch (error) { console.error('[Audio] Exception di playAudio:', error); return false; } } // Slash Commands Definition const commands = [ new SlashCommandBuilder() .setName('join') .setDescription('Membuat bot masuk ke Voice Channel kamu'), new SlashCommandBuilder() .setName('test_sahur') .setDescription('Memutar sahur.mp3 secara berulang (loop)'), ].map(command => command.toJSON()); const rest = new REST({ version: '10' }).setToken(DISCORD_TOKEN); async function registerCommands() { try { console.log('Mendaftarkan ulang slash commands (setelah reset)...'); await rest.put( Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), { body: commands } ); console.log('Berhasil mendaftarkan command: /join, /test_sahur'); } catch (error) { console.error('Gagal mendaftarkan command:', error); } } client.once('ready', () => { console.log(`Bot logged in as ${client.user.tag}`); registerCommands(); // Simple Keep-Alive log every 15 minutes setInterval(() => { console.log(`[Keep-Alive] Bot active at ${new Date().toISOString()}`); }, 15 * 60 * 1000); }); client.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; const { commandName } = interaction; try { if (commandName === 'join') { const member = interaction.member; const voiceChannel = member.voice.channel; if (!voiceChannel) { return interaction.reply({ content: 'Kamu harus berada di Voice Channel!', ephemeral: true }); } connection = joinVoiceChannel({ channelId: voiceChannel.id, guildId: interaction.guildId, adapterCreator: interaction.guild.voiceAdapterCreator, }); connection.on(VoiceConnectionStatus.Disconnected, async (oldState, newState) => { try { await Promise.race([ enterState(connection, VoiceConnectionStatus.Signalling, 5_000), enterState(connection, VoiceConnectionStatus.Connecting, 5_000), ]); } catch (error) { console.error('[Voice] Gagal menyambung kembali:', error); if (connection) connection.destroy(); connection = null; } }); await interaction.reply({ content: `Bot berhasil join ke ${voiceChannel.name}! ✅` }); } if (commandName === 'test_sahur') { if (!connection) { return interaction.reply({ content: 'Bot harus join ke Voice Channel dulu! Gunakan /join', ephemeral: true }); } isLooping = true; const success = playAudio(); if (success) { await interaction.reply({ content: 'Memutar sahur.mp3 dalam mode loop... 🔁' }); } else { await interaction.reply({ content: 'Gagal memutar audio. Cek Logs untuk detailnya. ❌', ephemeral: true }); } } } catch (error) { console.error(`[Interaction Error] Command: ${commandName}`, error); const errorMsg = 'Terjadi kesalahan saat menjalankan command ini. Silakan coba lagi.'; if (interaction.replied || interaction.deferred) { await interaction.followUp({ content: errorMsg, ephemeral: true }); } else { await interaction.reply({ content: errorMsg, ephemeral: true }); } } }); client.login(DISCORD_TOKEN);