Autosave: 20260216-071603
This commit is contained in:
parent
ca3a32f23e
commit
96f3e9caa7
@ -1263,3 +1263,8 @@ Alarm scheduled at 0 30 03 * * *
|
||||
[2026-02-16T06:49:05.559Z] Ready! Logged in as AsepXiaoQin#6954
|
||||
[2026-02-16T06:49:05.563Z] FFMPEG Path: /home/ubuntu/executor/workspace/bot/node_modules/ffmpeg-static/ffmpeg
|
||||
[2026-02-16T06:49:06.586Z] Alarm scheduled at 0 30 03 * * *
|
||||
[2026-02-16T07:06:43.653Z] Ready! Logged in as AsepXiaoQin#6954
|
||||
[2026-02-16T07:06:43.654Z] FFMPEG Path: /home/ubuntu/executor/workspace/bot/node_modules/ffmpeg-static/ffmpeg
|
||||
[2026-02-16T07:06:43.786Z] Alarm scheduled at 0 30 03 * * *
|
||||
[2026-02-16T07:12:30.682Z] Ready! Logged in as AsepXiaoQin#6954
|
||||
[2026-02-16T07:12:30.922Z] Alarm scheduled at 0 30 03 * * *
|
||||
|
||||
286
bot/index.js
286
bot/index.js
@ -1,20 +1,20 @@
|
||||
process.env.FFMPEG_PATH = require('ffmpeg-static');
|
||||
const path = require('path');
|
||||
// Tambahkan folder ffmpeg ke PATH agar yt-dlp bisa menemukannya
|
||||
const fs = require('fs');
|
||||
const { Client, GatewayIntentBits, Events } = require('discord.js');
|
||||
const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, NoSubscriberBehavior } = require('@discordjs/voice');
|
||||
const { CronJob } = require('cron');
|
||||
const play = require('play-dl');
|
||||
|
||||
// Ensure .env is loaded early
|
||||
require('dotenv').config({ path: path.join(__dirname, '.env') });
|
||||
|
||||
// Tambahkan folder ffmpeg ke PATH
|
||||
const ffmpegDir = path.dirname(process.env.FFMPEG_PATH);
|
||||
if (!process.env.PATH.includes(ffmpegDir)) {
|
||||
process.env.PATH = `${ffmpegDir}${path.delimiter}${process.env.PATH}`;
|
||||
}
|
||||
|
||||
require('dotenv').config({ path: path.join(__dirname, '.env') });
|
||||
const { Client, GatewayIntentBits, Events, EmbedBuilder } = require('discord.js');
|
||||
const { DisTube } = require('distube');
|
||||
const { YtDlpPlugin } = require('@distube/yt-dlp');
|
||||
const { SpotifyPlugin } = require('@distube/spotify');
|
||||
const { SoundCloudPlugin } = require('@distube/soundcloud');
|
||||
const { CronJob } = require('cron');
|
||||
const fs = require('fs');
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
@ -24,61 +24,27 @@ const client = new Client({
|
||||
]
|
||||
});
|
||||
|
||||
// Initialize DisTube with the requested plugins
|
||||
const distube = new DisTube(client, {
|
||||
plugins: [
|
||||
new SpotifyPlugin(),
|
||||
new SoundCloudPlugin(),
|
||||
new YtDlpPlugin() // yt-dlp must be last
|
||||
],
|
||||
emitNewSongOnly: true,
|
||||
emitAddSongWhenCreatingQueue: false,
|
||||
emitAddListWhenCreatingQueue: false
|
||||
});
|
||||
|
||||
const VC_ID = process.env.VC_ID;
|
||||
const SAHUR_AUDIO_PATH = path.join(__dirname, 'assets/audio/sahur.mp3');
|
||||
const AUDIO_PATH = path.join(__dirname, 'assets/audio/sahur.mp3');
|
||||
const PREFIX = '!';
|
||||
|
||||
// Global state
|
||||
const queues = new Map(); // guildId -> { queue: [], player, connection }
|
||||
|
||||
function logToFile(message) {
|
||||
const logMessage = `[${new Date().toISOString()}] ${message}\n`;
|
||||
fs.appendFileSync(path.join(__dirname, 'bot.log'), logMessage);
|
||||
try {
|
||||
fs.appendFileSync(path.join(__dirname, 'bot.log'), logMessage);
|
||||
} catch (err) {
|
||||
console.error('Failed to write to log file:', err);
|
||||
}
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
// Better Error Handling for DisTube
|
||||
distube
|
||||
.on('playSong', (queue, song) => {
|
||||
logToFile(`Playing: ${song.name}`);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor('#00FF00')
|
||||
.setTitle('🎶 Sedang Memutar')
|
||||
.setDescription(`**${song.name}**`)
|
||||
.addFields(
|
||||
{ name: 'Durasi', value: song.formattedDuration, inline: true },
|
||||
{ name: 'Platform', value: song.source.toUpperCase(), inline: true }
|
||||
)
|
||||
.setThumbnail(song.thumbnail);
|
||||
queue.textChannel.send({ embeds: [embed] });
|
||||
})
|
||||
.on('addSong', (queue, song) => {
|
||||
queue.textChannel.send(`✅ **${song.name}** ditambahkan ke antrean!`);
|
||||
})
|
||||
.on('error', (channel, e) => {
|
||||
const errorMsg = `DisTube Error: ${e.stack || e.message || e}`;
|
||||
logToFile(errorMsg);
|
||||
if (channel && channel.send) channel.send(`❌ Terjadi kesalahan audio: ${e.message.slice(0, 1000)}`);
|
||||
})
|
||||
.on('empty', queue => {
|
||||
if (queue.textChannel) queue.textChannel.send('Voice channel kosong, bot keluar...');
|
||||
})
|
||||
.on('searchNoResult', (message, query) => {
|
||||
if (message.channel) message.channel.send(`❌ Tidak ada hasil untuk: ${query}`);
|
||||
});
|
||||
|
||||
client.once(Events.ClientReady, async c => {
|
||||
logToFile(`Ready! Logged in as ${c.user.tag}`);
|
||||
logToFile(`FFMPEG Path: ${process.env.FFMPEG_PATH}`);
|
||||
|
||||
client.once(Events.ClientReady, () => {
|
||||
console.log('Bot berhasil login!');
|
||||
logToFile(`Ready! Logged in as ${client.user.tag}`);
|
||||
|
||||
// Sahur Alarm
|
||||
const sahurTime = process.env.SAHUR_TIME || '30 03 * * *';
|
||||
let cronTime = sahurTime;
|
||||
@ -94,10 +60,7 @@ client.once(Events.ClientReady, async c => {
|
||||
if (!guild) return;
|
||||
const channel = guild.channels.cache.get(VC_ID);
|
||||
if (channel) {
|
||||
distube.play(channel, SAHUR_AUDIO_PATH, {
|
||||
skip: true,
|
||||
textChannel: guild.systemChannel || guild.channels.cache.find(ch => ch.type === 0)
|
||||
});
|
||||
playLocal(channel, AUDIO_PATH);
|
||||
}
|
||||
}, null, true, 'Asia/Jakarta');
|
||||
logToFile(`Alarm scheduled at ${cronTime}`);
|
||||
@ -106,86 +69,163 @@ client.once(Events.ClientReady, async c => {
|
||||
}
|
||||
});
|
||||
|
||||
client.on(Events.InteractionCreate, async interaction => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const { commandName } = interaction;
|
||||
const voiceChannel = interaction.member?.voice.channel;
|
||||
async function playLocal(channel, filePath) {
|
||||
try {
|
||||
const connection = joinVoiceChannel({
|
||||
channelId: channel.id,
|
||||
guildId: channel.guild.id,
|
||||
adapterCreator: channel.guild.voiceAdapterCreator,
|
||||
});
|
||||
|
||||
if (['play', 'testsahur', 'skip', 'stop', 'pause', 'resume'].includes(commandName) && !voiceChannel) {
|
||||
return interaction.reply({ content: 'Kamu harus berada di voice channel!', ephemeral: true });
|
||||
const player = createAudioPlayer();
|
||||
const resource = createAudioResource(filePath);
|
||||
player.play(resource);
|
||||
connection.subscribe(player);
|
||||
|
||||
player.once(AudioPlayerStatus.Idle, () => {
|
||||
connection.destroy();
|
||||
});
|
||||
} catch (err) {
|
||||
logToFile(`Error playing local file: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
client.on(Events.MessageCreate, async (message) => {
|
||||
if (message.author.bot || !message.content.startsWith(PREFIX)) return;
|
||||
|
||||
const args = message.content.slice(PREFIX.length).trim().split(/ +/);
|
||||
const command = args.shift().toLowerCase();
|
||||
const voiceChannel = message.member?.voice.channel;
|
||||
|
||||
try {
|
||||
if (commandName === 'testsahur') {
|
||||
await interaction.deferReply();
|
||||
logToFile('Executing /testsahur...');
|
||||
if (command === 'join') {
|
||||
if (!voiceChannel) return message.reply('Anda harus berada di voice channel!');
|
||||
joinVoiceChannel({
|
||||
channelId: voiceChannel.id,
|
||||
guildId: message.guild.id,
|
||||
adapterCreator: message.guild.voiceAdapterCreator,
|
||||
});
|
||||
message.reply('Sudah join! 🎧');
|
||||
}
|
||||
|
||||
else if (command === 'testsahur') {
|
||||
if (!voiceChannel) return message.reply('Anda harus berada di voice channel!');
|
||||
if (!fs.existsSync(AUDIO_PATH)) return message.reply(`File audio tidak ditemukan di ${AUDIO_PATH}`);
|
||||
playLocal(voiceChannel, AUDIO_PATH);
|
||||
message.reply('Memainkan suara sahur... 📢');
|
||||
}
|
||||
|
||||
else if (command === 'play') {
|
||||
if (!voiceChannel) return message.reply('Anda harus berada di voice channel!');
|
||||
const query = args.join(' ');
|
||||
if (!query) return message.reply('Berikan link atau nama lagu!');
|
||||
|
||||
let serverQueue = queues.get(message.guild.id);
|
||||
|
||||
if (!serverQueue) {
|
||||
serverQueue = {
|
||||
songs: [],
|
||||
connection: null,
|
||||
player: createAudioPlayer({
|
||||
behaviors: { noSubscriber: NoSubscriberBehavior.Play }
|
||||
}),
|
||||
};
|
||||
queues.set(message.guild.id, serverQueue);
|
||||
}
|
||||
|
||||
message.reply('Mencari lagu... 🔍');
|
||||
|
||||
if (!fs.existsSync(SAHUR_AUDIO_PATH)) {
|
||||
throw new Error(`File tidak ditemukan di: ${SAHUR_AUDIO_PATH}`);
|
||||
let songInfo;
|
||||
if (play.sp_validate(query) === 'track') {
|
||||
const sp_data = await play.spotify(query);
|
||||
const search = await play.search(`${sp_data.name} ${sp_data.artists[0].name}`, { limit: 1 });
|
||||
songInfo = { title: sp_data.name, url: search[0].url };
|
||||
} else if (play.so_validate(query)) {
|
||||
const so_data = await play.soundcloud(query);
|
||||
songInfo = { title: so_data.name, url: so_data.url };
|
||||
} else {
|
||||
const yt_info = await play.search(query, { limit: 1 });
|
||||
if (yt_info.length === 0) return message.channel.send('Lagu tidak ditemukan!');
|
||||
songInfo = { title: yt_info[0].title, url: yt_info[0].url };
|
||||
}
|
||||
|
||||
await distube.play(voiceChannel, SAHUR_AUDIO_PATH, {
|
||||
skip: true,
|
||||
member: interaction.member,
|
||||
textChannel: interaction.channel
|
||||
});
|
||||
await interaction.editReply('📢 Memutar suara Sahur untuk pengetesan!');
|
||||
}
|
||||
serverQueue.songs.push(songInfo);
|
||||
|
||||
else if (commandName === 'play') {
|
||||
const query = interaction.options.getString('query');
|
||||
await interaction.deferReply();
|
||||
logToFile(`Executing /play with query: ${query}`);
|
||||
|
||||
if (query.toLowerCase() === 'sahur') {
|
||||
await distube.play(voiceChannel, SAHUR_AUDIO_PATH, {
|
||||
skip: true,
|
||||
member: interaction.member,
|
||||
textChannel: interaction.channel
|
||||
});
|
||||
return interaction.editReply('📢 Memainkan audio Sahur lokal...');
|
||||
if (serverQueue.songs.length === 1) {
|
||||
playSong(message.guild.id, voiceChannel);
|
||||
message.channel.send(`🎵 Sekarang memutar: **${songInfo.title}**`);
|
||||
} else {
|
||||
message.channel.send(`✅ **${songInfo.title}** ditambahkan ke antrean.`);
|
||||
}
|
||||
|
||||
await distube.play(voiceChannel, query, {
|
||||
textChannel: interaction.channel,
|
||||
member: interaction.member
|
||||
});
|
||||
await interaction.editReply(`🔍 Mencari: **${query}**`);
|
||||
}
|
||||
|
||||
else if (commandName === 'pause') {
|
||||
await interaction.deferReply();
|
||||
distube.pause(interaction.guildId);
|
||||
await interaction.editReply('Musik dipause. ⏸️');
|
||||
else if (command === 'skip') {
|
||||
const serverQueue = queues.get(message.guild.id);
|
||||
if (!serverQueue) return message.reply('Tidak ada lagu yang sedang diputar!');
|
||||
serverQueue.player.stop();
|
||||
message.reply('Lagu dilewati! ⏭️');
|
||||
}
|
||||
|
||||
else if (commandName === 'resume') {
|
||||
await interaction.deferReply();
|
||||
distube.resume(interaction.guildId);
|
||||
await interaction.editReply('Musik dilanjutkan. ▶️');
|
||||
else if (command === 'stop') {
|
||||
const serverQueue = queues.get(message.guild.id);
|
||||
if (serverQueue) {
|
||||
serverQueue.songs = [];
|
||||
serverQueue.player.stop();
|
||||
if (serverQueue.connection) serverQueue.connection.destroy();
|
||||
queues.delete(message.guild.id);
|
||||
}
|
||||
message.reply('Musik dihentikan. 👋');
|
||||
}
|
||||
|
||||
else if (commandName === 'skip') {
|
||||
await interaction.deferReply();
|
||||
await distube.skip(interaction.guildId);
|
||||
await interaction.editReply('Lagu dilewati! ⏭️');
|
||||
else if (command === 'pause') {
|
||||
const serverQueue = queues.get(message.guild.id);
|
||||
if (serverQueue) serverQueue.player.pause();
|
||||
message.reply('Dipause. ⏸️');
|
||||
}
|
||||
|
||||
else if (commandName === 'stop') {
|
||||
await interaction.deferReply();
|
||||
await distube.stop(interaction.guildId);
|
||||
await interaction.editReply('Musik dihentikan.');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logToFile(`Command Error (${commandName}): ${err.stack || err.message}`);
|
||||
if (interaction.deferred || interaction.replied) {
|
||||
await interaction.editReply(`❌ Error: ${err.message}`);
|
||||
} else {
|
||||
await interaction.reply({ content: `❌ Error: ${err.message}`, ephemeral: true });
|
||||
else if (command === 'resume') {
|
||||
const serverQueue = queues.get(message.guild.id);
|
||||
if (serverQueue) serverQueue.player.unpause();
|
||||
message.reply('Dilanjutkan. ▶️');
|
||||
}
|
||||
} catch (error) {
|
||||
logToFile(`Command Error (${command}): ${error.stack || error.message}`);
|
||||
message.reply(`❌ Terjadi kesalahan: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
async function playSong(guildId, channel) {
|
||||
const serverQueue = queues.get(guildId);
|
||||
if (!serverQueue || serverQueue.songs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const song = serverQueue.songs[0];
|
||||
const stream = await play.stream(song.url);
|
||||
const resource = createAudioResource(stream.stream, { inputType: stream.type });
|
||||
|
||||
if (!serverQueue.connection) {
|
||||
serverQueue.connection = joinVoiceChannel({
|
||||
channelId: channel.id,
|
||||
guildId: guildId,
|
||||
adapterCreator: channel.guild.voiceAdapterCreator,
|
||||
});
|
||||
serverQueue.connection.subscribe(serverQueue.player);
|
||||
}
|
||||
|
||||
serverQueue.player.play(resource);
|
||||
|
||||
serverQueue.player.once(AudioPlayerStatus.Idle, () => {
|
||||
serverQueue.songs.shift();
|
||||
playSong(guildId, channel);
|
||||
});
|
||||
} catch (err) {
|
||||
logToFile(`Playback Error: ${err.message}`);
|
||||
serverQueue.songs.shift();
|
||||
playSong(guildId, channel);
|
||||
}
|
||||
}
|
||||
|
||||
// Login at the very bottom
|
||||
client.login(process.env.DISCORD_TOKEN);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user