deepak191z commited on
Commit
ce6c0f3
·
verified ·
1 Parent(s): 04baf63

Update index.js

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