Autosave: 20260217-173440

This commit is contained in:
Flatlogic Bot 2026-02-17 17:34:40 +00:00
parent 7406216157
commit 3380578641
10 changed files with 2475 additions and 317 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
DISCORD_TOKEN=MTQ3Mjc2ODQ4NTQ0NzY5NjY1MA.GPu0MI.AGkSXyQrBdV-5cy6wsYX-KmaNbbZljX_7_UiPA
PORT=8080

View File

@ -1,302 +1,283 @@
:root {
--discord-blurple: #5865F2;
--discord-green: #3BA55D;
--discord-yellow: #FAA61A;
--discord-red: #ED4245;
--discord-dark: #2F3136;
--discord-darker: #23272A;
--discord-darkest: #1E1F22;
--discord-text: #FFFFFF;
--discord-text-muted: #B9BBBE;
--discord-card: #36393F;
}
body {
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
color: #212529;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px;
background-color: var(--discord-dark);
color: var(--discord-text);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
margin: 0;
padding: 0;
min-height: 100vh;
}
.main-wrapper {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
width: 100%;
padding: 20px;
box-sizing: border-box;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
position: relative;
z-index: 1;
z-index: 2;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.chat-container {
width: 100%;
max-width: 600px;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 20px;
display: flex;
flex-direction: column;
height: 85vh;
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
overflow: hidden;
}
.chat-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
background: rgba(255, 255, 255, 0.5);
font-weight: 700;
font-size: 1.1rem;
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: var(--discord-darkest);
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--discord-blurple);
text-decoration: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
.hero {
text-align: center;
padding: 4rem 1rem;
background: linear-gradient(180deg, var(--discord-darkest) 0%, var(--discord-dark) 100%);
border-radius: 0 0 50px 50px;
margin-bottom: 3rem;
margin-top: -2rem;
}
.hero h1 {
font-size: 3.5rem;
margin-bottom: 1rem;
font-weight: 800;
}
.hero p {
color: var(--discord-text-muted);
font-size: 1.25rem;
max-width: 700px;
margin: 0 auto 2.5rem;
}
.btn-primary {
background-color: var(--discord-blurple);
color: white !important;
padding: 1rem 2.5rem;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: all 0.2s;
border: none;
cursor: pointer;
display: inline-block;
}
.btn-primary:hover {
background-color: #4752C4;
transform: translateY(-2px);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
margin-bottom: 4rem;
}
.card {
background-color: var(--discord-card);
border-radius: 12px;
padding: 2rem;
border: 1px solid rgba(255,255,255,0.05);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card h3 {
margin-top: 0;
color: var(--discord-blurple);
font-size: 1.5rem;
margin-bottom: 1.5rem;
}
.command-item {
display: flex;
justify-content: space-between;
padding: 0.75rem 0;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.command-name {
font-family: 'Fira Code', monospace;
color: var(--discord-green);
font-weight: 600;
}
.setup-section {
background-color: var(--discord-darkest);
padding: 3rem;
border-radius: 20px;
margin-top: 4rem;
border: 1px solid rgba(88, 101, 242, 0.2);
}
.setup-section h2 {
font-size: 2rem;
margin-bottom: 2rem;
}
pre {
background-color: #000;
padding: 1.5rem;
border-radius: 12px;
overflow-x: auto;
font-size: 0.9rem;
color: #e6e6e6;
border: 1px solid #333;
line-height: 1.6;
}
.config-form input {
width: 100%;
padding: 1rem;
background: var(--discord-darker);
border: 1px solid #444;
border-radius: 8px;
color: white;
margin-bottom: 1.5rem;
font-size: 1rem;
}
.config-form input:focus {
outline: none;
border-color: var(--discord-blurple);
}
.mock-chat {
background-color: var(--discord-darker);
border-radius: 12px;
height: 400px;
display: flex;
flex-direction: column;
margin-top: 2rem;
border: 1px solid rgba(255,255,255,0.05);
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.25rem;
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.message {
max-width: 85%;
padding: 0.85rem 1.1rem;
border-radius: 16px;
line-height: 1.5;
font-size: 0.95rem;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.message.visitor {
align-self: flex-end;
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
color: #fff;
border-bottom-right-radius: 4px;
}
.message.bot {
align-self: flex-start;
background: #ffffff;
color: #212529;
border-bottom-left-radius: 4px;
}
.chat-input-area {
padding: 1.25rem;
background: rgba(255, 255, 255, 0.5);
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
.chat-input-area form {
margin-bottom: 1.5rem;
display: flex;
gap: 0.75rem;
gap: 1rem;
}
.chat-input-area input {
.avatar {
width: 45px;
height: 45px;
background-color: var(--discord-blurple);
border-radius: 50%;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.msg-content {
flex: 1;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
padding: 0.75rem 1rem;
outline: none;
background: rgba(255, 255, 255, 0.9);
transition: all 0.3s ease;
}
.chat-input-area input:focus {
border-color: #23a6d5;
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
.msg-author {
font-weight: 700;
font-size: 1rem;
margin-bottom: 0.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.chat-input-area button {
background: #212529;
color: #fff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 12px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
.bot-tag {
background-color: var(--discord-blurple);
font-size: 0.65rem;
padding: 0.1rem 0.4rem;
border-radius: 4px;
text-transform: uppercase;
}
.chat-input-area button:hover {
background: #000;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
.msg-text {
font-size: 1rem;
color: var(--discord-text-muted);
line-height: 1.4;
}
/* Background Animations */
.bg-animations {
.embed {
background-color: var(--discord-darkest);
border-left: 4px solid var(--discord-blurple);
padding: 1.25rem;
border-radius: 4px;
margin-top: 0.75rem;
max-width: 450px;
}
.embed-title {
font-weight: 700;
color: white;
margin-bottom: 0.5rem;
}
.embed-description {
font-size: 0.9rem;
color: var(--discord-text-muted);
}
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background-color: var(--discord-green);
color: white;
padding: 1rem 2rem;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
z-index: 1000;
display: none;
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.bg-decorations {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.blob {
.circle {
position: absolute;
width: 500px;
height: 500px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
filter: blur(80px);
animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
background: radial-gradient(circle, rgba(88, 101, 242, 0.1) 0%, transparent 70%);
}
.blob-1 {
top: -10%;
left: -10%;
background: rgba(238, 119, 82, 0.4);
}
.blob-2 {
bottom: -10%;
right: -10%;
background: rgba(35, 166, 213, 0.4);
animation-delay: -7s;
width: 600px;
height: 600px;
}
.blob-3 {
top: 40%;
left: 30%;
background: rgba(231, 60, 126, 0.3);
animation-delay: -14s;
width: 450px;
height: 450px;
}
@keyframes move {
0% { transform: translate(0, 0) rotate(0deg) scale(1); }
33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
100% { transform: translate(0, 0) rotate(360deg) scale(1); }
}
.admin-link {
font-size: 14px;
color: #fff;
text-decoration: none;
background: rgba(0, 0, 0, 0.2);
padding: 0.5rem 1rem;
border-radius: 8px;
transition: all 0.3s ease;
}
.admin-link:hover {
background: rgba(0, 0, 0, 0.4);
text-decoration: none;
}
/* Admin Styles */
.admin-container {
max-width: 900px;
margin: 3rem auto;
padding: 2.5rem;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 24px;
box-shadow: 0 20px 50px rgba(0,0,0,0.15);
border: 1px solid rgba(255, 255, 255, 0.4);
position: relative;
z-index: 1;
}
.admin-container h1 {
margin-top: 0;
color: #212529;
font-weight: 800;
}
.table {
width: 100%;
border-collapse: separate;
border-spacing: 0 8px;
margin-top: 1.5rem;
}
.table th {
background: transparent;
border: none;
padding: 1rem;
color: #6c757d;
font-weight: 600;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 1px;
}
.table td {
background: #fff;
padding: 1rem;
border: none;
}
.table tr td:first-child { border-radius: 12px 0 0 12px; }
.table tr td:last-child { border-radius: 0 12px 12px 0; }
.form-group {
margin-bottom: 1.25rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
font-size: 0.9rem;
}
.form-control {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
background: #fff;
transition: all 0.3s ease;
box-sizing: border-box;
}
.form-control:focus {
outline: none;
border-color: #23a6d5;
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
}

View File

@ -1,39 +1,76 @@
document.addEventListener('DOMContentLoaded', () => {
const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatMessages = document.getElementById('chat-messages');
const mockForm = document.getElementById('mock-chat-form');
const mockInput = document.getElementById('mock-chat-input');
const mockMessages = document.getElementById('mock-chat-messages');
const appendMessage = (text, sender) => {
const appendMockMessage = (content, author = 'You', isBot = false, embed = null) => {
const msgDiv = document.createElement('div');
msgDiv.classList.add('message', sender);
msgDiv.textContent = text;
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
msgDiv.className = 'message';
let avatarText = author.substring(0, 2).toUpperCase();
if (isBot) avatarText = 'MB';
let html = `
<div class="avatar" style="${isBot ? 'background-color: #5865F2;' : 'background-color: #4F545C;'}">${avatarText}</div>
<div class="msg-content">
<div class="msg-author">${author} ${isBot ? '<span class="bot-tag">BOT</span>' : ''}</div>
<div class="msg-text">${content}</div>
`;
if (embed) {
html += `
<div class="embed">
<div class="embed-title">${embed.title}</div>
<div class="embed-description">${embed.description}</div>
</div>
`;
}
html += `</div>`;
msgDiv.innerHTML = html;
mockMessages.appendChild(msgDiv);
mockMessages.scrollTop = mockMessages.scrollHeight;
};
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const message = chatInput.value.trim();
if (!message) return;
if (mockForm) {
mockForm.addEventListener('submit', (e) => {
e.preventDefault();
const val = mockInput.value.trim();
if (!val) return;
appendMessage(message, 'visitor');
chatInput.value = '';
appendMockMessage(val, 'User');
mockInput.value = '';
try {
const response = await fetch('api/chat.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await response.json();
// Artificial delay for realism
// Bot logic simulation
setTimeout(() => {
appendMessage(data.reply, 'bot');
}, 500);
} catch (error) {
console.error('Error:', error);
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
}
if (val.startsWith('/play')) {
const song = val.replace('/play', '').trim() || 'Lofi Hip Hop';
appendMockMessage('🔍 Searching for `' + song + '`...', 'MusicBot', true);
setTimeout(() => {
appendMockMessage('🎶 Now playing:', 'MusicBot', true, {
title: song,
description: 'Requested by User • Duration: 03:45 • Platform: YouTube'
});
}, 1200);
} else if (val.startsWith('/skip')) {
appendMockMessage('⏭️ Skipped current song!', 'MusicBot', true);
} else if (val.startsWith('/stop')) {
appendMockMessage('⏹️ Stopped the player and left the voice channel.', 'MusicBot', true);
} else {
appendMockMessage('❓ Unknown command. Use /play to start music!', 'MusicBot', true);
}
}, 600);
});
}
// Smooth scroll for nav links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

20
bot.log Normal file
View File

@ -0,0 +1,20 @@
[dotenv@17.3.1] injecting env (2) from .env -- tip: 🛠️ run anywhere with `dotenvx run -- yourcommand`
/home/ubuntu/executor/workspace/node_modules/distube/dist/index.js:1288
if (invalidKey) throw new DisTubeError("INVALID_KEY", sourceName, invalidKey);
^
DisTubeError [INVALID_KEY]: 'ffmpegPath' does not need to be provided in DisTubeOptions
at checkInvalidKey (/home/ubuntu/executor/workspace/node_modules/distube/dist/index.js:1288:25)
at new Options (/home/ubuntu/executor/workspace/node_modules/distube/dist/index.js:1692:5)
at new DisTube (/home/ubuntu/executor/workspace/node_modules/distube/dist/index.js:2281:20)
at Object.<anonymous> (/home/ubuntu/executor/workspace/index.js:25:17)
at Module._compile (node:internal/modules/cjs/loader:1688:14)
at Object..js (node:internal/modules/cjs/loader:1820:10)
at Module.load (node:internal/modules/cjs/loader:1423:32)
at Function._load (node:internal/modules/cjs/loader:1246:12)
at TracingChannel.traceSync (node:diagnostics_channel:322:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:235:24) {
errorCode: 'INVALID_KEY'
}
Node.js v22.18.0

138
index.js Normal file
View File

@ -0,0 +1,138 @@
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');
const ffmpeg = require('ffmpeg-static');
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 => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
const voiceChannel = interaction.member.voice.channel;
if (!voiceChannel) {
return interaction.reply({ content: 'Kamu harus berada di Voice Channel untuk menggunakan perintah ini!', ephemeral: true });
}
// Anti-Stuck: Defer Reply
await interaction.deferReply();
try {
if (commandName === 'play') {
const query = interaction.options.getString('query');
await distube.play(voiceChannel, query, {
textChannel: interaction.channel,
member: interaction.member
});
await interaction.editReply(`🔍 Sedang mencari dan memutar: **${query}**`);
} else if (commandName === 'skip') {
await distube.skip(interaction.guild);
await interaction.editReply('⏭️ Lagu dilewati!');
} else if (commandName === 'stop') {
await distube.stop(interaction.guild);
await interaction.editReply('⏹️ Musik dihentikan dan bot keluar!');
} else if (commandName === 'pause') {
distube.pause(interaction.guild);
await interaction.editReply('⏸️ Musik dijeda!');
} else if (commandName === 'resume') {
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 ? 'Memutar:' : `${i}.`} ${song.name} - \`${song.formattedDuration}\``).join('\n');
await interaction.editReply(`🎶 **Antrean Saat Ini:**\n${q.slice(0, 2000)}`);
}
} catch (error) {
console.error(error);
await interaction.editReply(`❌ Terjadi kesalahan: ${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(`❌ Error: ${e.message.slice(0, 1900)}`);
console.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);
}
});
client.login(process.env.DISCORD_TOKEN);

181
index.php
View File

@ -1,6 +1,17 @@
<?php
require_once __DIR__ . '/db/config.php';
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Modern AI-ready Chat Assistant';
// Fetch current settings
$stmt = db()->query("SELECT setting_key, setting_value FROM bot_settings");
$settings = [];
while ($row = $stmt->fetch()) {
$settings[$row['setting_key']] = $row['setting_value'];
}
$botToken = $settings['bot_token'] ?? '';
$prefix = $settings['prefix'] ?? '/';
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Advanced Discord Music Bot powered by DisTube';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<!doctype html>
@ -8,45 +19,153 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chat Assistant</title>
<title>MusicBot Dashboard</title>
<?php if ($projectDescription): ?>
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>">
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>">
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>">
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Fira+Code:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<div class="bg-animations">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>
<div class="main-wrapper">
<div class="chat-container">
<div class="chat-header">
<span>Chat Assistant</span>
<a href="admin.php" class="admin-link">Admin</a>
</div>
<div class="chat-messages" id="chat-messages">
<div class="message bot">
Hello! I'm your assistant. How can I help you today?
</div>
</div>
<div class="chat-input-area">
<form id="chat-form">
<input type="text" id="chat-input" placeholder="Type your message..." autocomplete="off">
<button type="submit">Send</button>
</form>
<div class="bg-decorations">
<div class="circle" style="width: 600px; height: 600px; top: -200px; left: -200px;"></div>
<div class="circle" style="width: 800px; height: 800px; bottom: -300px; right: -300px;"></div>
</div>
<header class="header">
<a href="/" class="logo">
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-7c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5z"/></svg>
MusicBot
</a>
<nav>
<a href="#commands" style="color: white; margin-right: 1.5rem; text-decoration: none;">Commands</a>
<a href="#setup" style="color: white; margin-right: 1.5rem; text-decoration: none;">Setup</a>
<a href="#config" class="btn-primary" style="padding: 0.5rem 1.2rem;">Get Started</a>
</nav>
</header>
<div class="hero">
<h1>Your Music, Elevated.</h1>
<p>The most stable Discord music player with DisTube support. High-quality audio, zero timeout, and support for all your favorite platforms.</p>
<div style="display: flex; gap: 1rem; justify-content: center;">
<a href="#config" class="btn-primary">Configure Bot</a>
<a href="#commands" class="btn-primary" style="background-color: #4F545C;">View Commands</a>
</div>
</div>
</div>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<main class="main-wrapper">
<section id="commands" style="padding-top: 4rem;">
<h2 style="text-align: center; margin-bottom: 3rem; font-size: 2.5rem;">Powerful Commands</h2>
<div class="grid">
<div class="card">
<h3>🎵 Playback</h3>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>play</span> <span>Search & play song</span></div>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>skip</span> <span>Next song</span></div>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>stop</span> <span>Stop music & leave</span></div>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>pause</span> <span>Pause playback</span></div>
</div>
<div class="card">
<h3>📋 Queue</h3>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>queue</span> <span>Show current queue</span></div>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>nowplaying</span> <span>Current song info</span></div>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>shuffle</span> <span>Shuffle queue</span></div>
<div class="command-item"><span class="command-name"><?= htmlspecialchars($prefix) ?>volume</span> <span>Change volume</span></div>
</div>
<div class="card">
<h3>⚙️ Simulation</h3>
<p style="color: var(--discord-text-muted); font-size: 0.9rem;">Try typing <span style="color: var(--discord-green)">/play [song]</span> below to see the bot in action.</p>
<div class="mock-chat">
<div class="chat-messages" id="mock-chat-messages">
<div class="message">
<div class="avatar">MB</div>
<div class="msg-content">
<div class="msg-author">MusicBot <span class="bot-tag">BOT</span></div>
<div class="msg-text">Welcome! Use /play to start listening.</div>
</div>
</div>
</div>
<div style="padding: 1rem; border-top: 1px solid rgba(255,255,255,0.05);">
<form id="mock-chat-form" style="display: flex; gap: 0.5rem;">
<input type="text" id="mock-chat-input" placeholder="Type a command..." style="flex: 1; padding: 0.5rem; background: #40444B; border: none; border-radius: 4px; color: white;">
<button type="submit" style="display: none;">Send</button>
</form>
</div>
</div>
</div>
</div>
</section>
<section id="config" style="padding-top: 4rem;">
<div class="card" style="max-width: 600px; margin: 0 auto;">
<h3>Bot Configuration</h3>
<p style="color: var(--discord-text-muted); margin-bottom: 2rem;">Save your Discord bot credentials here. These settings will be stored in your private database.</p>
<form action="save_settings.php" method="POST" class="config-form">
<label>Discord Bot Token</label>
<input type="password" name="bot_token" value="<?= htmlspecialchars($botToken) ?>" placeholder="Enter your bot token...">
<label>Command Prefix</label>
<input type="text" name="prefix" value="<?= htmlspecialchars($prefix) ?>" placeholder="/">
<button type="submit" class="btn-primary" style="width: 100%;">Save Settings</button>
</form>
</div>
</section>
<section id="setup" class="setup-section">
<h2>Implementation Guide</h2>
<p>To run this bot locally or on your server, ensure you have Node.js installed and use the following code based on your requirements (DisTube v4 + Discord.js v14).</p>
<div style="margin-bottom: 1.5rem;">
<strong>1. Install Dependencies:</strong>
<pre>npm install discord.js distube @distube/yt-dlp play-dl ffmpeg-static @discordjs/voice</pre>
</div>
<div>
<strong>2. Bot Core Code (index.js):</strong>
<pre>const { Client, GatewayIntentBits } = require('discord.js');
const { DisTube } = require('distube');
const { YtDlpPlugin } = require('@distube/yt-dlp');
const ffmpeg = require('ffmpeg-static');
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]
});
const distube = new DisTube(client, {
emitNewSongOnly: true,
leaveOnFinish: false,
ffmpegPath: ffmpeg, // Fix for suara music
plugins: [new YtDlpPlugin()]
});
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'play') {
await interaction.deferReply(); // Anti-stuck
const query = interaction.options.getString('query');
distube.play(interaction.member.voice.channel, query, {
textChannel: interaction.channel,
member: interaction.member
});
await interaction.editReply(`Searching for: ${query}`);
}
});
client.login('YOUR_TOKEN_HERE');</pre>
</div>
</section>
</main>
<footer style="text-align: center; padding: 4rem 2rem; color: var(--discord-text-muted);">
<p>&copy; <?= date('Y') ?> MusicBot. Built for high-performance Discord communities.</p>
</footer>
<div id="toast" class="toast">Settings saved successfully!</div>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<?php if (isset($_GET['saved'])): ?>
<script>
document.getElementById('toast').style.display = 'block';
setTimeout(() => { document.getElementById('toast').style.display = 'none'; }, 3000);
</script>
<?php endif; ?>
</body>
</html>
</html>

1804
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "workspace",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@discordjs/voice": "^0.19.0",
"@distube/youtube": "^1.0.4",
"@distube/yt-dlp": "^2.0.1",
"@snazzah/davey": "^0.1.9",
"discord-api-types": "^0.38.39",
"discord.js": "^14.25.1",
"distube": "^5.2.3",
"dotenv": "^17.3.1",
"express": "^5.2.1",
"ffmpeg-static": "^5.3.0",
"is-ci": "^4.1.0",
"libsodium-wrappers": "^0.8.2",
"opusscript": "^0.0.8"
}
}

29
save_settings.php Normal file
View File

@ -0,0 +1,29 @@
<?php
require_once __DIR__ . '/db/config.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$botToken = $_POST['bot_token'] ?? '';
$prefix = $_POST['prefix'] ?? '/';
$db = db();
// UPSERT pattern for MySQL/MariaDB
$sql = "INSERT INTO bot_settings (setting_key, setting_value)
VALUES (:key1, :val1), (:key2, :val2)
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)";
try {
$stmt = $db->prepare("INSERT INTO bot_settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = ?");
$db->beginTransaction();
$stmt->execute(['bot_token', $botToken, $botToken]);
$stmt->execute(['prefix', $prefix, $prefix]);
$db->commit();
header('Location: index.php?saved=1#config');
exit;
} catch (Exception $e) {
if ($db->inTransaction()) $db->rollBack();
die("Error saving settings: " . $e->getMessage());
}
}