yxmiler/GrokAPIService

#1
Files changed (5) hide show
  1. Dockerfile +0 -31
  2. README.md +0 -12
  3. index.js +0 -1315
  4. logger.js +0 -66
  5. package.json +0 -23
Dockerfile DELETED
@@ -1,31 +0,0 @@
1
- FROM node:18-slim
2
-
3
- # 安装 Chrome 依赖
4
- RUN apt-get update && apt-get install -y \
5
- wget \
6
- gnupg \
7
- ca-certificates \
8
- procps \
9
- chromium \
10
- chromium-sandbox
11
-
12
- # 设置工作目录
13
- WORKDIR /app
14
-
15
- # 复制 package.json 和 package-lock.json
16
- COPY package*.json ./
17
-
18
- # 安装依赖
19
- RUN npm install
20
-
21
- # 复制源代码
22
- COPY . .
23
-
24
- # 设置环境变量
25
- ENV PORT=7860
26
-
27
- # 暴露端口
28
- EXPOSE 7860
29
-
30
- # 启动应用
31
- CMD ["npm", "start"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md DELETED
@@ -1,12 +0,0 @@
1
- ---
2
- title: Grok API Service
3
- emoji: 🤖
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- app_port: 7860
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
12
-
 
 
 
 
 
 
 
 
 
 
 
 
 
index.js DELETED
@@ -1,1315 +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_TEMP_CONVERSATION: process.env.IS_TEMP_CONVERSATION == undefined ? false : process.env.IS_TEMP_CONVERSATION == 'true',
27
- IS_TEMP_GROK2: process.env.IS_TEMP_GROK2 == undefined ? true : process.env.IS_TEMP_GROK2 == 'true',
28
- GROK2_CONCURRENCY_LEVEL: process.env.GROK2_CONCURRENCY_LEVEL || 4,
29
- IS_CUSTOM_SSO: process.env.IS_CUSTOM_SSO == undefined ? false : process.env.IS_CUSTOM_SSO == 'true',
30
- BASE_URL: "https://grok.com",
31
- API_KEY: process.env.API_KEY || "sk-123456",
32
- SIGNATURE_COOKIE: null,
33
- TEMP_COOKIE: null,
34
- PICGO_KEY: process.env.PICGO_KEY || null, //想要流式生图的话需要填入这个PICGO图床的key
35
- TUMY_KEY: process.env.TUMY_KEY || null //想要流式生图的话需要填入这个TUMY图床的key 两个图床二选一,默认使用PICGO
36
- },
37
- SERVER: {
38
- PORT: process.env.PORT || 3000,
39
- BODY_LIMIT: '5mb'
40
- },
41
- RETRY: {
42
- MAX_ATTEMPTS: 2//重试次数
43
- },
44
- SHOW_THINKING: process.env.SHOW_THINKING == undefined ? true : process.env.SHOW_THINKING == 'true',
45
- IS_THINKING: false,
46
- IS_IMG_GEN: false,
47
- IS_IMG_GEN2: false,
48
- TEMP_COOKIE_INDEX: 0,//临时cookie的下标
49
- ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS == undefined ? true : process.env.ISSHOW_SEARCH_RESULTS == 'true',//是否显示搜索结果
50
- CHROME_PATH: process.env.CHROME_PATH || null
51
- };
52
- puppeteer.use(StealthPlugin())
53
-
54
- // 请求头配置
55
- const DEFAULT_HEADERS = {
56
- 'Accept': '*/*',
57
- 'Accept-Language': 'zh-CN,zh;q=0.9',
58
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
59
- 'Content-Type': 'text/plain;charset=UTF-8',
60
- 'Connection': 'keep-alive',
61
- 'Origin': 'https://grok.com',
62
- 'Priority': 'u=1, i',
63
- 'Sec-Ch-Ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
64
- 'Sec-Ch-Ua-Mobile': '?0',
65
- 'Sec-Ch-Ua-Platform': '"Windows"',
66
- 'Sec-Fetch-Dest': 'empty',
67
- 'Sec-Fetch-Mode': 'cors',
68
- 'Sec-Fetch-Site': 'same-origin',
69
- 'Baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
70
- };
71
-
72
-
73
- async function initialization() {
74
- if (CONFIG.CHROME_PATH == null) {
75
- try {
76
- CONFIG.CHROME_PATH = puppeteer.executablePath();
77
- } catch (error) {
78
- CONFIG.CHROME_PATH = "/usr/bin/chromium";
79
- }
80
- }
81
- Logger.info(`CHROME_PATH: ${CONFIG.CHROME_PATH}`, 'Server');
82
- if (CONFIG.API.IS_CUSTOM_SSO) {
83
- if (CONFIG.API.IS_TEMP_GROK2) {
84
- await tempCookieManager.ensureCookies();
85
- }
86
- return;
87
- }
88
- const ssoArray = process.env.SSO.split(',');
89
- const concurrencyLimit = 4;
90
- for (let i = 0; i < ssoArray.length; i += concurrencyLimit) {
91
- const batch = ssoArray.slice(i, i + concurrencyLimit);
92
- const batchPromises = batch.map(sso =>
93
- tokenManager.addToken(`sso-rw=${sso};sso=${sso}`)
94
- );
95
-
96
- await Promise.all(batchPromises);
97
- Logger.info(`已加载令牌: ${i+1} 个`, 'Server');
98
- await new Promise(resolve => setTimeout(resolve, 1000));
99
- }
100
- Logger.info(`令牌加载完成: ${JSON.stringify(tokenManager.getAllTokens(), null, 2)}`, 'Server');
101
- Logger.info(`共加载: ${tokenManager.getAllTokens().length}个令牌`, 'Server');
102
- if (CONFIG.API.IS_TEMP_GROK2) {
103
- await tempCookieManager.ensureCookies();
104
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
105
- }
106
- Logger.info("初始化完成", 'Server');
107
- }
108
-
109
- class AuthTokenManager {
110
- constructor() {
111
- this.tokenModelMap = {};
112
- this.expiredTokens = new Set();
113
- this.tokenStatusMap = {};
114
-
115
- // 定义模型请求频率限制和过期时间
116
- this.modelConfig = {
117
- "grok-2": {
118
- RequestFrequency: 30,
119
- ExpirationTime: 1 * 60 * 60 * 1000 // 1小时
120
- },
121
- "grok-3": {
122
- RequestFrequency: 20,
123
- ExpirationTime: 2 * 60 * 60 * 1000 // 2小时
124
- },
125
- "grok-3-deepsearch": {
126
- RequestFrequency: 10,
127
- ExpirationTime: 24 * 60 * 60 * 1000 // 24小时
128
- },
129
- "grok-3-reasoning": {
130
- RequestFrequency: 10,
131
- ExpirationTime: 24 * 60 * 60 * 1000 // 24小���
132
- }
133
- };
134
- this.tokenResetSwitch = false;
135
- this.tokenResetTimer = null;
136
- }
137
- async fetchGrokStats(token, modelName) {
138
- let requestKind = 'DEFAULT';
139
- if (modelName == 'grok-2' || modelName == 'grok-3') {
140
- requestKind = 'DEFAULT';
141
- } else if (modelName == 'grok-3-deepsearch') {
142
- requestKind = 'DEEPSEARCH';
143
- } else if (modelName == 'grok-3-reasoning') {
144
- requestKind = 'REASONING';
145
- }
146
- const response = await fetch('https://grok.com/rest/rate-limits', {
147
- method: 'POST',
148
- headers: {
149
- 'content-type': 'application/json',
150
- 'Cookie': token,
151
- },
152
- body: JSON.stringify({
153
- "requestKind": requestKind,
154
- "modelName": modelName == 'grok-2' ? 'grok-latest' : "grok-3"
155
- })
156
- });
157
-
158
- if (response.status != 200) {
159
- return 0;
160
- }
161
- const data = await response.json();
162
- return data.remainingQueries;
163
- }
164
- async addToken(token) {
165
- const sso = token.split("sso=")[1].split(";")[0];
166
-
167
- for (const model of Object.keys(this.modelConfig)) {
168
- if (!this.tokenModelMap[model]) {
169
- this.tokenModelMap[model] = [];
170
- }
171
- if (!this.tokenStatusMap[sso]) {
172
- this.tokenStatusMap[sso] = {};
173
- }
174
- const existingTokenEntry = this.tokenModelMap[model].find(entry => entry.token === token);
175
-
176
- if (!existingTokenEntry) {
177
- try {
178
- const remainingQueries = await this.fetchGrokStats(token, model);
179
-
180
- const modelRequestFrequency = this.modelConfig[model].RequestFrequency;
181
- const usedRequestCount = modelRequestFrequency - remainingQueries;
182
-
183
- if (usedRequestCount === modelRequestFrequency) {
184
- this.expiredTokens.add({
185
- token: token,
186
- model: model,
187
- expiredTime: Date.now()
188
- });
189
-
190
- if (!this.tokenStatusMap[sso][model]) {
191
- this.tokenStatusMap[sso][model] = {
192
- isValid: false,
193
- invalidatedTime: Date.now(),
194
- totalRequestCount: Math.max(0, usedRequestCount)
195
- };
196
- }
197
-
198
- if (!this.tokenResetSwitch) {
199
- this.startTokenResetProcess();
200
- this.tokenResetSwitch = true;
201
- }
202
- } else {
203
- this.tokenModelMap[model].push({
204
- token: token,
205
- RequestCount: Math.max(0, usedRequestCount),
206
- AddedTime: Date.now(),
207
- StartCallTime: null
208
- });
209
-
210
- if (!this.tokenStatusMap[sso][model]) {
211
- this.tokenStatusMap[sso][model] = {
212
- isValid: true,
213
- invalidatedTime: null,
214
- totalRequestCount: Math.max(0, usedRequestCount)
215
- };
216
- }
217
- }
218
- } catch (error) {
219
- this.tokenModelMap[model].push({
220
- token: token,
221
- RequestCount: 0,
222
- AddedTime: Date.now(),
223
- StartCallTime: null
224
- });
225
-
226
- if (!this.tokenStatusMap[sso][model]) {
227
- this.tokenStatusMap[sso][model] = {
228
- isValid: true,
229
- invalidatedTime: null,
230
- totalRequestCount: 0
231
- };
232
- }
233
-
234
- Logger.error(`获取模型 ${model} 的统计信息失败: ${error}`, 'TokenManager');
235
- }
236
- await Utils.delay(200);
237
- }
238
- }
239
- }
240
-
241
- setToken(token) {
242
- const models = Object.keys(this.modelConfig);
243
- this.tokenModelMap = models.reduce((map, model) => {
244
- map[model] = [{
245
- token,
246
- RequestCount: 0,
247
- AddedTime: Date.now(),
248
- StartCallTime: null
249
- }];
250
- return map;
251
- }, {});
252
- const sso = token.split("sso=")[1].split(";")[0];
253
- this.tokenStatusMap[sso] = models.reduce((statusMap, model) => {
254
- statusMap[model] = {
255
- isValid: true,
256
- invalidatedTime: null,
257
- totalRequestCount: 0
258
- };
259
- return statusMap;
260
- }, {});
261
- }
262
-
263
- async deleteToken(token) {
264
- try {
265
- const sso = token.split("sso=")[1].split(";")[0];
266
- await Promise.all([
267
- new Promise((resolve) => {
268
- this.tokenModelMap = Object.fromEntries(
269
- Object.entries(this.tokenModelMap).map(([model, entries]) => [
270
- model,
271
- entries.filter(entry => entry.token !== token)
272
- ])
273
- );
274
- resolve();
275
- }),
276
-
277
- new Promise((resolve) => {
278
- delete this.tokenStatusMap[sso];
279
- resolve();
280
- }),
281
- ]);
282
- Logger.info(`令牌已成功移除: ${token}`, 'TokenManager');
283
- return true;
284
- } catch (error) {
285
- Logger.error('令牌删除失败:', error);
286
- return false;
287
- }
288
- }
289
- getNextTokenForModel(modelId) {
290
- const normalizedModel = this.normalizeModelName(modelId);
291
-
292
- if (!this.tokenModelMap[normalizedModel] || this.tokenModelMap[normalizedModel].length === 0) {
293
- return null;
294
- }
295
- const tokenEntry = this.tokenModelMap[normalizedModel][0];
296
-
297
- if (tokenEntry) {
298
- if (tokenEntry.StartCallTime === null || tokenEntry.StartCallTime === undefined) {
299
- tokenEntry.StartCallTime = Date.now();
300
- }
301
- if (!this.tokenResetSwitch) {
302
- this.startTokenResetProcess();
303
- this.tokenResetSwitch = true;
304
- }
305
- tokenEntry.RequestCount++;
306
-
307
- if (tokenEntry.RequestCount > this.modelConfig[normalizedModel].RequestFrequency) {
308
- this.removeTokenFromModel(normalizedModel, tokenEntry.token);
309
- const nextTokenEntry = this.tokenModelMap[normalizedModel][0];
310
- return nextTokenEntry ? nextTokenEntry.token : null;
311
- }
312
- const sso = tokenEntry.token.split("sso=")[1].split(";")[0];
313
- if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][normalizedModel]) {
314
- if (tokenEntry.RequestCount === this.modelConfig[normalizedModel].RequestFrequency) {
315
- this.tokenStatusMap[sso][normalizedModel].isValid = false;
316
- this.tokenStatusMap[sso][normalizedModel].invalidatedTime = Date.now();
317
- }
318
- this.tokenStatusMap[sso][normalizedModel].totalRequestCount++;
319
- }
320
- return tokenEntry.token;
321
- }
322
-
323
- return null;
324
- }
325
-
326
- removeTokenFromModel(modelId, token) {
327
- const normalizedModel = this.normalizeModelName(modelId);
328
-
329
- if (!this.tokenModelMap[normalizedModel]) {
330
- Logger.error(`模型 ${normalizedModel} 不存在`, 'TokenManager');
331
- return false;
332
- }
333
-
334
- const modelTokens = this.tokenModelMap[normalizedModel];
335
- const tokenIndex = modelTokens.findIndex(entry => entry.token === token);
336
-
337
- if (tokenIndex !== -1) {
338
- const removedTokenEntry = modelTokens.splice(tokenIndex, 1)[0];
339
- this.expiredTokens.add({
340
- token: removedTokenEntry.token,
341
- model: normalizedModel,
342
- expiredTime: Date.now()
343
- });
344
-
345
- if (!this.tokenResetSwitch) {
346
- this.startTokenResetProcess();
347
- this.tokenResetSwitch = true;
348
- }
349
- Logger.info(`模型${modelId}的令牌已失效,已成功移除令牌: ${token}`, 'TokenManager');
350
- return true;
351
- }
352
-
353
- Logger.error(`在模型 ${normalizedModel} 中未找到 token: ${token}`, 'TokenManager');
354
- return false;
355
- }
356
-
357
- getExpiredTokens() {
358
- return Array.from(this.expiredTokens);
359
- }
360
-
361
- normalizeModelName(model) {
362
- if (model.startsWith('grok-') && !model.includes('deepsearch') && !model.includes('reasoning')) {
363
- return model.split('-').slice(0, 2).join('-');
364
- }
365
- return model;
366
- }
367
-
368
- getTokenCountForModel(modelId) {
369
- const normalizedModel = this.normalizeModelName(modelId);
370
- return this.tokenModelMap[normalizedModel]?.length || 0;
371
- }
372
-
373
- getRemainingTokenRequestCapacity() {
374
- const remainingCapacityMap = {};
375
-
376
- Object.keys(this.modelConfig).forEach(model => {
377
- const modelTokens = this.tokenModelMap[model] || [];
378
-
379
- const modelRequestFrequency = this.modelConfig[model].RequestFrequency;
380
-
381
- const totalUsedRequests = modelTokens.reduce((sum, tokenEntry) => {
382
- return sum + (tokenEntry.RequestCount || 0);
383
- }, 0);
384
-
385
- // 计算剩余可用请求数量
386
- const remainingCapacity = (modelTokens.length * modelRequestFrequency) - totalUsedRequests;
387
- remainingCapacityMap[model] = Math.max(0, remainingCapacity);
388
- });
389
-
390
- return remainingCapacityMap;
391
- }
392
-
393
- getTokenArrayForModel(modelId) {
394
- const normalizedModel = this.normalizeModelName(modelId);
395
- return this.tokenModelMap[normalizedModel] || [];
396
- }
397
-
398
- startTokenResetProcess() {
399
- if (this.tokenResetTimer) {
400
- clearInterval(this.tokenResetTimer);
401
- }
402
-
403
- this.tokenResetTimer = setInterval(() => {
404
- const now = Date.now();
405
-
406
- this.expiredTokens.forEach(expiredTokenInfo => {
407
- const { token, model, expiredTime } = expiredTokenInfo;
408
- const expirationTime = this.modelConfig[model].ExpirationTime;
409
- if (now - expiredTime >= expirationTime) {
410
- if (!this.tokenModelMap[model].some(entry => entry.token === token)) {
411
- this.tokenModelMap[model].push({
412
- token: token,
413
- RequestCount: 0,
414
- AddedTime: now,
415
- StartCallTime: null
416
- });
417
- }
418
- const sso = token.split("sso=")[1].split(";")[0];
419
-
420
- if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][model]) {
421
- this.tokenStatusMap[sso][model].isValid = true;
422
- this.tokenStatusMap[sso][model].invalidatedTime = null;
423
- this.tokenStatusMap[sso][model].totalRequestCount = 0;
424
- }
425
-
426
- this.expiredTokens.delete(expiredTokenInfo);
427
- }
428
- });
429
-
430
- Object.keys(this.modelConfig).forEach(model => {
431
- if (!this.tokenModelMap[model]) return;
432
-
433
- const processedTokens = this.tokenModelMap[model].map(tokenEntry => {
434
- if (!tokenEntry.StartCallTime) return tokenEntry;
435
-
436
- const expirationTime = this.modelConfig[model].ExpirationTime;
437
- if (now - tokenEntry.StartCallTime >= expirationTime) {
438
- const sso = tokenEntry.token.split("sso=")[1].split(";")[0];
439
- if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][model]) {
440
- this.tokenStatusMap[sso][model].isValid = true;
441
- this.tokenStatusMap[sso][model].invalidatedTime = null;
442
- this.tokenStatusMap[sso][model].totalRequestCount = 0;
443
- }
444
-
445
- return {
446
- ...tokenEntry,
447
- RequestCount: 0,
448
- StartCallTime: null
449
- };
450
- }
451
-
452
- return tokenEntry;
453
- });
454
-
455
- this.tokenModelMap[model] = processedTokens;
456
- });
457
- }, 1 * 60 * 60 * 1000);
458
- }
459
-
460
- getAllTokens() {
461
- const allTokens = new Set();
462
- Object.values(this.tokenModelMap).forEach(modelTokens => {
463
- modelTokens.forEach(entry => allTokens.add(entry.token));
464
- });
465
- return Array.from(allTokens);
466
- }
467
-
468
- getTokenStatusMap() {
469
- return this.tokenStatusMap;
470
- }
471
- }
472
-
473
-
474
- class Utils {
475
- static delay(time) {
476
- return new Promise(function (resolve) {
477
- setTimeout(resolve, time)
478
- });
479
- }
480
- static async organizeSearchResults(searchResults) {
481
- // 确保传入的是有效的搜索结果对象
482
- if (!searchResults || !searchResults.results) {
483
- return '';
484
- }
485
-
486
- const results = searchResults.results;
487
- const formattedResults = results.map((result, index) => {
488
- // 处理可能为空的字段
489
- const title = result.title || '未知标题';
490
- const url = result.url || '#';
491
- const preview = result.preview || '无预览内容';
492
-
493
- return `\r\n<details><summary>资料[${index}]: ${title}</summary>\r\n${preview}\r\n\n[Link](${url})\r\n</details>`;
494
- });
495
- return formattedResults.join('\n\n');
496
- }
497
- static async createAuthHeaders(model) {
498
- return await tokenManager.getNextTokenForModel(model);
499
- }
500
- }
501
- class GrokTempCookieManager {
502
- constructor() {
503
- this.cookies = [];
504
- this.currentIndex = 0;
505
- this.isRefreshing = false;
506
- this.initialCookieCount = CONFIG.API.GROK2_CONCURRENCY_LEVEL;
507
- this.extractCount = 0;
508
- }
509
-
510
- async ensureCookies() {
511
- // 如果 cookies 数量不足,则重新获取
512
- if (this.cookies.length < this.initialCookieCount) {
513
- await this.refreshCookies();
514
- }
515
- }
516
- async extractGrokHeaders(browser) {
517
- Logger.info("开始提取头信息", 'Server');
518
- try {
519
- const page = await browser.newPage();
520
- await page.goto('https://grok.com/', { waitUntil: 'domcontentloaded' });
521
- let waitTime = 0;
522
- const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
523
-
524
- while (true) {
525
- const cookies = await page.cookies();
526
- const extractedHeaders = cookies
527
- .filter(cookie => targetHeaders.includes(cookie.name.toLowerCase()))
528
- .map(cookie => `${cookie.name}=${cookie.value}`);
529
-
530
- if (targetHeaders.every(header =>
531
- extractedHeaders.some(cookie => cookie && cookie.startsWith(header + '='))
532
- )) {
533
- await browser.close();
534
- Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
535
- this.cookies.push(extractedHeaders.join(';'));
536
- this.extractCount++;
537
- return true;
538
- }
539
-
540
- await Utils.delay(500);
541
- waitTime += 500;
542
- if (waitTime >= 10000) {
543
- await browser.close();
544
- return null;
545
- }
546
- }
547
- } catch (error) {
548
- Logger.error('获取头信息出错:', error, 'Server');
549
- return null;
550
- }
551
- }
552
- async initializeTempCookies(count = 1) {
553
- Logger.info(`开始初始化 ${count} 个临时账号认证信息`, 'Server');
554
- const browserOptions = {
555
- headless: true,
556
- args: [
557
- '--no-sandbox',
558
- '--disable-setuid-sandbox',
559
- '--disable-dev-shm-usage',
560
- '--disable-gpu'
561
- ],
562
- executablePath: CONFIG.CHROME_PATH
563
- };
564
-
565
- const browsers = await Promise.all(
566
- Array.from({ length: count }, () => puppeteer.launch(browserOptions))
567
- );
568
-
569
- const cookiePromises = browsers.map(browser => this.extractGrokHeaders(browser));
570
- return Promise.all(cookiePromises);
571
- }
572
- async refreshCookies() {
573
- if (this.isRefreshing) return;
574
- this.isRefreshing = true;
575
- this.extractCount = 0;
576
- try {
577
- // 获取新的 cookies
578
- let retryCount = 0;
579
- let remainingCount = this.initialCookieCount - this.cookies.length;
580
-
581
- while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
582
- await this.initializeTempCookies(remainingCount);
583
- if (this.extractCount != remainingCount) {
584
- if (this.extractCount == 0) {
585
- Logger.error(`无法获取足够的有效 TempCookies,可能网络存在问题,当前数量:${this.cookies.length}`);
586
- } else if (this.extractCount < remainingCount) {
587
- remainingCount -= this.extractCount;
588
- this.extractCount = 0;
589
- retryCount++;
590
- await Utils.delay(1000 * retryCount);
591
- } else {
592
- break;
593
- }
594
- } else {
595
- break;
596
- }
597
- }
598
- if (this.currentIndex >= this.cookies.length) {
599
- this.currentIndex = 0;
600
- }
601
-
602
- if (this.cookies.length < this.initialCookieCount) {
603
- if (this.cookies.length !== 0) {
604
- // 如果已经获取到一些 TempCookies,则只提示警告错误
605
- Logger.error(`无法获取足够的有效 TempCookies,可能网络存在问题,当前数量:${this.cookies.length}`);
606
- } else {
607
- // 如果未获取到任何 TempCookies,则抛出错误
608
- throw new Error(`无法获取足够的有效 TempCookies,可能网络存在问题,当前数量:${this.cookies.length}`);
609
- }
610
- }
611
- } catch (error) {
612
- Logger.error('刷新 cookies 失败:', error);
613
- } finally {
614
- Logger.info(`已提取${this.cookies.length}个TempCookies`, 'Server');
615
- Logger.info(`提取的TempCookies为${JSON.stringify(this.cookies, null, 2)}`, 'Server');
616
- this.isRefreshing = false;
617
- }
618
- }
619
- }
620
-
621
- class GrokApiClient {
622
- constructor(modelId) {
623
- if (!CONFIG.MODELS[modelId]) {
624
- throw new Error(`不支持的模型: ${modelId}`);
625
- }
626
- this.modelId = CONFIG.MODELS[modelId];
627
- }
628
-
629
- processMessageContent(content) {
630
- if (typeof content === 'string') return content;
631
- return null;
632
- }
633
- // 获取图片类型
634
- getImageType(base64String) {
635
- let mimeType = 'image/jpeg';
636
- if (base64String.includes('data:image')) {
637
- const matches = base64String.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,/);
638
- if (matches) {
639
- mimeType = matches[1];
640
- }
641
- }
642
- const extension = mimeType.split('/')[1];
643
- const fileName = `image.${extension}`;
644
-
645
- return {
646
- mimeType: mimeType,
647
- fileName: fileName
648
- };
649
- }
650
-
651
- async uploadBase64Image(base64Data, url) {
652
- try {
653
- // 处理 base64 数据
654
- let imageBuffer;
655
- if (base64Data.includes('data:image')) {
656
- imageBuffer = base64Data.split(',')[1];
657
- } else {
658
- imageBuffer = base64Data
659
- }
660
- const { mimeType, fileName } = this.getImageType(base64Data);
661
- let uploadData = {
662
- rpc: "uploadFile",
663
- req: {
664
- fileName: fileName,
665
- fileMimeType: mimeType,
666
- content: imageBuffer
667
- }
668
- };
669
- Logger.info("发送图片请求", 'Server');
670
- // 发送请求
671
- const response = await fetch(url, {
672
- method: 'POST',
673
- headers: {
674
- ...CONFIG.DEFAULT_HEADERS,
675
- "cookie": CONFIG.API.SIGNATURE_COOKIE
676
- },
677
- body: JSON.stringify(uploadData)
678
- });
679
-
680
- if (!response.ok) {
681
- Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
682
- return '';
683
- }
684
-
685
- const result = await response.json();
686
- Logger.info('上传图片成功:', result, 'Server');
687
- return result.fileMetadataId;
688
-
689
- } catch (error) {
690
- Logger.error(error, 'Server');
691
- return '';
692
- }
693
- }
694
-
695
- async prepareChatRequest(request) {
696
- if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY && request.stream) {
697
- throw new Error(`该模型流式输出需要配置PICGO或者TUMY图床密钥!`);
698
- }
699
-
700
- // 处理画图模型的消息限制
701
- let todoMessages = request.messages;
702
- if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
703
- const lastMessage = todoMessages[todoMessages.length - 1];
704
- if (lastMessage.role !== 'user') {
705
- throw new Error('画图模型的最后一条消息必须是用户消息!');
706
- }
707
- todoMessages = [lastMessage];
708
- }
709
-
710
- const fileAttachments = [];
711
- let messages = '';
712
- let lastRole = null;
713
- let lastContent = '';
714
- const search = request.model === 'grok-2-search' || request.model === 'grok-3-search';
715
-
716
- // 移除<think>标签及其内容和base64图片
717
- const removeThinkTags = (text) => {
718
- text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
719
- text = text.replace(/!\[image\]\(data:.*?base64,.*?\)/g, '[图片]');
720
- return text;
721
- };
722
-
723
- const processImageUrl = async (content) => {
724
- if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
725
- const imageResponse = await this.uploadBase64Image(
726
- content.image_url.url,
727
- `${CONFIG.API.BASE_URL}/api/rpc`
728
- );
729
- return imageResponse;
730
- }
731
- return null;
732
- };
733
-
734
- const processContent = async (content) => {
735
- if (Array.isArray(content)) {
736
- let textContent = '';
737
- for (const item of content) {
738
- if (item.type === 'image_url') {
739
- textContent += (textContent ? '\n' : '') + "[图片]";
740
- } else if (item.type === 'text') {
741
- textContent += (textContent ? '\n' : '') + removeThinkTags(item.text);
742
- }
743
- }
744
- return textContent;
745
- } else if (typeof content === 'object' && content !== null) {
746
- if (content.type === 'image_url') {
747
- return "[图片]";
748
- } else if (content.type === 'text') {
749
- return removeThinkTags(content.text);
750
- }
751
- }
752
- return removeThinkTags(this.processMessageContent(content));
753
- };
754
-
755
- for (const current of todoMessages) {
756
- const role = current.role === 'assistant' ? 'assistant' : 'user';
757
- const isLastMessage = current === todoMessages[todoMessages.length - 1];
758
-
759
- // 处理图片附件
760
- if (isLastMessage && current.content) {
761
- if (Array.isArray(current.content)) {
762
- for (const item of current.content) {
763
- if (item.type === 'image_url') {
764
- const processedImage = await processImageUrl(item);
765
- if (processedImage) fileAttachments.push(processedImage);
766
- }
767
- }
768
- } else if (current.content.type === 'image_url') {
769
- const processedImage = await processImageUrl(current.content);
770
- if (processedImage) fileAttachments.push(processedImage);
771
- }
772
- }
773
-
774
- // 处理文本内容
775
- const textContent = await processContent(current.content);
776
-
777
- if (textContent || (isLastMessage && fileAttachments.length > 0)) {
778
- if (role === lastRole && textContent) {
779
- lastContent += '\n' + textContent;
780
- messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
781
- `${role.toUpperCase()}: ${lastContent}\n`;
782
- } else {
783
- messages += `${role.toUpperCase()}: ${textContent || '[图片]'}\n`;
784
- lastContent = textContent;
785
- lastRole = role;
786
- }
787
- }
788
- }
789
-
790
- return {
791
- temporary: CONFIG.API.IS_TEMP_CONVERSATION,
792
- modelName: this.modelId,
793
- message: messages.trim(),
794
- fileAttachments: fileAttachments.slice(0, 4),
795
- imageAttachments: [],
796
- disableSearch: false,
797
- enableImageGeneration: true,
798
- returnImageBytes: false,
799
- returnRawGrokInXaiRequest: false,
800
- enableImageStreaming: false,
801
- imageGenerationCount: 1,
802
- forceConcise: false,
803
- toolOverrides: {
804
- imageGen: request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen',
805
- webSearch: search,
806
- xSearch: search,
807
- xMediaSearch: search,
808
- trendsSearch: search,
809
- xPostAnalyze: search
810
- },
811
- enableSideBySide: true,
812
- isPreset: false,
813
- sendFinalMetadata: true,
814
- customInstructions: "",
815
- deepsearchPreset: request.model === 'grok-3-deepsearch' ? "default" : "",
816
- isReasoning: request.model === 'grok-3-reasoning'
817
- };
818
- }
819
- }
820
-
821
- class MessageProcessor {
822
- static createChatResponse(message, model, isStream = false) {
823
- const baseResponse = {
824
- id: `chatcmpl-${uuidv4()}`,
825
- created: Math.floor(Date.now() / 1000),
826
- model: model
827
- };
828
-
829
- if (isStream) {
830
- return {
831
- ...baseResponse,
832
- object: 'chat.completion.chunk',
833
- choices: [{
834
- index: 0,
835
- delta: {
836
- content: message
837
- }
838
- }]
839
- };
840
- }
841
-
842
- return {
843
- ...baseResponse,
844
- object: 'chat.completion',
845
- choices: [{
846
- index: 0,
847
- message: {
848
- role: 'assistant',
849
- content: message
850
- },
851
- finish_reason: 'stop'
852
- }],
853
- usage: null
854
- };
855
- }
856
- }
857
- async function processModelResponse(response, model) {
858
- let result = { token: null, imageUrl: null }
859
- if (CONFIG.IS_IMG_GEN) {
860
- if (response?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
861
- result.imageUrl = response.cachedImageGenerationResponse.imageUrl;
862
- }
863
- return result;
864
- }
865
-
866
- //非生图模型的处理
867
- switch (model) {
868
- case 'grok-2':
869
- result.token = response?.token;
870
- return result;
871
- case 'grok-2-search':
872
- case 'grok-3-search':
873
- if (response?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
874
- result.token = `\r\n<think>${await Utils.organizeSearchResults(response.webSearchResults)}</think>\r\n`;
875
- } else {
876
- result.token = response?.token;
877
- }
878
- return result;
879
- case 'grok-3':
880
- result.token = response?.token;
881
- return result;
882
- case 'grok-3-deepsearch':
883
- if (response?.messageTag === "final") {
884
- result.token = response?.token;
885
- }
886
- return result;
887
- case 'grok-3-reasoning':
888
- if (response?.isThinking && !CONFIG.SHOW_THINKING) return result;
889
-
890
- if (response?.isThinking && !CONFIG.IS_THINKING) {
891
- result.token = "<think>" + response?.token;
892
- CONFIG.IS_THINKING = true;
893
- } else if (!response.isThinking && CONFIG.IS_THINKING) {
894
- result.token = "</think>" + response?.token;
895
- CONFIG.IS_THINKING = false;
896
- } else {
897
- result.token = response?.token;
898
- }
899
- return result;
900
- }
901
- return result;
902
- }
903
-
904
- async function handleResponse(response, model, res, isStream) {
905
- try {
906
- const stream = response.body;
907
- let buffer = '';
908
- let fullResponse = '';
909
- const dataPromises = [];
910
- if (isStream) {
911
- res.setHeader('Content-Type', 'text/event-stream');
912
- res.setHeader('Cache-Control', 'no-cache');
913
- res.setHeader('Connection', 'keep-alive');
914
- }
915
- CONFIG.IS_THINKING = false;
916
- CONFIG.IS_IMG_GEN = false;
917
- CONFIG.IS_IMG_GEN2 = false;
918
- Logger.info("开始处理流式响应", 'Server');
919
-
920
- return new Promise((resolve, reject) => {
921
- stream.on('data', async (chunk) => {
922
- buffer += chunk.toString();
923
- const lines = buffer.split('\n');
924
- buffer = lines.pop() || '';
925
-
926
- for (const line of lines) {
927
- if (!line.trim()) continue;
928
- try {
929
- const linejosn = JSON.parse(line.trim());
930
- if (linejosn?.error) {
931
- Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
932
- if (linejosn.error?.name === "RateLimitError") {
933
- CONFIG.API.TEMP_COOKIE = null;
934
- }
935
- stream.destroy();
936
- reject(new Error("RateLimitError"));
937
- return;
938
- }
939
- let response = linejosn?.result?.response;
940
- if (!response) continue;
941
- if (response?.doImgGen || response?.imageAttachmentInfo) {
942
- CONFIG.IS_IMG_GEN = true;
943
- }
944
- const processPromise = (async () => {
945
- const result = await processModelResponse(response, model);
946
-
947
- if (result.token) {
948
- if (isStream) {
949
- res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
950
- } else {
951
- fullResponse += result.token;
952
- }
953
- }
954
- if (result.imageUrl) {
955
- CONFIG.IS_IMG_GEN2 = true;
956
- const dataImage = await handleImageResponse(result.imageUrl);
957
- if (isStream) {
958
- res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(dataImage, model, true))}\n\n`);
959
- } else {
960
- res.json(MessageProcessor.createChatResponse(dataImage, model));
961
- }
962
- }
963
- })();
964
- dataPromises.push(processPromise);
965
- } catch (error) {
966
- Logger.error(error, 'Server');
967
- continue;
968
- }
969
- }
970
- });
971
-
972
- stream.on('end', async () => {
973
- try {
974
- await Promise.all(dataPromises);
975
- if (isStream) {
976
- res.write('data: [DONE]\n\n');
977
- res.end();
978
- } else {
979
- if (!CONFIG.IS_IMG_GEN2) {
980
- res.json(MessageProcessor.createChatResponse(fullResponse, model));
981
- }
982
- }
983
- resolve();
984
- } catch (error) {
985
- Logger.error(error, 'Server');
986
- reject(error);
987
- }
988
- });
989
-
990
- stream.on('error', (error) => {
991
- Logger.error(error, 'Server');
992
- reject(error);
993
- });
994
- });
995
- } catch (error) {
996
- Logger.error(error, 'Server');
997
- throw new Error(error);
998
- }
999
- }
1000
-
1001
- async function handleImageResponse(imageUrl) {
1002
- const MAX_RETRIES = 2;
1003
- let retryCount = 0;
1004
- let imageBase64Response;
1005
-
1006
- while (retryCount < MAX_RETRIES) {
1007
- try {
1008
- imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
1009
- method: 'GET',
1010
- headers: {
1011
- ...DEFAULT_HEADERS,
1012
- "cookie": CONFIG.API.SIGNATURE_COOKIE
1013
- }
1014
- });
1015
-
1016
- if (imageBase64Response.ok) break;
1017
- retryCount++;
1018
- if (retryCount === MAX_RETRIES) {
1019
- throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
1020
- }
1021
- await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
1022
-
1023
- } catch (error) {
1024
- Logger.error(error, 'Server');
1025
- retryCount++;
1026
- if (retryCount === MAX_RETRIES) {
1027
- throw error;
1028
- }
1029
- await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
1030
- }
1031
- }
1032
-
1033
-
1034
- const arrayBuffer = await imageBase64Response.arrayBuffer();
1035
- const imageBuffer = Buffer.from(arrayBuffer);
1036
-
1037
- if (!CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY) {
1038
- const base64Image = imageBuffer.toString('base64');
1039
- const imageContentType = imageBase64Response.headers.get('content-type');
1040
- return `![image](data:${imageContentType};base64,${base64Image})`
1041
- }
1042
-
1043
- Logger.info("开始上传图床", 'Server');
1044
- const formData = new FormData();
1045
- if (CONFIG.API.PICGO_KEY) {
1046
- formData.append('source', imageBuffer, {
1047
- filename: `image-${Date.now()}.jpg`,
1048
- contentType: 'image/jpeg'
1049
- });
1050
- const formDataHeaders = formData.getHeaders();
1051
- const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
1052
- method: "POST",
1053
- headers: {
1054
- ...formDataHeaders,
1055
- "Content-Type": "multipart/form-data",
1056
- "X-API-Key": CONFIG.API.PICGO_KEY
1057
- },
1058
- body: formData
1059
- });
1060
- if (!responseURL.ok) {
1061
- return "生图失败,请查看PICGO图床密钥是否设置正确"
1062
- } else {
1063
- Logger.info("生图成功", 'Server');
1064
- const result = await responseURL.json();
1065
- return `![image](${result.image.url})`
1066
- }
1067
- } else if (CONFIG.API.TUMY_KEY) {
1068
- const formData = new FormData();
1069
- formData.append('file', imageBuffer, {
1070
- filename: `image-${Date.now()}.jpg`,
1071
- contentType: 'image/jpeg'
1072
- });
1073
- const formDataHeaders = formData.getHeaders();
1074
- const responseURL = await fetch("https://tu.my/api/v1/upload", {
1075
- method: "POST",
1076
- headers: {
1077
- ...formDataHeaders,
1078
- "Accept": "application/json",
1079
- 'Authorization': `Bearer ${CONFIG.API.TUMY_KEY}`
1080
- },
1081
- body: formData
1082
- });
1083
- if (!responseURL.ok) {
1084
- return "生图失败,请查看TUMY图床密钥是否设置正确"
1085
- } else {
1086
- try {
1087
- const result = await responseURL.json();
1088
- Logger.info("生图成功", 'Server');
1089
- return `![image](${result.data.links.url})`
1090
- } catch (error) {
1091
- Logger.error(error, 'Server');
1092
- return "生图失败,请查看TUMY图床密钥是否设置正确"
1093
- }
1094
- }
1095
- }
1096
- }
1097
-
1098
- const tokenManager = new AuthTokenManager();
1099
- const tempCookieManager = new GrokTempCookieManager();
1100
- await initialization();
1101
-
1102
- // 中间件配置
1103
- const app = express();
1104
- app.use(Logger.requestLogger);
1105
- app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
1106
- app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
1107
- app.use(cors({
1108
- origin: '*',
1109
- methods: ['GET', 'POST', 'OPTIONS'],
1110
- allowedHeaders: ['Content-Type', 'Authorization']
1111
- }));
1112
-
1113
-
1114
- app.get('/hf/get/tokens', (req, res) => {
1115
- const authToken = req.headers.authorization?.replace('Bearer ', '');
1116
- if (CONFIG.API.IS_CUSTOM_SSO) {
1117
- return res.status(403).json({ error: '自定义的SSO令牌模式无法获取轮询sso令牌状态' });
1118
- } else if (authToken !== CONFIG.API.API_KEY) {
1119
- return res.status(401).json({ error: 'Unauthorized' });
1120
- }
1121
- res.json(tokenManager.getTokenStatusMap());
1122
- });
1123
- app.post('/hf/add/token', async (req, res) => {
1124
- const authToken = req.headers.authorization?.replace('Bearer ', '');
1125
- if (CONFIG.API.IS_CUSTOM_SSO) {
1126
- return res.status(403).json({ error: '自定义的SSO令牌模式无法添加sso令牌' });
1127
- } else if (authToken !== CONFIG.API.API_KEY) {
1128
- return res.status(401).json({ error: 'Unauthorized' });
1129
- }
1130
- try {
1131
- const sso = req.body.sso;
1132
- await tokenManager.addToken(`sso-rw=${sso};sso=${sso}`);
1133
- res.status(200).json(tokenManager.getTokenStatusMap()[sso]);
1134
- } catch (error) {
1135
- Logger.error(error, 'Server');
1136
- res.status(500).json({ error: '添加sso令牌失败' });
1137
- }
1138
- });
1139
- app.post('/hf/delete/token', async (req, res) => {
1140
- const authToken = req.headers.authorization?.replace('Bearer ', '');
1141
- if (CONFIG.API.IS_CUSTOM_SSO) {
1142
- return res.status(403).json({ error: '自定义的SSO令牌模式无法删除sso令牌' });
1143
- } else if (authToken !== CONFIG.API.API_KEY) {
1144
- return res.status(401).json({ error: 'Unauthorized' });
1145
- }
1146
- try {
1147
- const sso = req.body.sso;
1148
- await tokenManager.deleteToken(`sso-rw=${sso};sso=${sso}`);
1149
- res.status(200).json({ message: '删除sso令牌成功' });
1150
- } catch (error) {
1151
- Logger.error(error, 'Server');
1152
- res.status(500).json({ error: '删除sso令牌失败' });
1153
- }
1154
- });
1155
-
1156
- app.get('/hf/v1/models', (req, res) => {
1157
- res.json({
1158
- object: "list",
1159
- data: Object.keys(CONFIG.MODELS).map((model, index) => ({
1160
- id: model,
1161
- object: "model",
1162
- created: Math.floor(Date.now() / 1000),
1163
- owned_by: "grok",
1164
- }))
1165
- });
1166
- });
1167
-
1168
-
1169
- app.post('/hf/v1/chat/completions', async (req, res) => {
1170
- try {
1171
- const authToken = req.headers.authorization?.replace('Bearer ', '');
1172
- if (CONFIG.API.IS_CUSTOM_SSO) {
1173
- if (authToken) {
1174
- const result = `sso=${authToken};ssp_rw=${authToken}`;
1175
- tokenManager.setToken(result);
1176
- } else {
1177
- return res.status(401).json({ error: '自定义的SSO令牌缺失' });
1178
- }
1179
- } else if (authToken !== CONFIG.API.API_KEY) {
1180
- return res.status(401).json({ error: 'Unauthorized' });
1181
- }
1182
- const { model, stream } = req.body;
1183
- let isTempCookie = model.includes("grok-2") && CONFIG.API.IS_TEMP_GROK2;
1184
- let retryCount = 0;
1185
- const grokClient = new GrokApiClient(model);
1186
- const requestPayload = await grokClient.prepareChatRequest(req.body);
1187
- //Logger.info(`请求体: ${JSON.stringify(requestPayload, null, 2)}`, 'Server');
1188
-
1189
- while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
1190
- retryCount++;
1191
- if (isTempCookie) {
1192
- CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
1193
- Logger.info(`已切换为临时令牌`, 'Server');
1194
- } else {
1195
- CONFIG.API.SIGNATURE_COOKIE = await Utils.createAuthHeaders(model);
1196
- }
1197
- if (!CONFIG.API.SIGNATURE_COOKIE) {
1198
- throw new Error('该模型无可用令牌');
1199
- }
1200
- Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE, null, 2)}`, 'Server');
1201
- Logger.info(`当前可用模型的全部可用数量: ${JSON.stringify(tokenManager.getRemainingTokenRequestCapacity(), null, 2)}`, 'Server');
1202
- const response = await fetch(`${CONFIG.API.BASE_URL}/rest/app-chat/conversations/new`, {
1203
- method: 'POST',
1204
- headers: {
1205
- ...DEFAULT_HEADERS,
1206
- "Cookie": CONFIG.API.SIGNATURE_COOKIE
1207
- },
1208
- body: JSON.stringify(requestPayload)
1209
- });
1210
-
1211
- if (response.ok) {
1212
- Logger.info(`请求成功`, 'Server');
1213
- Logger.info(`当前${model}剩余可用令牌数: ${tokenManager.getTokenCountForModel(model)}`, 'Server');
1214
- try {
1215
- await handleResponse(response, model, res, stream);
1216
- Logger.info(`请求结束`, 'Server');
1217
- return;
1218
- } catch (error) {
1219
- Logger.error(error, 'Server');
1220
- if (isTempCookie) {
1221
- tempCookieManager.cookies.splice(tempCookieManager.currentIndex, 1);
1222
- if (tempCookieManager.cookies.length != 0) {
1223
- tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
1224
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
1225
- tempCookieManager.ensureCookies()
1226
- } else {
1227
- try {
1228
- await tempCookieManager.ensureCookies();
1229
- tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
1230
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
1231
- } catch (error) {
1232
- throw error;
1233
- }
1234
- }
1235
- } else {
1236
- if (CONFIG.API.IS_CUSTOM_SSO) throw new Error(`自定义SSO令牌当前模型${model}的请求次数已失效`);
1237
- tokenManager.removeTokenFromModel(model, CONFIG.API.SIGNATURE_COOKIE.cookie);
1238
- if (tokenManager.getTokenCountForModel(model) === 0) {
1239
- throw new Error(`${model} 次数已达上限,请切换其他模型或者重新对话`);
1240
- }
1241
- }
1242
- }
1243
- } else {
1244
- if (response.status === 429) {
1245
- if (isTempCookie) {
1246
- // 移除当前失效的 cookie
1247
- tempCookieManager.cookies.splice(tempCookieManager.currentIndex, 1);
1248
- if (tempCookieManager.cookies.length != 0) {
1249
- tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
1250
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
1251
- tempCookieManager.ensureCookies()
1252
- } else {
1253
- try {
1254
- await tempCookieManager.ensureCookies();
1255
- tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
1256
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
1257
- } catch (error) {
1258
- throw error;
1259
- }
1260
- }
1261
- } else {
1262
- if (CONFIG.API.IS_CUSTOM_SSO) throw new Error(`自定义SSO令牌当前模型${model}的请求次数已失效`);
1263
- tokenManager.removeTokenFromModel(model, CONFIG.API.SIGNATURE_COOKIE.cookie);
1264
- if (tokenManager.getTokenCountForModel(model) === 0) {
1265
- throw new Error(`${model} 次数已达上限,请切换其他模型或者重新对话`);
1266
- }
1267
- }
1268
- } else {
1269
- // 非429错误直接抛出
1270
- if (isTempCookie) {
1271
- // 移除当前失效的 cookie
1272
- tempCookieManager.cookies.splice(tempCookieManager.currentIndex, 1);
1273
- if (tempCookieManager.cookies.length != 0) {
1274
- tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
1275
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
1276
- tempCookieManager.ensureCookies()
1277
- } else {
1278
- try {
1279
- await tempCookieManager.ensureCookies();
1280
- tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
1281
- CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
1282
- } catch (error) {
1283
- throw error;
1284
- }
1285
- }
1286
- } else {
1287
- if (CONFIG.API.IS_CUSTOM_SSO) throw new Error(`自定义SSO令牌当前模型${model}的请求次数已失效`);
1288
- Logger.error(`令牌异常错误状态!status: ${response.status}`, 'Server');
1289
- tokenManager.removeTokenFromModel(model, CONFIG.API.SIGNATURE_COOKIE.cookie);
1290
- Logger.info(`当前${model}剩余可用令牌数: ${tokenManager.getTokenCountForModel(model)}`, 'Server');
1291
- }
1292
- }
1293
- }
1294
- }
1295
- throw new Error('当前模型所有令牌都已耗尽');
1296
- } catch (error) {
1297
- Logger.error(error, 'ChatAPI');
1298
- res.status(500).json({
1299
- error: {
1300
- message: error.message || error,
1301
- type: 'server_error'
1302
- }
1303
- });
1304
- }
1305
- });
1306
-
1307
-
1308
- app.use((req, res) => {
1309
- res.status(200).send('api运行正常');
1310
- });
1311
-
1312
-
1313
- app.listen(CONFIG.SERVER.PORT, () => {
1314
- Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
1315
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
logger.js DELETED
@@ -1,66 +0,0 @@
1
- import chalk from 'chalk';
2
- import moment from 'moment';
3
-
4
- const LogLevel = {
5
- INFO: 'INFO',
6
- WARN: 'WARN',
7
- ERROR: 'ERROR',
8
- DEBUG: 'DEBUG'
9
- };
10
-
11
- class Logger {
12
- static formatMessage(level, message) {
13
- const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');
14
-
15
- switch(level) {
16
- case LogLevel.INFO:
17
- return chalk.blue(`[${timestamp}] [${level}] ${message}`);
18
- case LogLevel.WARN:
19
- return chalk.yellow(`[${timestamp}] [${level}] ${message}`);
20
- case LogLevel.ERROR:
21
- return chalk.red(`[${timestamp}] [${level}] ${message}`);
22
- case LogLevel.DEBUG:
23
- return chalk.gray(`[${timestamp}] [${level}] ${message}`);
24
- default:
25
- return message;
26
- }
27
- }
28
-
29
- static info(message, context) {
30
- console.log(this.formatMessage(LogLevel.INFO, context ? `[${context}] ${message}` : message));
31
- }
32
-
33
- static warn(message, context) {
34
- console.warn(this.formatMessage(LogLevel.WARN, context ? `[${context}] ${message}` : message));
35
- }
36
-
37
- static error(message, context, error = null) {
38
- const errorMessage = error ? ` - ${error.message}` : '';
39
- console.error(this.formatMessage(LogLevel.ERROR, `${context ? `[${context}] ` : ''}${message}${errorMessage}`));
40
- }
41
-
42
- static debug(message, context) {
43
- if (process.env.NODE_ENV === 'development') {
44
- console.debug(this.formatMessage(LogLevel.DEBUG, context ? `[${context}] ${message}` : message));
45
- }
46
- }
47
-
48
- static requestLogger(req, res, next) {
49
- const startTime = Date.now();
50
-
51
- res.on('finish', () => {
52
- const duration = Date.now() - startTime;
53
- const logMessage = `${req.method} ${req.path} - ${res.statusCode} (${duration}ms)`;
54
-
55
- if (res.statusCode >= 400) {
56
- Logger.error(logMessage, undefined, 'HTTP');
57
- } else {
58
- Logger.info(logMessage, 'HTTP');
59
- }
60
- });
61
-
62
- next();
63
- }
64
- }
65
-
66
- export default Logger;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "name": "grok2api",
3
- "version": "1.0.0",
4
- "main": "index.js",
5
- "type": "module",
6
- "scripts": {
7
- "start": "node index.js"
8
- },
9
- "author": "yxmiler",
10
- "dependencies": {
11
- "express": "^4.18.2",
12
- "node-fetch": "^3.3.2",
13
- "dotenv": "^16.3.1",
14
- "cors": "^2.8.5",
15
- "form-data": "^4.0.0",
16
- "puppeteer": "^22.8.2",
17
- "puppeteer-extra": "^3.3.6",
18
- "puppeteer-extra-plugin-stealth": "^2.11.2",
19
- "moment": "^2.30.1",
20
- "chalk": "^5.4.1",
21
- "uuid": "^9.0.0"
22
- }
23
- }