Jones0 commited on
Commit
0c57c75
·
verified ·
1 Parent(s): 201afff

Create index.js

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