process.env.FFMPEG_PATH = require('ffmpeg-static'); console.log('Bot starting...'); const { Client, GatewayIntentBits, SlashCommandBuilder, Routes } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, VoiceConnectionStatus, entersState } = require('@discordjs/voice'); const { REST } = require('@discordjs/rest'); const fs = require('fs'); const path = require('path'); const configPath = path.join(__dirname, 'data/config.json'); function loadConfig() { if (fs.existsSync(configPath)) { return JSON.parse(fs.readFileSync(configPath, 'utf8')); } return {}; } const config = loadConfig(); const token = config.discord_token; if (!token) { console.error('No Discord Token found in data/config.json'); process.exit(1); } const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, ] }); const player = createAudioPlayer(); player.on(AudioPlayerStatus.Idle, () => { console.log('Audio finished playing. Bot will stay in the channel.'); }); player.on('error', error => { console.error('Audio Player Error:', error.message); }); client.on('ready', async () => { console.log(`Logged in as ${client.user.tag}`); // Register commands const commands = [ new SlashCommandBuilder() .setName('testsahur') .setDescription('Memutar file audio sahur'), new SlashCommandBuilder() .setName('join') .setDescription('Menyuruh bot join ke voice channel kamu'), new SlashCommandBuilder() .setName('stop') .setDescription('Menghentikan pemutaran audio') ].map(command => command.toJSON()); const rest = new REST({ version: '10' }).setToken(token); try { console.log('Started refreshing application (/) commands.'); await rest.put(Routes.applicationCommands(client.user.id), { body: commands }); console.log('Successfully reloaded application (/) commands.'); } catch (error) { console.error('Error reloading commands:', error); } // Auto-reconnect to last voice channel const currentConfig = loadConfig(); const lastVoice = currentConfig.last_voice_channel; if (lastVoice && lastVoice.guildId && lastVoice.channelId) { console.log(`Auto-rejoining channel ${lastVoice.channelId}`); joinVC(lastVoice.guildId, lastVoice.channelId); } }); function joinVC(gId, cId) { const guild = client.guilds.cache.get(gId); if (!guild) { console.error(`Guild ${gId} not found in cache`); return; } const connection = joinVoiceChannel({ channelId: cId, guildId: gId, adapterCreator: guild.voiceAdapterCreator, selfDeaf: false, selfMute: false, }); connection.on(VoiceConnectionStatus.Disconnected, async () => { try { await Promise.race([ entersState(connection, VoiceConnectionStatus.Signalling, 5_000), entersState(connection, VoiceConnectionStatus.Connecting, 5_000), ]); } catch (error) { console.log('Connection lost, attempting to reconnect...'); setTimeout(() => joinVC(gId, cId), 5000); } }); connection.on('error', error => { console.error('Voice Connection Error:', error); }); connection.subscribe(player); } client.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; try { if (interaction.commandName === 'testsahur') { const member = interaction.member; if (!member.voice.channel) { return interaction.reply('Kamu harus berada di Voice Channel!'); } const channel = member.voice.channel; const gId = interaction.guildId; const cId = channel.id; // Save last channel const currentConfig = loadConfig(); currentConfig.last_voice_channel = { guildId: gId, channelId: cId }; fs.writeFileSync(configPath, JSON.stringify(currentConfig, null, 2)); joinVC(gId, cId); const audioPath = path.join(__dirname, 'assets/audio/sahur.mp3'); if (!fs.existsSync(audioPath)) { return interaction.reply(`File audio tidak ditemukan: ${audioPath}`); } const resource = createAudioResource(audioPath, { inlineVolume: true }); resource.volume.setVolume(1.0); player.play(resource); await interaction.reply('🔊 Memutar audio Sahur!'); } else if (interaction.commandName === 'join') { const member = interaction.member; if (!member.voice.channel) { return interaction.reply('Kamu harus berada di Voice Channel!'); } const channel = member.voice.channel; const gId = interaction.guildId; const cId = channel.id; // Save last channel const currentConfig = loadConfig(); currentConfig.last_voice_channel = { guildId: gId, channelId: cId }; fs.writeFileSync(configPath, JSON.stringify(currentConfig, null, 2)); joinVC(gId, cId); await interaction.reply('✅ Bot telah bergabung ke Voice Channel!'); } else if (interaction.commandName === 'stop') { player.stop(); await interaction.reply('🛑 Audio dihentikan.'); } } catch (error) { console.error('Interaction Error:', error); if (interaction.replied || interaction.deferred) { await interaction.followUp({ content: 'Terjadi kesalahan saat menjalankan command!', ephemeral: true }); } else { await interaction.reply({ content: 'Terjadi kesalahan saat menjalankan command!', ephemeral: true }); } } }); let lastAlarmMinute = -1; setInterval(() => { const now = new Date(); const currentMinute = now.getMinutes(); if (currentMinute !== lastAlarmMinute) { lastAlarmMinute = currentMinute; const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; const currentConfig = loadConfig(); if (currentConfig.alarm_time === currentTime) { console.log(`Alarm triggered at ${currentTime}`); const lastVoice = currentConfig.last_voice_channel; if (lastVoice && lastVoice.guildId && lastVoice.channelId) { try { joinVC(lastVoice.guildId, lastVoice.channelId); const audioPath = path.join(__dirname, 'assets/audio/sahur.mp3'); if (fs.existsSync(audioPath)) { const resource = createAudioResource(audioPath, { inlineVolume: true }); resource.volume.setVolume(1.0); player.play(resource); } } catch (e) { console.error('Failed to trigger alarm VC:', e); } } } } }, 30000); // Check every 30 seconds client.login(token);