Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efb0d9f141 | ||
|
|
0e42ce02fa | ||
|
|
2c5ca6a915 | ||
|
|
44e4b520c3 | ||
|
|
2d98a7e127 | ||
|
|
28d4436944 | ||
|
|
3380578641 |
2
.env
Normal file
2
.env
Normal file
@ -0,0 +1,2 @@
|
||||
DISCORD_TOKEN=MTQ3Mjc2ODQ4NTQ0NzY5NjY1MA.GPu0MI.AGkSXyQrBdV-5cy6wsYX-KmaNbbZljX_7_UiPA
|
||||
PORT=8080
|
||||
@ -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);
|
||||
}
|
||||
@ -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'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
BIN
assets/pasted-20260217-172544-3aee9fad.jpg
Normal file
BIN
assets/pasted-20260217-172544-3aee9fad.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 558 KiB |
16
bot.log
Normal file
16
bot.log
Normal 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
206
index.js
Normal 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
181
index.php
@ -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>© <?= 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
1833
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
package.json
Normal file
30
package.json
Normal 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
29
save_settings.php
Normal 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());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user