File size: 8,335 Bytes
0ba4f29 b64dfae 0ba4f29 b64dfae 0ba4f29 b64dfae 0ba4f29 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | import express from 'express';
import { createServer as createViteServer } from 'vite';
import { GoogleGenAI } from '@google/genai';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
dotenv.config();
const resolvedFilename = typeof import.meta !== 'undefined' && import.meta.url
? fileURLToPath(import.meta.url)
: __filename;
const resolvedDirname = typeof import.meta !== 'undefined' && import.meta.url
? path.dirname(resolvedFilename)
: __dirname;
async function startServer() {
const app = express();
app.use(express.json());
// Initialize Gemini AI SDK
const apiKey = process.env.GEMINI_API_KEY;
const ai = apiKey ? new GoogleGenAI({
apiKey: apiKey,
httpOptions: {
headers: {
'User-Agent': 'aistudio-build',
}
}
}) : null;
// Health check
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', hasApiKey: !!apiKey });
});
// Suggest or search tracks using Gemini 3.5 Flash
app.post('/api/music/suggest', async (req, res) => {
try {
const { query } = req.body;
if (!query) {
return res.status(400).json({ error: 'Query is required.' });
}
if (!ai) {
// Fallback mock songs if no API Key
const mockResponses: Record<string, any[]> = {
'synthwave': [
{ title: 'Neon Skyline', artist: 'Laserhawk', duration: '3:45', genre: 'Synthwave' },
{ title: 'Miami Nights', artist: 'Outrun Kid', duration: '4:12', genre: 'Synthwave' },
{ title: 'Midnight City', artist: 'Retro Future', duration: '3:50', genre: 'Synthwave' }
],
'lofi': [
{ title: 'Rainy Cafe Cozy', artist: 'Lofi Butter', duration: '2:30', genre: 'Lofi Chill' },
{ title: 'Midnight Coffee', artist: 'Study Beats', duration: '3:05', genre: 'Lofi Chill' },
{ title: 'Warm Tea Lounge', artist: 'Snoozy Cat', duration: '2:45', genre: 'Lofi Chill' }
]
};
const key = query.toLowerCase();
let matched = mockResponses.lofi;
if (key.includes('synth') || key.includes('retro') || key.includes('wave') || key.includes('rock')) {
matched = mockResponses.synthwave;
} else {
matched = [
{ title: `${query} Club Mix`, artist: 'DJ Glizh', duration: '04:20', genre: 'Dance' },
{ title: `Acoustic ${query}`, artist: 'The Strings', duration: '03:15', genre: 'Acoustic' },
{ title: `${query} (Chill Version)`, artist: 'Echo Sound', duration: '03:40', genre: 'Chillout' }
];
}
return res.json({ tracks: matched });
}
const prompt = `Based on the search query: "${query}", suggest exactly 4 highly realistic/creative music tracks.
Please yield the response as a JSON array of objects with the fields "title", "artist", "duration" (format like MM:SS), and "genre".
Do not wrap in markdown or any code blocks, return only raw JSON. Ensure it is valid JSON.`;
const response = await ai.models.generateContent({
model: 'gemini-3.5-flash',
contents: prompt,
config: {
responseMimeType: 'application/json',
}
});
const responseText = response.text || '[]';
const parsed = JSON.parse(responseText.trim());
res.json({ tracks: parsed });
} catch (error: any) {
console.error('Gemini music search error:', error);
res.status(500).json({ error: error.message || 'Failed to suggest tracks.' });
}
});
// Chat with the Symphony AI assistant/Music bot inside the console
app.post('/api/music/chat', async (req, res) => {
try {
const { message, history = [] } = req.body;
if (!message) {
return res.status(400).json({ error: 'Message is required.' });
}
if (!ai) {
return res.json({
text: `🎸 **Glizh Bot** está listo para sonar! Intenta ejecutar comandos en nuestro Simulador de Terminal de Discord como \`/play provenza\` o \`/queue\`. (¡Configura un GEMINI_API_KEY para desbloquear respuestas inteligentes y listas generadas por IA!)`
});
}
// Contexto enriquecido para el chatbot asistente Glizh
const prompt = `
Eres "Glizh", el Bot de Música de Discord definitivo y su asistente de IA integrado.
Un usuario te ha enviado un mensaje en el simulador: "${message}"
Detalles clave del bot Glizh:
1. COMANDOS:
- /play [búsqueda o enlace]: reproduce cualquier canción o vibe (reggaeton, bachata, trap, pop, lofi) usando nuestro reproductor sintetizado en vivo.
- /skip: salta a la siguiente canción en la cola.
- /queue: muestra la lista de canciones en espera y la actual.
- /volume [0-150]: ajusta el nivel del volumen sintetizado.
- /bassboost [mode]: cambia el ecualizador de bajos (Off, Low, Heavy, Extreme).
- /lyrics: busca las letras de la canción actual.
- /filter [ambient | lofi | nightcore | vaporwave | normal]: aplica filtros de audio premium en tiempo real.
2. RESOLUCIÓN DE PROBLEMAS TÉCNICOS (ERRORES DE YOUTUBE / LAVALINK):
- Si preguntan sobre caídas, logs, o errores como "SocketTimeoutException" o "Read timed out" conectando a YouTube:
* Explica que YouTube bloquea/limita IPs de hosting públicos (como Render, Hugging Face o AWS).
* Recomienda configurar rotación de bloques IPv6 en "application.yml" de Lavalink en "ratelimit.ipBlocks".
* Sugiere usar la última versión del plugin "youtube-plugin" (1.18.1+) configurando tokens OAuth2/refreshToken.
* Recomienda usar proxies residenciales HTTP o SOCKS5 en la configuración.
COMPORTAMIENTO Y TONO:
- Responde en español fluido y conversacional de forma predeterminada (o inglés si el mensaje del usuario está en inglés).
- Mantén la respuesta súper concisa (máximo 2 a 4 oraciones) para que encaje perfectamente en la interfaz de chat.
- Usa negrita, listas o viñetas cortas para que sea fácil de leer de un vistazo.
- Añade emojis llamativos y tecnológicos (✨, 🔊, ⚡, 👑, 👾, 🎸, 🎶).
- ¡Demuestra mucho entusiasmo y un tono premium y festivo!
`;
const response = await ai.models.generateContent({
model: 'gemini-3.5-flash',
contents: prompt,
});
res.json({ text: response.text });
} catch (error: any) {
console.error('Gemini music chat error:', error);
res.status(500).json({ error: error.message || 'Failed to chat.' });
}
});
// Serve static assets or use Vite dev server
const isProd = process.env.NODE_ENV === 'production';
if (isProd) {
app.use(express.static(path.join(resolvedDirname, 'dist')));
app.get('*', (req, res) => {
res.sendFile(path.join(resolvedDirname, 'dist', 'index.html'));
});
} else {
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom',
});
app.use(vite.middlewares);
app.get('*', async (req, res, next) => {
const url = req.originalUrl;
try {
let template = await vite.transformIndexHtml(url, `
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Glizh - El Bot de Música de Discord Definitivo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap">
</head>
<body style="background-color: #09090b; margin: 0; padding: 0;">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
`);
res.status(200).set({ 'Content-Type': 'text/html' }).end(template);
} catch (e) {
vite.ssrFixStacktrace(e as Error);
next(e);
}
});
}
const port = parseInt(process.env.PORT || '7860');
app.listen(port, '0.0.0.0', () => {
console.log(`Server running at http://0.0.0.0:${port}`);
});
}
startServer();
|