diff --git a/bot.log b/bot.log index d78a6eb..f154d9e 100644 --- a/bot.log +++ b/bot.log @@ -6,3 +6,22 @@ Scheduled sahur at 3:0 (node:7171) DeprecationWarning: The ready event has been renamed to clientReady to distinguish it from the gateway READY event and will only emit under that name in v15. Please use clientReady instead. (Use `node --trace-deprecation ...` to show where the warning was created) Successfully reloaded application (/) commands. +/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:746 + throw new Error( + ^ + +Error: Cannot utilize the DAVE protocol as the @snazzah/davey package has not been installed. +- Use the generateDependencyReport() function for more information. + + at new DAVESession (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:746:13) + at Networking.createDaveSession (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:1467:21) + at Networking.onWsPacket (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:1602:20) + at VoiceWebSocket.emit (node:events:530:35) + at VoiceWebSocket.onMessage (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:1239:10) + at VoiceWebSocket.ws.onmessage (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:1182:39) + at callListener (/home/ubuntu/executor/workspace/node_modules/ws/lib/event-target.js:290:14) + at WebSocket.onMessage (/home/ubuntu/executor/workspace/node_modules/ws/lib/event-target.js:209:9) + at WebSocket.emit (node:events:518:28) + at Receiver.receiverOnMessage (/home/ubuntu/executor/workspace/node_modules/ws/lib/websocket.js:1225:20) + +Node.js v22.18.0 diff --git a/index.js b/index.js index 8cfe294..31cfd70 100644 --- a/index.js +++ b/index.js @@ -4,21 +4,16 @@ const { GatewayIntentBits, REST, Routes, - SlashCommandBuilder, - ActionRowBuilder, - ButtonBuilder, - ButtonStyle + SlashCommandBuilder } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, - VoiceConnectionStatus + NoSubscriberBehavior } = require('@discordjs/voice'); const express = require('express'); -const multer = require('multer'); -const cron = require('node-cron'); const fs = require('fs'); const path = require('path'); @@ -26,230 +21,119 @@ const path = require('path'); const DISCORD_TOKEN = process.env.DISCORD_TOKEN; const CLIENT_ID = process.env.CLIENT_ID; const GUILD_ID = process.env.GUILD_ID; -const VOICE_CHANNEL_ID = process.env.VOICE_CHANNEL_ID; -const TEXT_CHANNEL_ID = process.env.TEXT_CHANNEL_ID; - -let sahurHour = parseInt(process.env.SAHUR_HOUR) || 3; -let sahurMinute = parseInt(process.env.SAHUR_MINUTE) || 0; const app = express(); const port = 8080; -// Multer for MP3 Upload -const storage = multer.diskStorage({ - destination: (req, file, cb) => cb(null, 'uploads/'), - filename: (req, file, cb) => cb(null, 'sahur.mp3') -}); -const upload = multer({ storage }); - // Discord Client const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent ] }); -// --- Bot Logic --- -async function playSahurAudio(targetVoiceId = VOICE_CHANNEL_ID) { - const guild = client.guilds.cache.get(GUILD_ID); - if (!guild) return console.error('Guild not found'); +let player = null; +let connection = null; +let isLooping = false; - const voiceChannel = guild.channels.cache.get(targetVoiceId); - if (!voiceChannel) return console.error('Voice channel not found'); +// Function to play local sahur.mp3 +function playAudio() { + if (!connection) return; - const filePath = path.join(__dirname, 'uploads', 'sahur.mp3'); + const filePath = path.join(__dirname, 'sahur.mp3'); if (!fs.existsSync(filePath)) { - const textChannel = guild.channels.cache.get(TEXT_CHANNEL_ID); - if (textChannel) textChannel.send('⚠️ Peringatan: file uploads/sahur.mp3 belum ada!'); + console.error('File sahur.mp3 tidak ditemukan!'); return; } - const connection = joinVoiceChannel({ - channelId: voiceChannel.id, - guildId: guild.id, - adapterCreator: guild.voiceAdapterCreator, - }); + if (!player) { + player = createAudioPlayer({ + behaviors: { + noSubscriber: NoSubscriberBehavior.Play, + }, + }); + + player.on(AudioPlayerStatus.Idle, () => { + if (isLooping && connection) { + console.log('Looping: Memutar ulang sahur.mp3'); + playAudio(); + } + }); + + player.on('error', error => { + console.error('Error Audio Player:', error.message); + }); + } - const player = createAudioPlayer(); const resource = createAudioResource(filePath); - player.play(resource); connection.subscribe(player); - - player.on(AudioPlayerStatus.Idle, () => { - connection.destroy(); - }); - - player.on('error', error => { - console.error('Error playing audio:', error); - connection.destroy(); - }); } -// Scheduled Job -let cronTask; -function setupCron() { - if (cronTask) cronTask.stop(); - const cronExpression = `${sahurMinute} ${sahurHour} * * *`; - cronTask = cron.schedule(cronExpression, () => { - console.log(`Running scheduled sahur playback at ${sahurHour}:${sahurMinute}`); - playSahurAudio(); - }); - console.log(`Scheduled sahur at ${sahurHour}:${sahurMinute}`); -} - -// Slash Commands +// Slash Commands Definition const commands = [ new SlashCommandBuilder() - .setName('testsahur') - .setDescription('Tes bunyi sahur di voice channel sekarang'), + .setName('join') + .setDescription('Membuat bot masuk ke Voice Channel kamu'), new SlashCommandBuilder() - .setName('setsahur') - .setDescription('Ubah jadwal sahur') - .addIntegerOption(option => option.setName('jam').setDescription('Jam (0-23)').setRequired(true)) - .addIntegerOption(option => option.setName('menit').setDescription('Menit (0-59)').setRequired(true)), - new SlashCommandBuilder() - .setName('statussahur') - .setDescription('Cek status bot sahur'), + .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('Started refreshing application (/) commands.'); + console.log('Refreshing application (/) commands...'); await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), { body: commands }); - console.log('Successfully reloaded application (/) commands.'); + console.log('Commands registered successfully.'); } catch (error) { - console.error(error); + console.error('Error registering commands:', error); } } client.on('ready', () => { - console.log(`Bot logged in as ${client.user.tag}`); + console.log(`Bot Online: ${client.user.tag}`); registerCommands(); - setupCron(); + + // Keep-Alive Ping + setInterval(() => { + console.log(`[Keep-Alive] Bot still running at ${new Date().toISOString()}`); + }, 10 * 60 * 1000); // Every 10 minutes }); client.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; - if (interaction.commandName === 'testsahur') { - const member = interaction.member; - const voiceChannel = member.voice.channel; + if (interaction.commandName === 'join') { + const voiceChannel = interaction.member.voice.channel; if (!voiceChannel) { - return interaction.reply({ content: 'Kamu harus berada di voice channel untuk melakukan tes!', ephemeral: true }); + return interaction.reply({ content: 'Kamu harus berada di Voice Channel!', ephemeral: true }); } - await interaction.reply('Memulai tes bunyi sahur...'); - playSahurAudio(voiceChannel.id); - } - if (interaction.commandName === 'setsahur') { - sahurHour = interaction.options.getInteger('jam'); - sahurMinute = interaction.options.getInteger('menit'); - setupCron(); - await interaction.reply(`Jadwal sahur diubah ke pukul ${sahurHour}:${sahurMinute.toString().padStart(2, '0')}`); - } - - if (interaction.commandName === 'statussahur') { - const filePath = path.join(__dirname, 'uploads', 'sahur.mp3'); - const fileExists = fs.existsSync(filePath); - const stats = fileExists ? fs.statSync(filePath) : null; - const fileSize = stats ? (stats.size / (1024 * 1024)).toFixed(2) + ' MB' : 'N/A'; - - await interaction.reply({ - content: `**Status Bot Sahur:**\n` + - `- File MP3: ${fileExists ? '✅ Ada' : '❌ Tidak ada'}\n` + - `- Ukuran File: ${fileSize}\n` + - `- Jadwal Berikutnya: ${sahurHour}:${sahurMinute.toString().padStart(2, '0')}\n` + - `- Voice Channel: <#${VOICE_CHANNEL_ID}>` + connection = joinVoiceChannel({ + channelId: voiceChannel.id, + guildId: interaction.guild.id, + adapterCreator: interaction.guild.voiceAdapterCreator, }); + + await interaction.reply(`Berhasil join ke <#${voiceChannel.id}>`); + } + + if (interaction.commandName === 'test_sahur') { + if (!connection) { + return interaction.reply({ content: 'Gunakan /join terlebih dahulu!', ephemeral: true }); + } + + isLooping = true; + playAudio(); + await interaction.reply('Memutar sahur.mp3 (Loop aktif 🔁)'); } }); -// --- Dashboard Logic --- -app.use(express.static('public')); -app.use(express.urlencoded({ extended: true })); - -app.get('/', (req, res) => { - const filePath = path.join(__dirname, 'uploads', 'sahur.mp3'); - const fileExists = fs.existsSync(filePath); - const stats = fileExists ? fs.statSync(filePath) : null; - const fileSize = stats ? (stats.size / (1024 * 1024)).toFixed(2) + ' MB' : 'N/A'; - - res.send(` - - -
-File sahur.mp3: ${fileExists ? 'Ready (' + fileSize + ')' : 'Missing'}
-Jadwal: ${sahurHour}:${sahurMinute.toString().padStart(2, '0')}
-