38530-vm/index.js
2026-02-17 19:11:59 +00:00

181 lines
6.7 KiB
JavaScript

console.log('Bot Starting...');
const { Client, GatewayIntentBits, EmbedBuilder, PermissionsBitField, REST, Routes, SlashCommandBuilder } = require('discord.js');
const { DisTube } = require('distube');
const { YtDlpPlugin } = require('@distube/yt-dlp');
const { YouTubePlugin } = require('@distube/youtube');
const express = require('express');
require('dotenv').config();
// 1. Keep-Alive System
const app = express();
app.get('/', (req, res) => res.send('Bot Musik Online!'));
app.listen(process.env.PORT || 8080, () => console.log('Keep-Alive aktif di port ' + (process.env.PORT || 8080)));
// 2. Discord Client Setup
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
]
});
// 3. DisTube Setup
const distube = new DisTube(client, {
emitNewSongOnly: true,
emitAddSongWhenCreatingQueue: false,
emitAddListWhenCreatingQueue: false,
plugins: [
new YouTubePlugin(),
new YtDlpPlugin()
]
});
// 4. Slash Commands Definition
const commands = [
new SlashCommandBuilder()
.setName('play')
.setDescription('Putar musik dari judul atau link')
.addStringOption(option =>
option.setName('query')
.setDescription('Judul lagu atau link (YouTube/Spotify/SoundCloud)')
.setRequired(true)),
new SlashCommandBuilder()
.setName('skip')
.setDescription('Lewati lagu saat ini'),
new SlashCommandBuilder()
.setName('stop')
.setDescription('Berhentikan musik dan keluar dari voice channel'),
new SlashCommandBuilder()
.setName('queue')
.setDescription('Lihat daftar antrean lagu'),
new SlashCommandBuilder()
.setName('pause')
.setDescription('Jeda musik'),
new SlashCommandBuilder()
.setName('resume')
.setDescription('Lanjutkan musik'),
].map(command => command.toJSON());
// 5. Interaction Handling
client.on('interactionCreate', async interaction => {
await interaction.deferReply().catch(e => console.error(e));
console.log('--- COMMAND DITERIMA: ' + interaction.commandName + ' ---');
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'play') {
try {
const query = interaction.options.getString('query');
if (!interaction.member.voice.channel) {
return interaction.editReply('Kamu harus berada di voice channel untuk menggunakan bot ini!');
}
// Memberikan feedback awal
await interaction.editReply({ content: `🔍 Sedang mencari lagu: **${query}**...` });
await distube.play(interaction.member.voice.channel, query, {
textChannel: interaction.channel,
member: interaction.member,
interaction
});
} catch (error) {
console.error('Play Error:', error);
await interaction.editReply({ content: `❌ Terjadi kesalahan: ${error.message}` });
}
} else {
// Untuk command lain, kita tetap gunakan try-catch global
try {
if (!interaction.member.voice.channel) {
return interaction.editReply('Kamu harus berada di voice channel!');
}
if (commandName === 'skip') {
const queue = distube.getQueue(interaction.guild);
if (!queue) return interaction.editReply('❌ Tidak ada lagu yang sedang diputar!');
await distube.skip(interaction.guild);
await interaction.editReply('⏭️ Lagu berhasil dilewati!');
} else if (commandName === 'stop') {
await distube.stop(interaction.guild);
await interaction.editReply('⏹️ Musik dihentikan!');
} else if (commandName === 'pause') {
const queue = distube.getQueue(interaction.guild);
if (!queue) return interaction.editReply('❌ Tidak ada antrean!');
distube.pause(interaction.guild);
await interaction.editReply('⏸️ Musik dijeda!');
} else if (commandName === 'resume') {
const queue = distube.getQueue(interaction.guild);
if (!queue) return interaction.editReply('❌ Tidak ada antrean!');
distube.resume(interaction.guild);
await interaction.editReply('▶️ Musik dilanjutkan!');
} else if (commandName === 'queue') {
const queue = distube.getQueue(interaction.guild);
if (!queue) return interaction.editReply('📭 Antrean kosong!');
const q = queue.songs
.map((song, i) => `${i === 0 ? '▶️' : `${i}.`} ${song.name}`)
.join('\n');
await interaction.editReply(`🎶 **Antrean:**\n${q.slice(0, 1900)}`);
}
} catch (error) {
console.error('Interaction Error:', error);
if (interaction.deferred || interaction.replied) {
await interaction.editReply({ content: `❌ Error: ${error.message}` });
}
}
}
});
// 6. DisTube Events
distube
.on('playSong', (queue, song) => {
queue.textChannel.send(`🎶 Sedang memutar: **${song.name}** - \`${song.formattedDuration}\`\nDiminta oleh: ${song.user}`);
})
.on('addSong', (queue, song) => {
queue.textChannel.send(`✅ Menambahkan **${song.name}** ke antrean!`);
})
.on('error', (channel, e) => {
if (channel && channel.send) {
const errorMessage = e && e.message ? e.message.slice(0, 1900) : "Unknown Error";
channel.send(`❌ Error: ${errorMessage}`).catch(console.error);
}
console.error('DisTube Error:', e);
});
// 7. Client Ready & Command Registration
client.once('ready', async () => {
console.log(`Bot logged in as ${client.user.tag}`);
// Register Global Slash Commands
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
try {
console.log('Memulai refresh slash commands...');
await rest.put(
Routes.applicationCommands(client.user.id),
{ body: commands },
);
console.log('Berhasil mendaftarkan slash commands!');
} catch (error) {
console.error(error);
}
});
// 8. Global Error Handling (Anti-Crash)
process.on('unhandledRejection', error => {
console.error('Unhandled promise rejection:', error);
});
process.on('uncaughtException', error => {
console.error('Uncaught exception:', error);
});
client.login('MTQ3Mjc2ODQ4NTQ0NzY5NjY1MA.Gx78sk.NGDrdd51zwcDbqyVrupL78RlUZdUSgmGvzn3pw');