Compare commits

...

7 Commits

Author SHA1 Message Date
Flatlogic Bot
efb0d9f141 Autosave: 20260218-022345 2026-02-18 02:23:46 +00:00
Flatlogic Bot
0e42ce02fa Autosave: 20260217-191159 2026-02-17 19:11:59 +00:00
Flatlogic Bot
2c5ca6a915 ttt 2026-02-17 18:48:38 +00:00
Flatlogic Bot
44e4b520c3 Autosave: 20260217-184206 2026-02-17 18:42:06 +00:00
Flatlogic Bot
2d98a7e127 V6 2026-02-17 18:03:41 +00:00
Flatlogic Bot
28d4436944 Autosave: 20260217-175814 2026-02-17 17:58:14 +00:00
Flatlogic Bot
3380578641 Autosave: 20260217-173440 2026-02-17 17:34:40 +00:00
12 changed files with 2572 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

16
bot.log Normal file
View File

@ -0,0 +1,16 @@
[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 Normal file
View File

@ -0,0 +1,206 @@
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,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>

1833
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"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"
}
}

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());
}
}

1
test.js Normal file
View File

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

1
test.log Normal file
View File

@ -0,0 +1 @@
test