File size: 5,974 Bytes
0fd4af6
7f2a14a
 
 
 
0fd4af6
7f2a14a
 
 
 
 
0ded4c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ffbc23
0ded4c9
0fd4af6
 
0ded4c9
 
0fd4af6
3ffbc23
0ded4c9
3ffbc23
0ded4c9
 
3ffbc23
0ded4c9
 
 
 
0fd4af6
0ded4c9
 
 
 
 
 
 
 
 
0fd4af6
 
0ded4c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0fd4af6
0ded4c9
0fd4af6
3ffbc23
0ded4c9
0fd4af6
7f2a14a
0ded4c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f2a14a
 
0ded4c9
 
7f2a14a
0fd4af6
0ded4c9
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
// 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}`));