rr1 commited on
Commit
6b6d0ea
·
verified ·
1 Parent(s): cac1531

Delete index.js

Browse files
Files changed (1) hide show
  1. index.js +0 -935
index.js DELETED
@@ -1,935 +0,0 @@
1
- import express from 'express';
2
- import fetch from 'node-fetch';
3
- import FormData from 'form-data';
4
- import dotenv from 'dotenv';
5
- import cors from 'cors';
6
- import puppeteer from 'puppeteer-extra'
7
- import StealthPlugin from 'puppeteer-extra-plugin-stealth'
8
- import { v4 as uuidv4 } from 'uuid';
9
- import Logger from './logger.js';
10
-
11
- dotenv.config();
12
-
13
- // 配置常量
14
- const CONFIG = {
15
- MODELS: {
16
- 'grok-2': 'grok-latest',
17
- 'grok-2-imageGen': 'grok-latest',
18
- 'grok-2-search': 'grok-latest',
19
- "grok-3": "grok-3",
20
- "grok-3-search": "grok-3",
21
- "grok-3-imageGen": "grok-3",
22
- "grok-3-deepsearch": "grok-3",
23
- "grok-3-reasoning": "grok-3"
24
- },
25
- API: {
26
- IS_CUSTOM_SSO: process.env.IS_CUSTOM_SSO === 'true',
27
- BASE_URL: "https://grok.com",
28
- API_KEY: process.env.API_KEY || "sk-123456",
29
- SIGNATURE_COOKIE: null,
30
- TEMP_COOKIE: null,
31
- PICGO_KEY: process.env.PICGO_KEY || null, //想要流式生图的话需要填入这个PICGO图床的key
32
- PICUI_KEY: process.env.PICUI_KEY || null //想要流式生图的话需要填入这个PICUI图床的key 两个图床二选一,默认使用PICGO
33
- },
34
- SERVER: {
35
- PORT: process.env.PORT || 3000,
36
- BODY_LIMIT: '5mb'
37
- },
38
- RETRY: {
39
- MAX_ATTEMPTS: 2//重试次数
40
- },
41
- SHOW_THINKING: process.env.SHOW_THINKING === 'true',
42
- IS_THINKING: false,
43
- IS_IMG_GEN: false,
44
- IS_IMG_GEN2: false,
45
- SSO_INDEX: 0,//sso的索引
46
- ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS === 'true',//是否显示搜索结果
47
- CHROME_PATH: process.env.CHROME_PATH || "/usr/bin/chromium"//chrome路径
48
- };
49
- puppeteer.use(StealthPlugin())
50
- // 请求头配置
51
- const DEFAULT_HEADERS = {
52
- 'accept': '*/*',
53
- 'accept-language': 'zh-CN,zh;q=0.9',
54
- 'accept-encoding': 'gzip, deflate, br, zstd',
55
- 'content-type': 'text/plain;charset=UTF-8',
56
- 'Connection': 'keep-alive',
57
- 'origin': 'https://grok.com',
58
- 'priority': 'u=1, i',
59
- 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
60
- 'sec-ch-ua-mobile': '?0',
61
- 'sec-ch-ua-platform': '"Windows"',
62
- 'sec-fetch-dest': 'empty',
63
- 'sec-fetch-mode': 'cors',
64
- 'sec-fetch-site': 'same-origin',
65
- 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
66
- 'baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
67
- };
68
-
69
-
70
- async function initialization() {
71
- if (CONFIG.API.IS_CUSTOM_SSO) {
72
- await Utils.get_signature()
73
- return;
74
- }
75
- const ssoArray = process.env.SSO.split(',');
76
- const ssorwArray = process.env.SSO_RW.split(',');
77
- ssoArray.forEach((sso, index) => {
78
- tokenManager.addToken(`sso-rw=${ssorwArray[index]};sso=${sso}`);
79
- });
80
- console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
81
- await Utils.get_signature()
82
- Logger.info("初始化完成", 'Server');
83
- }
84
-
85
-
86
- class AuthTokenManager {
87
- constructor() {
88
- this.activeTokens = [];
89
- this.expiredTokens = new Map();
90
- this.tokenModelFrequency = new Map();
91
- this.modelRateLimit = {
92
- "grok-3": { RequestFrequency: 20 },
93
- "grok-3-deepsearch": { RequestFrequency: 5 },
94
- "grok-3-reasoning": { RequestFrequency: 5 }
95
- };
96
- }
97
-
98
- addToken(token) {
99
- if (!this.activeTokens.includes(token)) {
100
- this.activeTokens.push(token);
101
- this.tokenModelFrequency.set(token, {
102
- "grok-3": 0,
103
- "grok-3-deepsearch": 0,
104
- "grok-3-reasoning": 0
105
- });
106
- }
107
- }
108
- setToken(token) {
109
- this.activeTokens = [token];
110
- this.tokenModelFrequency.set(token, {
111
- "grok-3": 0,
112
- "grok-3-deepsearch": 0,
113
- "grok-3-reasoning": 0
114
- });
115
- }
116
-
117
- getTokenByIndex(index, model) {
118
- if (this.activeTokens.length === 0) {
119
- return null;
120
- }
121
- const token = this.activeTokens[index];
122
- this.recordModelRequest(token, model);
123
- return token;
124
- }
125
-
126
- recordModelRequest(token, model) {
127
- if (model === 'grok-3-search' || model === 'grok-3-imageGen') {
128
- model = 'grok-3';
129
- }
130
-
131
- if (!this.modelRateLimit[model]) return;
132
- const tokenFrequency = this.tokenModelFrequency.get(token);
133
- if (tokenFrequency && tokenFrequency[model] !== undefined) {
134
- tokenFrequency[model]++;
135
- }
136
- this.checkAndRemoveTokenIfLimitReached(token);
137
- }
138
- setModelLimit(index, model) {
139
- if (model === 'grok-3-search' || model === 'grok-3-imageGen') {
140
- model = 'grok-3';
141
- }
142
- if (!this.modelRateLimit[model]) return;
143
- const tokenFrequency = this.tokenModelFrequency.get(this.activeTokens[index]);
144
- tokenFrequency[model] = 9999;
145
- }
146
- isTokenModelLimitReached(index, model) {
147
- if (model === 'grok-3-search' || model === 'grok-3-imageGen') {
148
- model = 'grok-3';
149
- }
150
- if (!this.modelRateLimit[model]) return;
151
- const token = this.activeTokens[index];
152
- const tokenFrequency = this.tokenModelFrequency.get(token);
153
-
154
- if (!tokenFrequency) {
155
- return false;
156
- }
157
- return tokenFrequency[model] >= this.modelRateLimit[model].RequestFrequency;
158
- }
159
- checkAndRemoveTokenIfLimitReached(token) {
160
- const tokenFrequency = this.tokenModelFrequency.get(token);
161
- if (!tokenFrequency) return;
162
-
163
- const isLimitReached = Object.keys(tokenFrequency).every(model =>
164
- tokenFrequency[model] >= this.modelRateLimit[model].RequestFrequency
165
- );
166
-
167
- if (isLimitReached) {
168
- const tokenIndex = this.activeTokens.indexOf(token);
169
- if (tokenIndex !== -1) {
170
- this.removeTokenByIndex(tokenIndex);
171
- }
172
- }
173
- }
174
-
175
- removeTokenByIndex(index) {
176
- if (!this.isRecoveryProcess) {
177
- this.startTokenRecoveryProcess();
178
- }
179
- const token = this.activeTokens[index];
180
- this.expiredTokens.set(token, Date.now());
181
- this.activeTokens.splice(index, 1);
182
- this.tokenModelFrequency.delete(token);
183
- Logger.info(`令牌${token}已达到上限,已移除`, 'TokenManager');
184
- }
185
-
186
- startTokenRecoveryProcess() {
187
- if (CONFIG.API.IS_CUSTOM_SSO) return;
188
- setInterval(() => {
189
- const now = Date.now();
190
- for (const [token, expiredTime] of this.expiredTokens.entries()) {
191
- if (now - expiredTime >= 2 * 60 * 60 * 1000) {
192
- this.tokenModelUsage.set(token, {
193
- "grok-3": 0,
194
- "grok-3-deepsearch": 0,
195
- "grok-3-reasoning": 0
196
- });
197
- this.activeTokens.push(token);
198
- this.expiredTokens.delete(token);
199
- Logger.info(`令牌${token}已恢复,已添加到可用令牌列表`, 'TokenManager');
200
- }
201
- }
202
- }, 2 * 60 * 60 * 1000);
203
- }
204
-
205
- getTokenCount() {
206
- return this.activeTokens.length || 0;
207
- }
208
-
209
- getActiveTokens() {
210
- return [...this.activeTokens];
211
- }
212
- }
213
-
214
- class Utils {
215
- static async extractGrokHeaders() {
216
- Logger.info("开始提取头信息", 'Server');
217
- try {
218
- // 启动浏览器
219
- const browser = await puppeteer.launch({
220
- headless: true,
221
- args: [
222
- '--no-sandbox',
223
- '--disable-setuid-sandbox',
224
- '--disable-dev-shm-usage',
225
- '--disable-gpu'
226
- ],
227
- executablePath: CONFIG.CHROME_PATH
228
- });
229
-
230
- const page = await browser.newPage();
231
- await page.goto('https://grok.com/', { waitUntil: 'domcontentloaded' });
232
- await page.evaluate(() => {
233
- return new Promise(resolve => setTimeout(resolve, 5000))
234
- })
235
- // 获取所有 Cookies
236
- const cookies = await page.cookies();
237
- const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
238
- const extractedHeaders = {};
239
- // 遍历 Cookies
240
- for (const cookie of cookies) {
241
- // 检查是否为目标头信息
242
- if (targetHeaders.includes(cookie.name.toLowerCase())) {
243
- extractedHeaders[cookie.name.toLowerCase()] = cookie.value;
244
- }
245
- }
246
- // 关闭浏览器
247
- await browser.close();
248
- // 打印并返回提取的头信息
249
- Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
250
- return extractedHeaders;
251
-
252
- } catch (error) {
253
- Logger.error('获取头信息出错:', error, 'Server');
254
- return null;
255
- }
256
- }
257
- static async get_signature() {
258
- if (CONFIG.API.TEMP_COOKIE) {
259
- return CONFIG.API.TEMP_COOKIE;
260
- }
261
- Logger.info("刷新认证信息", 'Server');
262
- let retryCount = 0;
263
- while (!CONFIG.API.TEMP_COOKIE || retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
264
- let headers = await Utils.extractGrokHeaders();
265
- if (headers) {
266
- Logger.info("获取认证信息成功", 'Server');
267
- CONFIG.API.TEMP_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
268
- return;
269
- }
270
- retryCount++;
271
- if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
272
- throw new Error(`获取认证信息失败!`);
273
- }
274
- }
275
- }
276
- static async organizeSearchResults(searchResults) {
277
- // 确保传入的是有效的搜索结果对象
278
- if (!searchResults || !searchResults.results) {
279
- return '';
280
- }
281
-
282
- const results = searchResults.results;
283
- const formattedResults = results.map((result, index) => {
284
- // 处理可能为空的字段
285
- const title = result.title || '未知标题';
286
- const url = result.url || '#';
287
- const preview = result.preview || '无预览内容';
288
-
289
- return `\r\n<details><summary>资料[${index}]: ${title}</summary>\r\n${preview}\r\n\n[Link](${url})\r\n</details>`;
290
- });
291
- return formattedResults.join('\n\n');
292
- }
293
- static async createAuthHeaders(model) {
294
- return {
295
- 'cookie': `${await tokenManager.getTokenByIndex(CONFIG.SSO_INDEX, model)}`
296
- };
297
- }
298
- }
299
-
300
-
301
- class GrokApiClient {
302
- constructor(modelId) {
303
- if (!CONFIG.MODELS[modelId]) {
304
- throw new Error(`不支持的模型: ${modelId}`);
305
- }
306
- this.modelId = CONFIG.MODELS[modelId];
307
- }
308
-
309
- processMessageContent(content) {
310
- if (typeof content === 'string') return content;
311
- return null;
312
- }
313
- // 获取图片类型
314
- getImageType(base64String) {
315
- let mimeType = 'image/jpeg';
316
- if (base64String.includes('data:image')) {
317
- const matches = base64String.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,/);
318
- if (matches) {
319
- mimeType = matches[1];
320
- }
321
- }
322
- const extension = mimeType.split('/')[1];
323
- const fileName = `image.${extension}`;
324
-
325
- return {
326
- mimeType: mimeType,
327
- fileName: fileName
328
- };
329
- }
330
-
331
- async uploadBase64Image(base64Data, url) {
332
- try {
333
- // 处理 base64 数据
334
- let imageBuffer;
335
- if (base64Data.includes('data:image')) {
336
- imageBuffer = base64Data.split(',')[1];
337
- } else {
338
- imageBuffer = base64Data
339
- }
340
- const { mimeType, fileName } = this.getImageType(base64Data);
341
- let uploadData = {
342
- rpc: "uploadFile",
343
- req: {
344
- fileName: fileName,
345
- fileMimeType: mimeType,
346
- content: imageBuffer
347
- }
348
- };
349
- Logger.info("发送图片请求", 'Server');
350
- // 发送请求
351
- const response = await fetch(url, {
352
- method: 'POST',
353
- headers: {
354
- ...CONFIG.DEFAULT_HEADERS,
355
- ...CONFIG.API.SIGNATURE_COOKIE
356
- },
357
- body: JSON.stringify(uploadData)
358
- });
359
-
360
- if (!response.ok) {
361
- Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
362
- return '';
363
- }
364
-
365
- const result = await response.json();
366
- Logger.info('上传图片成功:', result, 'Server');
367
- return result.fileMetadataId;
368
-
369
- } catch (error) {
370
- Logger.error(error, 'Server');
371
- return '';
372
- }
373
- }
374
-
375
- async prepareChatRequest(request) {
376
- if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.PICUI_KEY && request.stream) {
377
- throw new Error(`该模型流式输出需要配置PICGO或者PICUI图床密钥!`);
378
- }
379
-
380
- // 处理画图模型的消息限制
381
- let todoMessages = request.messages;
382
- if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
383
- const lastMessage = todoMessages[todoMessages.length - 1];
384
- if (lastMessage.role !== 'user') {
385
- throw new Error('画图模型的最后一条消息必须是用户消息!');
386
- }
387
- todoMessages = [lastMessage];
388
- }
389
-
390
- const fileAttachments = [];
391
- let messages = '';
392
- let lastRole = null;
393
- let lastContent = '';
394
- const search = request.model === 'grok-2-search' || request.model === 'grok-3-search';
395
-
396
- // 移除<think>标签及其内容和base64图片
397
- const removeThinkTags = (text) => {
398
- text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
399
- text = text.replace(/!\[image\]\(data:.*?base64,.*?\)/g, '[图片]');
400
- return text;
401
- };
402
-
403
- const processImageUrl = async (content) => {
404
- if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
405
- const imageResponse = await this.uploadBase64Image(
406
- content.image_url.url,
407
- `${CONFIG.API.BASE_URL}/api/rpc`
408
- );
409
- return imageResponse;
410
- }
411
- return null;
412
- };
413
-
414
- const processContent = async (content) => {
415
- if (Array.isArray(content)) {
416
- let textContent = '';
417
- for (const item of content) {
418
- if (item.type === 'image_url') {
419
- textContent += (textContent ? '\n' : '') + "[图片]";
420
- } else if (item.type === 'text') {
421
- textContent += (textContent ? '\n' : '') + removeThinkTags(item.text);
422
- }
423
- }
424
- return textContent;
425
- } else if (typeof content === 'object' && content !== null) {
426
- if (content.type === 'image_url') {
427
- return "[图片]";
428
- } else if (content.type === 'text') {
429
- return removeThinkTags(content.text);
430
- }
431
- }
432
- return removeThinkTags(this.processMessageContent(content));
433
- };
434
-
435
- for (const current of todoMessages) {
436
- const role = current.role === 'assistant' ? 'assistant' : 'user';
437
- const isLastMessage = current === todoMessages[todoMessages.length - 1];
438
-
439
- // 处理图片附件
440
- if (isLastMessage && current.content) {
441
- if (Array.isArray(current.content)) {
442
- for (const item of current.content) {
443
- if (item.type === 'image_url') {
444
- const processedImage = await processImageUrl(item);
445
- if (processedImage) fileAttachments.push(processedImage);
446
- }
447
- }
448
- } else if (current.content.type === 'image_url') {
449
- const processedImage = await processImageUrl(current.content);
450
- if (processedImage) fileAttachments.push(processedImage);
451
- }
452
- }
453
-
454
- // 处理文本内容
455
- const textContent = await processContent(current.content);
456
-
457
- if (textContent || (isLastMessage && fileAttachments.length > 0)) {
458
- if (role === lastRole && textContent) {
459
- lastContent += '\n' + textContent;
460
- messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
461
- `${role.toUpperCase()}: ${lastContent}\n`;
462
- } else {
463
- messages += `${role.toUpperCase()}: ${textContent || '[图片]'}\n`;
464
- lastContent = textContent;
465
- lastRole = role;
466
- }
467
- }
468
- }
469
-
470
- return {
471
- modelName: this.modelId,
472
- message: messages.trim(),
473
- fileAttachments: fileAttachments.slice(0, 4),
474
- imageAttachments: [],
475
- disableSearch: false,
476
- enableImageGeneration: true,
477
- returnImageBytes: false,
478
- returnRawGrokInXaiRequest: false,
479
- enableImageStreaming: false,
480
- imageGenerationCount: 1,
481
- forceConcise: false,
482
- toolOverrides: {
483
- imageGen: request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen',
484
- webSearch: search,
485
- xSearch: search,
486
- xMediaSearch: search,
487
- trendsSearch: search,
488
- xPostAnalyze: search
489
- },
490
- enableSideBySide: true,
491
- isPreset: false,
492
- sendFinalMetadata: true,
493
- customInstructions: "",
494
- deepsearchPreset: request.model === 'grok-3-deepsearch' ? "default" : "",
495
- isReasoning: request.model === 'grok-3-reasoning'
496
- };
497
- }
498
- }
499
-
500
- class MessageProcessor {
501
- static createChatResponse(message, model, isStream = false) {
502
- const baseResponse = {
503
- id: `chatcmpl-${uuidv4()}`,
504
- created: Math.floor(Date.now() / 1000),
505
- model: model
506
- };
507
-
508
- if (isStream) {
509
- return {
510
- ...baseResponse,
511
- object: 'chat.completion.chunk',
512
- choices: [{
513
- index: 0,
514
- delta: {
515
- content: message
516
- }
517
- }]
518
- };
519
- }
520
-
521
- return {
522
- ...baseResponse,
523
- object: 'chat.completion',
524
- choices: [{
525
- index: 0,
526
- message: {
527
- role: 'assistant',
528
- content: message
529
- },
530
- finish_reason: 'stop'
531
- }],
532
- usage: null
533
- };
534
- }
535
- }
536
- async function processModelResponse(linejosn, model) {
537
- let result = { token: null, imageUrl: null }
538
- if (CONFIG.IS_IMG_GEN) {
539
- if (linejosn?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
540
- result.imageUrl = linejosn.cachedImageGenerationResponse.imageUrl;
541
- }
542
- return result;
543
- }
544
-
545
- //非生图模型的处理
546
- switch (model) {
547
- case 'grok-2':
548
- result.token = linejosn?.token;
549
- return result;
550
- case 'grok-2-search':
551
- case 'grok-3-search':
552
- if (linejosn?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
553
- result.token = `\r\n<think>${await Utils.organizeSearchResults(linejosn.webSearchResults)}</think>\r\n`;
554
- } else {
555
- result.token = linejosn?.token;
556
- }
557
- return result;
558
- case 'grok-3':
559
- result.token = linejosn?.token;
560
- return result;
561
- case 'grok-3-deepsearch':
562
- if (linejosn.messageTag === "final") {
563
- result.token = linejosn?.token;
564
- }
565
- return result;
566
- case 'grok-3-reasoning':
567
- if (linejosn?.isThinking && !CONFIG.SHOW_THINKING) return result;
568
-
569
- if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
570
- result.token = "<think>" + linejosn?.token;
571
- CONFIG.IS_THINKING = true;
572
- } else if (!linejosn.isThinking && CONFIG.IS_THINKING) {
573
- result.token = "</think>" + linejosn?.token;
574
- CONFIG.IS_THINKING = false;
575
- } else {
576
- result.token = linejosn?.token;
577
- }
578
- return result;
579
- }
580
- return result;
581
- }
582
-
583
- async function handleResponse(response, model, res, isStream) {
584
- try {
585
- const stream = response.body;
586
- let buffer = '';
587
- let fullResponse = '';
588
- const dataPromises = [];
589
- if (isStream) {
590
- res.setHeader('Content-Type', 'text/event-stream');
591
- res.setHeader('Cache-Control', 'no-cache');
592
- res.setHeader('Connection', 'keep-alive');
593
- }
594
-
595
- return new Promise((resolve, reject) => {
596
- stream.on('data', async (chunk) => {
597
- buffer += chunk.toString();
598
- const lines = buffer.split('\n');
599
- buffer = lines.pop() || '';
600
-
601
- for (const line of lines) {
602
- if (!line.trim()) continue;
603
- const trimmedLine = line.trim();
604
- if (trimmedLine.startsWith('data: ')) {
605
- const data = trimmedLine.substring(6);
606
- try {
607
- if (!data.trim()) continue;
608
- if (data === "[DONE]") continue;
609
- const linejosn = JSON.parse(data);
610
- if (linejosn?.error) {
611
- Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
612
- stream.destroy();
613
- reject(new Error("RateLimitError"));
614
- return;
615
- }
616
- if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
617
- CONFIG.IS_IMG_GEN = true;
618
- }
619
- const processPromise = (async () => {
620
- const result = await processModelResponse(linejosn, model);
621
-
622
- if (result.token) {
623
- if (isStream) {
624
- res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
625
- } else {
626
- fullResponse += result.token;
627
- }
628
- }
629
- if (result.imageUrl) {
630
- CONFIG.IS_IMG_GEN2 = true;
631
- const dataImage = await handleImageResponse(result.imageUrl);
632
- if (isStream) {
633
- res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(dataImage, model, true))}\n\n`);
634
- } else {
635
- res.json(MessageProcessor.createChatResponse(dataImage, model));
636
- }
637
- }
638
- })();
639
- dataPromises.push(processPromise);
640
- } catch (error) {
641
- continue;
642
- }
643
- }
644
- }
645
- });
646
-
647
- stream.on('end', async () => {
648
- try {
649
- await Promise.all(dataPromises);
650
- if (isStream) {
651
- res.write('data: [DONE]\n\n');
652
- res.end();
653
- } else {
654
- if (!CONFIG.IS_IMG_GEN2) {
655
- res.json(MessageProcessor.createChatResponse(fullResponse, model));
656
- }
657
- }
658
- CONFIG.IS_IMG_GEN = false;
659
- CONFIG.IS_IMG_GEN2 = false;
660
- resolve();
661
- } catch (error) {
662
- reject(error);
663
- }
664
- });
665
-
666
- stream.on('error', (error) => {
667
- reject(error);
668
- });
669
- });
670
- } catch (error) {
671
- Logger.error(error, 'Server');
672
- CONFIG.IS_IMG_GEN = false;
673
- CONFIG.IS_IMG_GEN2 = false;
674
- throw error;
675
- }
676
- }
677
-
678
- async function handleImageResponse(imageUrl) {
679
- const MAX_RETRIES = 2;
680
- let retryCount = 0;
681
- let imageBase64Response;
682
-
683
- while (retryCount < MAX_RETRIES) {
684
- try {
685
- imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
686
- method: 'GET',
687
- headers: {
688
- ...DEFAULT_HEADERS,
689
- ...CONFIG.API.SIGNATURE_COOKIE
690
- }
691
- });
692
-
693
- if (imageBase64Response.ok) break;
694
- retryCount++;
695
- if (retryCount === MAX_RETRIES) {
696
- throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
697
- }
698
- await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
699
-
700
- } catch (error) {
701
- retryCount++;
702
- if (retryCount === MAX_RETRIES) {
703
- throw error;
704
- }
705
- await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
706
- }
707
- }
708
-
709
-
710
- const arrayBuffer = await imageBase64Response.arrayBuffer();
711
- const imageBuffer = Buffer.from(arrayBuffer);
712
-
713
- if (!CONFIG.API.PICGO_KEY && !CONFIG.API.PICUI_KEY) {
714
- const base64Image = imageBuffer.toString('base64');
715
- const imageContentType = imageBase64Response.headers.get('content-type');
716
- return `![image](data:${imageContentType};base64,${base64Image})`
717
- }
718
-
719
- const formData = new FormData();
720
- if(CONFIG.API.PICGO_KEY){
721
- formData.append('source', imageBuffer, {
722
- filename: `image-${Date.now()}.jpg`,
723
- contentType: 'image/jpeg'
724
- });
725
- const formDataHeaders = formData.getHeaders();
726
- const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
727
- method: "POST",
728
- headers: {
729
- ...formDataHeaders,
730
- "Content-Type": "multipart/form-data",
731
- "X-API-Key": CONFIG.API.PICGO_KEY
732
- },
733
- body: formData
734
- });
735
- if (!responseURL.ok) {
736
- return "生图失败,请查看图床密钥是否设置正确"
737
- } else {
738
- Logger.info("生图成功", 'Server');
739
- const result = await responseURL.json();
740
- return `![image](${result.image.url})`
741
- }
742
- }else if(CONFIG.API.PICUI_KEY){
743
- const formData = new FormData();
744
- formData.append('file', imageBuffer, {
745
- filename: `image-${Date.now()}.jpg`,
746
- contentType: 'image/jpeg'
747
- });
748
- const formDataHeaders = formData.getHeaders();
749
- const responseURL = await fetch("https://picui.cn/api/v1/upload", {
750
- method: "POST",
751
- headers: {
752
- ...formDataHeaders,
753
- "Accept": "application/json",
754
- 'Authorization': `Bearer ${CONFIG.API.PICUI_KEY}`
755
- },
756
- body: formData
757
- });
758
- if (!responseURL.ok) {
759
- return "生图失败,请查看图床密钥是否设置正确"
760
- } else {
761
- Logger.info("生图成功", 'Server');
762
- const result = await responseURL.json();
763
- return `![image](${result.data.links.url})`
764
- }
765
- }
766
- }
767
-
768
- const tokenManager = new AuthTokenManager();
769
- await initialization();
770
-
771
- // 中间件配置
772
- const app = express();
773
- app.use(Logger.requestLogger);
774
- app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
775
- app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
776
- app.use(cors({
777
- origin: '*',
778
- methods: ['GET', 'POST', 'OPTIONS'],
779
- allowedHeaders: ['Content-Type', 'Authorization']
780
- }));
781
-
782
- app.get('/hf/v1/models', (req, res) => {
783
- res.json({
784
- object: "list",
785
- data: Object.keys(CONFIG.MODELS).map((model, index) => ({
786
- id: model,
787
- object: "model",
788
- created: Math.floor(Date.now() / 1000),
789
- owned_by: "grok",
790
- }))
791
- });
792
- });
793
-
794
-
795
- app.post('/hf/v1/chat/completions', async (req, res) => {
796
- try {
797
- const authToken = req.headers.authorization?.replace('Bearer ', '');
798
- if (CONFIG.API.IS_CUSTOM_SSO) {
799
- if (authToken && authToken.includes(';')) {
800
- const parts = authToken.split(';');
801
- const result = [
802
- `sso=${parts[0]}`,
803
- `ssp_rw=${parts[1]}`
804
- ].join(';');
805
- tokenManager.setToken(result);
806
- } else {
807
- return res.status(401).json({ error: '自定义的SSO令牌格式错误' });
808
- }
809
- } else if (authToken !== CONFIG.API.API_KEY) {
810
- return res.status(401).json({ error: 'Unauthorized' });
811
- }
812
- let isTempCookie = req.body.model.includes("grok-2");
813
- let retryCount = 0;
814
- const grokClient = new GrokApiClient(req.body.model);
815
- const requestPayload = await grokClient.prepareChatRequest(req.body);
816
- Logger.info(`当前请求体: ${JSON.stringify(requestPayload,null,2)}`, 'Server');
817
- while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
818
- retryCount++;
819
- if (!CONFIG.API.TEMP_COOKIE) {
820
- await Utils.get_signature();
821
- }
822
-
823
- if (isTempCookie) {
824
- CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
825
- Logger.info(`已切换为临时令牌`, 'Server');
826
- } else {
827
- CONFIG.API.SIGNATURE_COOKIE = await Utils.createAuthHeaders(req.body.model);
828
- }
829
- if (!CONFIG.API.SIGNATURE_COOKIE) {
830
- throw new Error('无可用令牌');
831
- }
832
- Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
833
- Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE,null,2)}`, 'Server');
834
- const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
835
- method: 'POST',
836
- headers: {
837
- ...DEFAULT_HEADERS,
838
- ...CONFIG.API.SIGNATURE_COOKIE
839
- },
840
- body: JSON.stringify({
841
- rpc: "createConversation",
842
- req: {
843
- temporary: false
844
- }
845
- })
846
- });
847
-
848
- const responseText = await newMessageReq.json();
849
- const conversationId = responseText.conversationId;
850
-
851
- const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
852
- method: 'POST',
853
- headers: {
854
- "accept": "text/event-stream",
855
- "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
856
- "content-type": "text/plain;charset=UTF-8",
857
- "Connection": "keep-alive",
858
- ...CONFIG.API.SIGNATURE_COOKIE
859
- },
860
- body: JSON.stringify(requestPayload)
861
- });
862
-
863
- if (response.ok) {
864
- Logger.info(`请求成功`, 'Server');
865
- CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
866
- Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
867
- try {
868
- await handleResponse(response, req.body.model, res, req.body.stream);
869
- return;
870
- } catch (error) {
871
- if (isTempCookie) {
872
- await Utils.get_signature();
873
- } else {
874
- tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
875
- for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
876
- CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
877
- if (!tokenManager.isTokenModelLimitReached(CONFIG.SSO_INDEX, req.body.model)) {
878
- break;
879
- } else if (i >= tokenManager.getTokenCount()) {
880
- throw new Error(`${req.body.model} 次数已达上限,请切换其他模型或者重新对话`);
881
- }
882
- }
883
- }
884
- }
885
- } else {
886
- if (response.status === 429) {
887
- if (isTempCookie) {
888
- await Utils.get_signature();
889
- } else {
890
- tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
891
- for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
892
- CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
893
- if (!tokenManager.isTokenModelLimitReached(CONFIG.SSO_INDEX, req.body.model)) {
894
- break;
895
- } else if (i >= tokenManager.getTokenCount()) {
896
- throw new Error(`${req.body.model} 次数已达上限,请切换其他模型或者重新对话`);
897
- }
898
- }
899
- }
900
- } else {
901
- // 非429错误直接抛出
902
- if (isTempCookie) {
903
- await Utils.get_signature();
904
- } else {
905
- Logger.error(`令牌异常错误状态!status: ${response.status}, 已移除当前令牌${CONFIG.SSO_INDEX.cookie}`, 'Server');
906
- tokenManager.removeTokenByIndex(CONFIG.SSO_INDEX);
907
- Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
908
- CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
909
- }
910
- }
911
- }
912
- }
913
- throw new Error('当前模型所有令牌都已耗尽');
914
- } catch (error) {
915
- Logger.error(error, 'ChatAPI');
916
- res.status(500).json({
917
- error: {
918
- message: error.message,
919
- type: 'server_error',
920
- param: null,
921
- code: error.code || null
922
- }
923
- });
924
- }
925
- });
926
-
927
-
928
- app.use((req, res) => {
929
- res.status(200).send('api运行正常');
930
- });
931
-
932
-
933
- app.listen(CONFIG.SERVER.PORT, () => {
934
- Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
935
- });