Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

12 changed files with 307 additions and 2562 deletions

2
.env
View File

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

View File

@ -1,283 +1,302 @@
: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-color: var(--discord-dark);
color: var(--discord-text);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
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;
margin: 0;
padding: 0;
min-height: 100vh;
}
.main-wrapper {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
position: relative;
z-index: 2;
}
.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 {
justify-content: center;
min-height: 100vh;
width: 100%;
padding: 1rem;
background: var(--discord-darker);
border: 1px solid #444;
border-radius: 8px;
color: white;
margin-bottom: 1.5rem;
font-size: 1rem;
padding: 20px;
box-sizing: border-box;
position: relative;
z-index: 1;
}
.config-form input:focus {
outline: none;
border-color: var(--discord-blurple);
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.mock-chat {
background-color: var(--discord-darker);
border-radius: 12px;
height: 400px;
.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;
margin-top: 2rem;
border: 1px solid rgba(255,255,255,0.05);
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;
display: flex;
justify-content: space-between;
align-items: center;
}
.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 {
margin-bottom: 1.5rem;
display: flex;
gap: 1rem;
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);
}
.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;
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.msg-content {
flex: 1;
.message.visitor {
align-self: flex-end;
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
color: #fff;
border-bottom-right-radius: 4px;
}
.msg-author {
font-weight: 700;
font-size: 1rem;
margin-bottom: 0.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
.message.bot {
align-self: flex-start;
background: #ffffff;
color: #212529;
border-bottom-left-radius: 4px;
}
.bot-tag {
background-color: var(--discord-blurple);
font-size: 0.65rem;
padding: 0.1rem 0.4rem;
border-radius: 4px;
text-transform: uppercase;
}
.msg-text {
font-size: 1rem;
color: var(--discord-text-muted);
line-height: 1.4;
}
.embed {
background-color: var(--discord-darkest);
border-left: 4px solid var(--discord-blurple);
.chat-input-area {
padding: 1.25rem;
border-radius: 4px;
margin-top: 0.75rem;
max-width: 450px;
background: rgba(255, 255, 255, 0.5);
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
.embed-title {
font-weight: 700;
color: white;
margin-bottom: 0.5rem;
.chat-input-area form {
display: flex;
gap: 0.75rem;
}
.embed-description {
font-size: 0.9rem;
color: var(--discord-text-muted);
.chat-input-area input {
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;
}
.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;
.chat-input-area input:focus {
border-color: #23a6d5;
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
}
@keyframes slideUp {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
.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;
}
.bg-decorations {
.chat-input-area button:hover {
background: #000;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
/* Background Animations */
.bg-animations {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.circle {
.blob {
position: absolute;
width: 500px;
height: 500px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
background: radial-gradient(circle, rgba(88, 101, 242, 0.1) 0%, transparent 70%);
filter: blur(80px);
animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
}
.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,76 +1,39 @@
document.addEventListener('DOMContentLoaded', () => {
const mockForm = document.getElementById('mock-chat-form');
const mockInput = document.getElementById('mock-chat-input');
const mockMessages = document.getElementById('mock-chat-messages');
const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatMessages = document.getElementById('chat-messages');
const appendMockMessage = (content, author = 'You', isBot = false, embed = null) => {
const appendMessage = (text, sender) => {
const msgDiv = document.createElement('div');
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;
msgDiv.classList.add('message', sender);
msgDiv.textContent = text;
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
};
if (mockForm) {
mockForm.addEventListener('submit', (e) => {
e.preventDefault();
const val = mockInput.value.trim();
if (!val) return;
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const message = chatInput.value.trim();
if (!message) return;
appendMockMessage(val, 'User');
mockInput.value = '';
appendMessage(message, 'visitor');
chatInput.value = '';
// Bot logic simulation
setTimeout(() => {
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'
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
setTimeout(() => {
appendMessage(data.reply, 'bot');
}, 500);
} catch (error) {
console.error('Error:', error);
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
}
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 KiB

16
bot.log
View File

@ -1,16 +0,0 @@
[dotenv@17.3.1] injecting env (2) from .env -- tip: ⚙️ override existing env vars with { override: true }
Logging in...
✅ Server Keep-Alive aktif di port 8080
Unhandled Rejection: Error [TokenInvalid]: An invalid token was provided.
at WebSocketManager.connect (/home/ubuntu/executor/workspace/node_modules/discord.js/src/client/websocket/WebSocketManager.js:140:26)
at Client.login (/home/ubuntu/executor/workspace/node_modules/discord.js/src/client/Client.js:229:21)
at Object.<anonymous> (/home/ubuntu/executor/workspace/index.js:206:8)
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)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:171:5) {
code: 'TokenInvalid'
}

206
index.js
View File

@ -1,206 +0,0 @@
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 { GoogleGenerativeAI } = require('@google/generative-ai');
const express = require('express');
require('dotenv').config();
// 1. Keep-Alive Server (Port 8080)
const app = express();
app.get('/', (req, res) => res.send('Wizzy Bot is Online!'));
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => console.log(`✅ Server Keep-Alive aktif di port ${PORT}`));
// 2. Gemini AI Setup
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });
// 3. Discord Client Setup
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
]
});
// 4. DisTube Setup (Music)
const distube = new DisTube(client, {
emitNewSongOnly: true,
emitAddSongWhenCreatingQueue: false,
emitAddListWhenCreatingQueue: false,
plugins: [
new YouTubePlugin(),
new YtDlpPlugin()
]
});
// 5. Slash Commands Definition
const commands = [
new SlashCommandBuilder()
.setName('wizzy')
.setDescription('Tanya Wizzy (Gemini AI)')
.addStringOption(option =>
option.setName('prompt')
.setDescription('Pertanyaanmu untuk Wizzy')
.setRequired(true)),
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());
// 6. Interaction Handling (Slash Commands)
client.on('interactionCreate', async interaction => {
// ⚡ INSTANT DEFER: Prevent 3s Timeout
if (interaction.isChatInputCommand()) {
await interaction.deferReply().catch(e => console.error('Defer Error:', e));
}
if (!interaction.isChatInputCommand()) return;
console.log(`--- Perintah masuk: /${interaction.commandName} ---`);
const { commandName } = interaction;
// --- WIZZY (GEMINI AI) HANDLER ---
if (commandName === 'wizzy') {
try {
const userInput = interaction.options.getString('prompt') ?? 'Halo!';
const result = await model.generateContent(userInput);
const response = result.response.text();
const reply = response.length > 1990 ? response.substring(0, 1990) + '...' : response;
await interaction.editReply(reply);
} catch (error) {
console.error('❌ Gemini Error:', error);
await interaction.editReply({ content: '⚠️ Wizzy mengalami error, coba lagi nanti.', ephemeral: true });
}
return;
}
// --- MUSIC HANDLERS ---
if (commandName === 'play') {
try {
const query = interaction.options.getString('query');
if (!interaction.member.voice.channel) {
return interaction.editReply('Kamu harus berada di voice channel!');
}
await interaction.editReply({ content: `🔍 Mencari: **${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: `❌ Error: ${error.message}` });
}
} else if (commandName === 'skip') {
try {
const queue = distube.getQueue(interaction.guild);
if (!queue) return interaction.editReply('❌ Tidak ada lagu!');
await distube.skip(interaction.guild);
await interaction.editReply('⏭️ Lagu dilewati!');
} catch (e) { await interaction.editReply(`❌ Error: ${e.message}`); }
} else if (commandName === 'stop') {
try {
await distube.stop(interaction.guild);
await interaction.editReply('⏹️ Musik berhenti!');
} catch (e) { await interaction.editReply(`❌ Error: ${e.message}`); }
} else if (commandName === 'pause') {
try {
distube.pause(interaction.guild);
await interaction.editReply('⏸️ Musik dijeda!');
} catch (e) { await interaction.editReply(`❌ Error: ${e.message}`); }
} else if (commandName === 'resume') {
try {
distube.resume(interaction.guild);
await interaction.editReply('▶️ Musik lanjut!');
} catch (e) { await interaction.editReply(`❌ Error: ${e.message}`); }
} else if (commandName === 'queue') {
try {
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 (e) { await interaction.editReply(`❌ Error: ${e.message}`); }
}
});
// 7. Message Handling (Prefix Command !wizzy)
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
if (!message.content.startsWith('!wizzy ')) return;
const userInput = message.content.slice(7).trim();
if (!userInput) return message.reply('Tulis pertanyaanmu!');
await message.channel.sendTyping();
try {
const result = await model.generateContent(userInput);
const response = result.response.text();
const reply = response.length > 1990 ? response.substring(0, 1990) + '...' : response;
await message.reply(reply);
} catch (error) {
console.error('❌ Error Gemini (Prefix):', error);
await message.reply('⚠️ Terjadi error pada Wizzy!');
}
});
// 8. DisTube Events
distube
.on('playSong', (queue, song) => {
queue.textChannel.send(`🎶 Memutar: **${song.name}** (\`${song.formattedDuration}\`)`);
})
.on('addSong', (queue, song) => {
queue.textChannel.send(`✅ Ditambahkan: **${song.name}**`);
})
.on('error', (channel, e) => {
if (channel?.send) channel.send(`❌ DisTube Error: ${e.message.slice(0, 1000)}`);
console.error('DisTube Error:', e);
});
// 9. Ready Event & Command Registration
client.once('ready', async () => {
console.log(`✅ Wizzy aktif sebagai ${client.user.tag}`);
console.log('Bot siap memproses perintah!');
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
try {
console.log('Registering slash commands...');
await rest.put(
Routes.applicationCommands(client.user.id),
{ body: commands },
);
console.log('Successfully registered slash commands!');
} catch (error) {
console.error('Registration Error:', error);
}
});
// 10. Anti-Crash
process.on('unhandledRejection', e => console.error('Unhandled Rejection:', e));
process.on('uncaughtException', e => console.error('Uncaught Exception:', e));
// 11. Login
console.log('Logging in...');
client.login(process.env.DISCORD_TOKEN);

181
index.php
View File

@ -1,17 +1,6 @@
<?php
require_once __DIR__ . '/db/config.php';
// 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';
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Modern AI-ready Chat Assistant';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<!doctype html>
@ -19,153 +8,45 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MusicBot Dashboard</title>
<title>Chat Assistant</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;800&family=Fira+Code:wght@400;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<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 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>
</div>
</div>
<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; ?>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>
</html>

1833
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
{
"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",
"@google/generative-ai": "^0.24.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",
"play-dl": "^1.9.7"
}
}

View File

@ -1,29 +0,0 @@
<?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());
}
}

View File

@ -1 +0,0 @@
console.log('test')

View File

@ -1 +0,0 @@
test