process.env.FFMPEG_PATH = require('ffmpeg-static'); require('opusscript'); require('libsodium-wrappers'); const { Client, GatewayIntentBits, SlashCommandBuilder, Routes } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, StreamType, getVoiceConnection } = require('@discordjs/voice'); const fs = require('fs'); const path = require('path'); const { REST } = require('@discordjs/rest'); // Load Config const configPath = path.join(__dirname, 'data/config.json'); const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const token = config.discord_token; const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates] }); const player = createAudioPlayer(); // Monitoring Status player.on('stateChange', (oldState, newState) => { console.log(`Status Player: ${newState.status}`); }); player.on('error', error => { console.log('ERROR AUDIO:', error.message); }); client.on('ready', async () => { console.log(`Bot Sahur Online: ${client.user.tag}`); const commands = [ new SlashCommandBuilder().setName('join').setDescription('Perintah untuk bot masuk ke Voice Channel'), new SlashCommandBuilder().setName('testsahur').setDescription('Perintah untuk memutar suara alarm sahur') ].map(cmd => cmd.toJSON()); const rest = new REST({ version: '10' }).setToken(token); try { await rest.put(Routes.applicationCommands(client.user.id), { body: commands }); console.log('Slash Commands Berhasil Didaftarkan.'); } catch (err) { console.error('Gagal daftar command:', err); } }); client.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; // 1. PERINTAH JOIN (Hanya Masuk) if (interaction.commandName === 'join') { await interaction.deferReply(); const channel = interaction.member.voice.channel; if (!channel) { return interaction.editReply('❌ Kamu harus masuk ke Voice Channel dulu!'); } const connection = joinVoiceChannel({ channelId: channel.id, guildId: interaction.guildId, adapterCreator: interaction.guild.voiceAdapterCreator, selfDeaf: false, selfMute: false, debug: true }); // Keep-Alive Logic connection.on('stateChange', (oldState, newState) => { if (newState.status === 'disconnected') { console.log('Bot terputus, mencoba masuk kembali...'); try { joinVoiceChannel({ channelId: channel.id, guildId: interaction.guildId, adapterCreator: interaction.guild.voiceAdapterCreator, selfDeaf: false, selfMute: false }); } catch (e) { console.log('Gagal rejoin:', e.message); } } }); console.log('Bot masuk voice!'); await interaction.editReply('✅ Bot sudah masuk ke Voice Channel. Siap untuk /testsahur!'); } // 2. PERINTAH TESTSAHUR (Auto-Join & Play) if (interaction.commandName === 'testsahur') { await interaction.deferReply(); let connection = getVoiceConnection(interaction.guildId); const channel = interaction.member.voice.channel; // Auto-Reconnect: Jika tidak ada koneksi, join dulu if (!connection) { if (!channel) { return interaction.editReply('❌ Bot tidak di VC dan kamu juga tidak di VC. Join VC dulu!'); } console.log('Connection tidak ditemukan, mencoba auto-join...'); connection = joinVoiceChannel({ channelId: channel.id, guildId: interaction.guildId, adapterCreator: interaction.guild.voiceAdapterCreator, selfDeaf: false, selfMute: false }); } try { const audioPath = path.join(process.cwd(), 'assets', 'audio', 'sahur.mp3'); console.log('Membaca file audio di:', audioPath); if (!fs.existsSync(audioPath)) { return interaction.editReply('❌ File audio tidak ditemukan!'); } const resource = createAudioResource(audioPath, { inputType: StreamType.Arbitrary, inlineVolume: true }); resource.volume.setVolume(1.5); connection.subscribe(player); player.play(resource); console.log('Kabel audio tersambung dan suara diputar!'); await interaction.editReply('🔊 Sedang memutar alarm sahur (Auto-Connect Aktif)...'); } catch (error) { console.log('ERROR AUDIO:', error.message); await interaction.editReply('❌ Terjadi kesalahan: ' + error.message); } } }); client.login(token);