// server/index.js const express = require('express'); const path = require('node:path'); const { WebSocketServer, WebSocket } = require('ws'); const http = require('node:http'); require('dotenv').config(); const app = express(); const server = http.createServer(app); const wss = new WebSocketServer({ server }); // بخش خواندن دستورالعمل‌های شخصیت از Secrets const instructionSecretNames = { default: 'PERSONALITY_DEFAULT', teacher: 'PERSONALITY_TEACHER', poetic: 'PERSONALITY_POETIC', funny: 'PERSONALITY_FUNNY', }; const personalityInstructions = {}; console.log('در حال خواندن دستورالعمل‌های شخصیت از Secrets...'); Object.keys(instructionSecretNames).forEach(key => { const secretName = instructionSecretNames[key]; const instruction = process.env[secretName]; if (instruction) { personalityInstructions[key] = instruction; console.log(`- دستورالعمل '${key}' با موفقیت خوانده شد.`); } else { personalityInstructions[key] = `دستورالعمل '${key}' از سرور بارگذاری نشد.`; console.warn(`** هشدار: Secret با نام '${secretName}' یافت نشد! **`); } }); // بخش مدیریت کلیدهای API (کد هوشمند شما بدون تغییر) const apiKeysEnv = process.env.ALL_GEMINI_API_KEYS; const apiKeys = apiKeysEnv ? apiKeysEnv.split(',').map(key => key.trim()).filter(key => key) : []; if (apiKeys.length === 0) { console.error('🔴 خطای حیاتی: Secret با نام ALL_GEMINI_API_KEYS یافت نشد!'); process.exit(1); } console.log(`✅ تعداد ${apiKeys.length} کلید API بارگذاری شد.`); let currentKeyIndex = 0; const getStartingKeyIndex = () => { const index = currentKeyIndex % apiKeys.length; currentKeyIndex++; return index; }; // توابع attachGeminiEventHandlers و tryConnectToGemini شما اینجا بدون تغییر قرار می‌گیرند. // ... (برای کوتاهی اینجا حذف شده، اما شما باید آنها را نگه دارید) ... function attachGeminiEventHandlers(clientWs, geminiWs, apiKeyUsed) { geminiWs.on('message', data => clientWs.readyState === WebSocket.OPEN && clientWs.send(data, { binary: true })); geminiWs.on('error', error => { console.error(`🔴 خطای WebSocket جیمینای (کلید ...${apiKeyUsed.slice(-4)}):`, error.message); clientWs.readyState === WebSocket.OPEN && clientWs.close(); }); geminiWs.on('close', code => { if (code !== 1000) console.log(`🟡 اتصال جیمینای بسته شد (کلید ...${apiKeyUsed.slice(-4)}). کد: ${code}`); clientWs.readyState === WebSocket.OPEN && clientWs.close(); }); } async function tryConnectToGemini(clientWs, setupData, startIndex, attemptCount = 0) { if (attemptCount >= apiKeys.length) { console.error(`🔴 تمام ${apiKeys.length} کلید API ناموفق بودند.`); if (clientWs.readyState === WebSocket.OPEN) clientWs.send(JSON.stringify({ error: "خطای داخلی سرور." })) && clientWs.close(); return null; } const keyIndexToTry = (startIndex + attemptCount) % apiKeys.length; const apiKey = apiKeys[keyIndexToTry]; return new Promise(resolve => { const geminiWs = new WebSocket(`wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContent?key=${apiKey}`); const timeout = setTimeout(() => { geminiWs.removeAllListeners() && geminiWs.close(); console.warn(`🟡 اتصال با کلید ...${apiKey.slice(-4)} زمان‌بر شد. امتحان کلید بعدی...`); resolve(tryConnectToGemini(clientWs, setupData, startIndex, attemptCount + 1)); }, 8000); geminiWs.on('open', () => { clearTimeout(timeout); console.log(`✅ اتصال با کلید اندیس ${keyIndexToTry} موفق بود.`); try { geminiWs.send(JSON.stringify(setupData)); } catch (e) { clientWs.close(); resolve(null); return; } attachGeminiEventHandlers(clientWs, geminiWs, apiKey); resolve(geminiWs); }); geminiWs.on('error', () => { clearTimeout(timeout); geminiWs.removeAllListeners(); console.warn(`🟡 اتصال با کلید ...${apiKey.slice(-4)} ناموفق بود. امتحان کلید بعدی...`); resolve(tryConnectToGemini(clientWs, setupData, startIndex, attemptCount + 1)); }); }); } // سرو کردن فایل‌های استاتیک app.use(express.static(path.join(__dirname, '../build'))); // API Endpoint جدید برای ارسال دستورالعمل‌ها app.get('/api/instructions', (req, res) => res.json(personalityInstructions)); // مدیریت اتصال WebSocket کلاینت (بدون تغییر) wss.on('connection', ws => { const startingKeyIndex = getStartingKeyIndex(); console.log(`🔌 کلاینت جدید متصل شد. کلید شروع: اندیس ${startingKeyIndex}`); let geminiWs = null; ws.on('message', async message => { try { const data = JSON.parse(message.toString()); if (data.setup) { if (geminiWs) return; geminiWs = await tryConnectToGemini(ws, data, startingKeyIndex); } else if (geminiWs?.readyState === WebSocket.OPEN) { geminiWs.send(JSON.stringify(data)); } } catch (e) { /* خطا */ } }); ws.on('close', () => console.log(`🔌 کلاینت با کلید شروع اندیس ${startingKeyIndex} قطع شد.`) && geminiWs?.close()); ws.on('error', error => console.error(`🔴 خطای WebSocket کلاینت:`, error) && geminiWs?.close()); }); // رسیدگی به سایر درخواست‌ها app.get('*', (req, res) => res.sendFile(path.join(__dirname, '../build', 'index.html'))); const PORT = process.env.PORT || 3001; server.listen(PORT, () => console.log(`🚀 سرور در حال اجرا روی پورت ${PORT}`));