Autosave: 20260218-091142
This commit is contained in:
parent
3c6738376b
commit
eb4f306c8d
19
bot.log
19
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.
|
(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)
|
(Use `node --trace-deprecation ...` to show where the warning was created)
|
||||||
Successfully reloaded application (/) commands.
|
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
|
||||||
|
|||||||
238
index.js
238
index.js
@ -4,21 +4,16 @@ const {
|
|||||||
GatewayIntentBits,
|
GatewayIntentBits,
|
||||||
REST,
|
REST,
|
||||||
Routes,
|
Routes,
|
||||||
SlashCommandBuilder,
|
SlashCommandBuilder
|
||||||
ActionRowBuilder,
|
|
||||||
ButtonBuilder,
|
|
||||||
ButtonStyle
|
|
||||||
} = require('discord.js');
|
} = require('discord.js');
|
||||||
const {
|
const {
|
||||||
joinVoiceChannel,
|
joinVoiceChannel,
|
||||||
createAudioPlayer,
|
createAudioPlayer,
|
||||||
createAudioResource,
|
createAudioResource,
|
||||||
AudioPlayerStatus,
|
AudioPlayerStatus,
|
||||||
VoiceConnectionStatus
|
NoSubscriberBehavior
|
||||||
} = require('@discordjs/voice');
|
} = require('@discordjs/voice');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const multer = require('multer');
|
|
||||||
const cron = require('node-cron');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
@ -26,230 +21,119 @@ const path = require('path');
|
|||||||
const DISCORD_TOKEN = process.env.DISCORD_TOKEN;
|
const DISCORD_TOKEN = process.env.DISCORD_TOKEN;
|
||||||
const CLIENT_ID = process.env.CLIENT_ID;
|
const CLIENT_ID = process.env.CLIENT_ID;
|
||||||
const GUILD_ID = process.env.GUILD_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 app = express();
|
||||||
const port = 8080;
|
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
|
// Discord Client
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
GatewayIntentBits.Guilds,
|
GatewayIntentBits.Guilds,
|
||||||
GatewayIntentBits.GuildVoiceStates,
|
GatewayIntentBits.GuildVoiceStates,
|
||||||
GatewayIntentBits.GuildMessages,
|
|
||||||
GatewayIntentBits.MessageContent
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Bot Logic ---
|
let player = null;
|
||||||
async function playSahurAudio(targetVoiceId = VOICE_CHANNEL_ID) {
|
let connection = null;
|
||||||
const guild = client.guilds.cache.get(GUILD_ID);
|
let isLooping = false;
|
||||||
if (!guild) return console.error('Guild not found');
|
|
||||||
|
|
||||||
const voiceChannel = guild.channels.cache.get(targetVoiceId);
|
// Function to play local sahur.mp3
|
||||||
if (!voiceChannel) return console.error('Voice channel not found');
|
function playAudio() {
|
||||||
|
if (!connection) return;
|
||||||
|
|
||||||
const filePath = path.join(__dirname, 'uploads', 'sahur.mp3');
|
const filePath = path.join(__dirname, 'sahur.mp3');
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
const textChannel = guild.channels.cache.get(TEXT_CHANNEL_ID);
|
console.error('File sahur.mp3 tidak ditemukan!');
|
||||||
if (textChannel) textChannel.send('⚠️ Peringatan: file uploads/sahur.mp3 belum ada!');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = joinVoiceChannel({
|
if (!player) {
|
||||||
channelId: voiceChannel.id,
|
player = createAudioPlayer({
|
||||||
guildId: guild.id,
|
behaviors: {
|
||||||
adapterCreator: guild.voiceAdapterCreator,
|
noSubscriber: NoSubscriberBehavior.Play,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const player = createAudioPlayer();
|
|
||||||
const resource = createAudioResource(filePath);
|
|
||||||
|
|
||||||
player.play(resource);
|
|
||||||
connection.subscribe(player);
|
|
||||||
|
|
||||||
player.on(AudioPlayerStatus.Idle, () => {
|
player.on(AudioPlayerStatus.Idle, () => {
|
||||||
connection.destroy();
|
if (isLooping && connection) {
|
||||||
|
console.log('Looping: Memutar ulang sahur.mp3');
|
||||||
|
playAudio();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
player.on('error', error => {
|
player.on('error', error => {
|
||||||
console.error('Error playing audio:', error);
|
console.error('Error Audio Player:', error.message);
|
||||||
connection.destroy();
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const resource = createAudioResource(filePath);
|
||||||
|
player.play(resource);
|
||||||
|
connection.subscribe(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scheduled Job
|
// Slash Commands Definition
|
||||||
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
|
|
||||||
const commands = [
|
const commands = [
|
||||||
new SlashCommandBuilder()
|
new SlashCommandBuilder()
|
||||||
.setName('testsahur')
|
.setName('join')
|
||||||
.setDescription('Tes bunyi sahur di voice channel sekarang'),
|
.setDescription('Membuat bot masuk ke Voice Channel kamu'),
|
||||||
new SlashCommandBuilder()
|
new SlashCommandBuilder()
|
||||||
.setName('setsahur')
|
.setName('test_sahur')
|
||||||
.setDescription('Ubah jadwal sahur')
|
.setDescription('Memutar sahur.mp3 secara berulang (loop)'),
|
||||||
.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'),
|
|
||||||
].map(command => command.toJSON());
|
].map(command => command.toJSON());
|
||||||
|
|
||||||
const rest = new REST({ version: '10' }).setToken(DISCORD_TOKEN);
|
const rest = new REST({ version: '10' }).setToken(DISCORD_TOKEN);
|
||||||
|
|
||||||
async function registerCommands() {
|
async function registerCommands() {
|
||||||
try {
|
try {
|
||||||
console.log('Started refreshing application (/) commands.');
|
console.log('Refreshing application (/) commands...');
|
||||||
await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), { body: 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) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error('Error registering commands:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.on('ready', () => {
|
client.on('ready', () => {
|
||||||
console.log(`Bot logged in as ${client.user.tag}`);
|
console.log(`Bot Online: ${client.user.tag}`);
|
||||||
registerCommands();
|
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 => {
|
client.on('interactionCreate', async interaction => {
|
||||||
if (!interaction.isChatInputCommand()) return;
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
if (interaction.commandName === 'testsahur') {
|
if (interaction.commandName === 'join') {
|
||||||
const member = interaction.member;
|
const voiceChannel = interaction.member.voice.channel;
|
||||||
const voiceChannel = member.voice.channel;
|
|
||||||
if (!voiceChannel) {
|
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') {
|
connection = joinVoiceChannel({
|
||||||
sahurHour = interaction.options.getInteger('jam');
|
channelId: voiceChannel.id,
|
||||||
sahurMinute = interaction.options.getInteger('menit');
|
guildId: interaction.guild.id,
|
||||||
setupCron();
|
adapterCreator: interaction.guild.voiceAdapterCreator,
|
||||||
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}>`
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 ---
|
// Minimal Keep-Alive Web Server
|
||||||
app.use(express.static('public'));
|
app.get('/', (req, res) => res.send('Bot Sahur is Alive! 🌙'));
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.listen(port, () => console.log(`Keep-Alive server on port ${port}`));
|
||||||
|
|
||||||
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(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Sahur Bot Dashboard</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: sans-serif; background: #1a1a1a; color: #fff; padding: 20px; }
|
|
||||||
.card { background: #2a2a2a; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
|
|
||||||
input, button { padding: 10px; margin: 5px 0; border-radius: 4px; border: none; }
|
|
||||||
button { cursor: pointer; background: #5865f2; color: white; }
|
|
||||||
button:hover { background: #4752c4; }
|
|
||||||
.status-ok { color: #3ba55c; }
|
|
||||||
.status-err { color: #ed4245; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>🌙 Sahur Bot Dashboard</h1>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h2>📊 Status</h2>
|
|
||||||
<p>File sahur.mp3: <span class="${fileExists ? 'status-ok' : 'status-err'}">${fileExists ? 'Ready (' + fileSize + ')' : 'Missing'}</span></p>
|
|
||||||
<p>Jadwal: <strong>${sahurHour}:${sahurMinute.toString().padStart(2, '0')}</strong></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h2>📤 Upload Audio (sahur.mp3)</h2>
|
|
||||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
|
||||||
<input type="file" name="sahur" accept="audio/mpeg" required><br>
|
|
||||||
<button type="submit">Upload & Overwrite</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h2>🕒 Ubah Jadwal</h2>
|
|
||||||
<form action="/set-time" method="post">
|
|
||||||
Jam: <input type="number" name="hour" value="${sahurHour}" min="0" max="23" required>
|
|
||||||
Menit: <input type="number" name="minute" value="${sahurMinute}" min="0" max="59" required>
|
|
||||||
<button type="submit">Update Jadwal</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h2>⚡ Kontrol</h2>
|
|
||||||
<form action="/test" method="post">
|
|
||||||
<button type="submit" style="background: #3ba55c;">Test Play di Voice Channel</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/upload', upload.single('sahur'), (req, res) => {
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/set-time', (req, res) => {
|
|
||||||
sahurHour = parseInt(req.body.hour);
|
|
||||||
sahurMinute = parseInt(req.body.minute);
|
|
||||||
setupCron();
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/test', (req, res) => {
|
|
||||||
playSahurAudio();
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
|
||||||
console.log(`Dashboard running at http://localhost:${port}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.login(DISCORD_TOKEN);
|
client.login(DISCORD_TOKEN);
|
||||||
|
|||||||
@ -17,8 +17,6 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"ffmpeg-static": "^5.3.0",
|
"ffmpeg-static": "^5.3.0",
|
||||||
"libsodium-wrappers": "^0.8.2",
|
"libsodium-wrappers": "^0.8.2",
|
||||||
"multer": "^2.0.2",
|
|
||||||
"node-cron": "^4.2.1",
|
|
||||||
"opusscript": "^0.0.8"
|
"opusscript": "^0.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user