Spaces:
Sleeping
Sleeping
Update index.js
Browse files
index.js
CHANGED
|
@@ -23,7 +23,9 @@ const CONFIG = {
|
|
| 23 |
"grok-3-reasoning": "grok-3"
|
| 24 |
},
|
| 25 |
API: {
|
| 26 |
-
|
|
|
|
|
|
|
| 27 |
BASE_URL: "https://grok.com",
|
| 28 |
API_KEY: process.env.API_KEY || "sk-123456",
|
| 29 |
SIGNATURE_COOKIE: null,
|
|
@@ -43,10 +45,12 @@ const CONFIG = {
|
|
| 43 |
IS_IMG_GEN: false,
|
| 44 |
IS_IMG_GEN2: false,
|
| 45 |
SSO_INDEX: 0,//sso的索引
|
| 46 |
-
|
| 47 |
-
|
|
|
|
| 48 |
};
|
| 49 |
puppeteer.use(StealthPlugin())
|
|
|
|
| 50 |
// 请求头配置
|
| 51 |
const DEFAULT_HEADERS = {
|
| 52 |
'accept': '*/*',
|
|
@@ -68,8 +72,18 @@ const DEFAULT_HEADERS = {
|
|
| 68 |
|
| 69 |
|
| 70 |
async function initialization() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
if (CONFIG.API.IS_CUSTOM_SSO) {
|
| 72 |
-
|
|
|
|
|
|
|
| 73 |
return;
|
| 74 |
}
|
| 75 |
const ssoArray = process.env.SSO.split(',');
|
|
@@ -77,7 +91,11 @@ async function initialization() {
|
|
| 77 |
tokenManager.addToken(`sso-rw=${sso};sso=${sso}`);
|
| 78 |
});
|
| 79 |
console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
Logger.info("初始化完成", 'Server');
|
| 82 |
}
|
| 83 |
|
|
@@ -87,30 +105,43 @@ class AuthTokenManager {
|
|
| 87 |
this.activeTokens = [];
|
| 88 |
this.expiredTokens = new Map();
|
| 89 |
this.tokenModelFrequency = new Map();
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
addToken(token) {
|
| 98 |
if (!this.activeTokens.includes(token)) {
|
| 99 |
this.activeTokens.push(token);
|
| 100 |
-
this.tokenModelFrequency.set(token,
|
| 101 |
-
"grok-3": 0,
|
| 102 |
-
"grok-3-deepsearch": 0,
|
| 103 |
-
"grok-3-reasoning": 0
|
| 104 |
-
});
|
| 105 |
}
|
| 106 |
}
|
| 107 |
setToken(token) {
|
| 108 |
this.activeTokens = [token];
|
| 109 |
-
this.tokenModelFrequency.set(token,
|
| 110 |
-
"grok-3": 0,
|
| 111 |
-
"grok-3-deepsearch": 0,
|
| 112 |
-
"grok-3-reasoning": 0
|
| 113 |
-
});
|
| 114 |
}
|
| 115 |
|
| 116 |
getTokenByIndex(index, model) {
|
|
@@ -123,8 +154,8 @@ class AuthTokenManager {
|
|
| 123 |
}
|
| 124 |
|
| 125 |
recordModelRequest(token, model) {
|
| 126 |
-
if (model
|
| 127 |
-
model = '
|
| 128 |
}
|
| 129 |
|
| 130 |
if (!this.modelRateLimit[model]) return;
|
|
@@ -135,16 +166,16 @@ class AuthTokenManager {
|
|
| 135 |
this.checkAndRemoveTokenIfLimitReached(token);
|
| 136 |
}
|
| 137 |
setModelLimit(index, model) {
|
| 138 |
-
if (model
|
| 139 |
-
model = '
|
| 140 |
}
|
| 141 |
if (!this.modelRateLimit[model]) return;
|
| 142 |
const tokenFrequency = this.tokenModelFrequency.get(this.activeTokens[index]);
|
| 143 |
tokenFrequency[model] = 9999;
|
| 144 |
}
|
| 145 |
isTokenModelLimitReached(index, model) {
|
| 146 |
-
if (model
|
| 147 |
-
model = '
|
| 148 |
}
|
| 149 |
if (!this.modelRateLimit[model]) return;
|
| 150 |
const token = this.activeTokens[index];
|
|
@@ -188,11 +219,7 @@ class AuthTokenManager {
|
|
| 188 |
const now = Date.now();
|
| 189 |
for (const [token, expiredTime] of this.expiredTokens.entries()) {
|
| 190 |
if (now - expiredTime >= 2 * 60 * 60 * 1000) {
|
| 191 |
-
this.
|
| 192 |
-
"grok-3": 0,
|
| 193 |
-
"grok-3-deepsearch": 0,
|
| 194 |
-
"grok-3-reasoning": 0
|
| 195 |
-
});
|
| 196 |
this.activeTokens.push(token);
|
| 197 |
this.expiredTokens.delete(token);
|
| 198 |
Logger.info(`令牌${token}已恢复,已添加到可用令牌列表`, 'TokenManager');
|
|
@@ -211,61 +238,10 @@ class AuthTokenManager {
|
|
| 211 |
}
|
| 212 |
|
| 213 |
class Utils {
|
| 214 |
-
static
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
headless: true,
|
| 219 |
-
args: [
|
| 220 |
-
'--no-sandbox',
|
| 221 |
-
'--disable-setuid-sandbox',
|
| 222 |
-
'--disable-dev-shm-usage',
|
| 223 |
-
'--disable-gpu'
|
| 224 |
-
],
|
| 225 |
-
executablePath: CONFIG.CHROME_PATH
|
| 226 |
-
});
|
| 227 |
-
|
| 228 |
-
const page = await browser.newPage();
|
| 229 |
-
await page.goto('https://grok.com/', { waitUntil: 'domcontentloaded' });
|
| 230 |
-
await page.evaluate(() => {
|
| 231 |
-
return new Promise(resolve => setTimeout(resolve, 5000))
|
| 232 |
-
})
|
| 233 |
-
// 获取所有 Cookies
|
| 234 |
-
const cookies = await page.cookies();
|
| 235 |
-
const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
|
| 236 |
-
const extractedHeaders = {};
|
| 237 |
-
for (const cookie of cookies) {
|
| 238 |
-
if (targetHeaders.includes(cookie.name.toLowerCase())) {
|
| 239 |
-
extractedHeaders[cookie.name.toLowerCase()] = cookie.value;
|
| 240 |
-
}
|
| 241 |
-
}
|
| 242 |
-
await browser.close();
|
| 243 |
-
Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
|
| 244 |
-
return extractedHeaders;
|
| 245 |
-
|
| 246 |
-
} catch (error) {
|
| 247 |
-
Logger.error('获取头信息出错:', error, 'Server');
|
| 248 |
-
return null;
|
| 249 |
-
}
|
| 250 |
-
}
|
| 251 |
-
static async get_signature() {
|
| 252 |
-
if (CONFIG.API.TEMP_COOKIE) {
|
| 253 |
-
return CONFIG.API.TEMP_COOKIE;
|
| 254 |
-
}
|
| 255 |
-
Logger.info("刷新认证信息", 'Server');
|
| 256 |
-
let retryCount = 0;
|
| 257 |
-
while (!CONFIG.API.TEMP_COOKIE || retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
|
| 258 |
-
let headers = await Utils.extractGrokHeaders();
|
| 259 |
-
if (headers) {
|
| 260 |
-
Logger.info("获取认证信息成功", 'Server');
|
| 261 |
-
CONFIG.API.TEMP_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
|
| 262 |
-
return;
|
| 263 |
-
}
|
| 264 |
-
retryCount++;
|
| 265 |
-
if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
|
| 266 |
-
throw new Error(`获取认证信息失败!`);
|
| 267 |
-
}
|
| 268 |
-
}
|
| 269 |
}
|
| 270 |
static async organizeSearchResults(searchResults) {
|
| 271 |
// 确保传入的是有效的搜索结果对象
|
|
@@ -285,12 +261,128 @@ class Utils {
|
|
| 285 |
return formattedResults.join('\n\n');
|
| 286 |
}
|
| 287 |
static async createAuthHeaders(model) {
|
| 288 |
-
return
|
| 289 |
-
'cookie': `${await tokenManager.getTokenByIndex(CONFIG.SSO_INDEX, model)}`
|
| 290 |
-
};
|
| 291 |
}
|
| 292 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
|
| 295 |
class GrokApiClient {
|
| 296 |
constructor(modelId) {
|
|
@@ -346,7 +438,7 @@ class GrokApiClient {
|
|
| 346 |
method: 'POST',
|
| 347 |
headers: {
|
| 348 |
...CONFIG.DEFAULT_HEADERS,
|
| 349 |
-
|
| 350 |
},
|
| 351 |
body: JSON.stringify(uploadData)
|
| 352 |
});
|
|
@@ -368,7 +460,7 @@ class GrokApiClient {
|
|
| 368 |
|
| 369 |
async prepareChatRequest(request) {
|
| 370 |
if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY && request.stream) {
|
| 371 |
-
throw new Error(`该模型流式输出需要配置PICGO或者
|
| 372 |
}
|
| 373 |
|
| 374 |
// 处理画图模型的消息限制
|
|
@@ -527,11 +619,11 @@ class MessageProcessor {
|
|
| 527 |
};
|
| 528 |
}
|
| 529 |
}
|
| 530 |
-
async function processModelResponse(
|
| 531 |
let result = { token: null, imageUrl: null }
|
| 532 |
if (CONFIG.IS_IMG_GEN) {
|
| 533 |
-
if (
|
| 534 |
-
result.imageUrl =
|
| 535 |
}
|
| 536 |
return result;
|
| 537 |
}
|
|
@@ -539,35 +631,35 @@ async function processModelResponse(linejosn, model) {
|
|
| 539 |
//非生图模型的处理
|
| 540 |
switch (model) {
|
| 541 |
case 'grok-2':
|
| 542 |
-
result.token =
|
| 543 |
return result;
|
| 544 |
case 'grok-2-search':
|
| 545 |
case 'grok-3-search':
|
| 546 |
-
if (
|
| 547 |
-
result.token = `\r\n<think>${await Utils.organizeSearchResults(
|
| 548 |
} else {
|
| 549 |
-
result.token =
|
| 550 |
}
|
| 551 |
return result;
|
| 552 |
case 'grok-3':
|
| 553 |
-
result.token =
|
| 554 |
return result;
|
| 555 |
case 'grok-3-deepsearch':
|
| 556 |
-
if (
|
| 557 |
-
result.token =
|
| 558 |
}
|
| 559 |
return result;
|
| 560 |
case 'grok-3-reasoning':
|
| 561 |
-
if (
|
| 562 |
|
| 563 |
-
if (
|
| 564 |
-
result.token = "<think>" +
|
| 565 |
CONFIG.IS_THINKING = true;
|
| 566 |
-
} else if (!
|
| 567 |
-
result.token = "</think>" +
|
| 568 |
CONFIG.IS_THINKING = false;
|
| 569 |
} else {
|
| 570 |
-
result.token =
|
| 571 |
}
|
| 572 |
return result;
|
| 573 |
}
|
|
@@ -585,6 +677,9 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 585 |
res.setHeader('Cache-Control', 'no-cache');
|
| 586 |
res.setHeader('Connection', 'keep-alive');
|
| 587 |
}
|
|
|
|
|
|
|
|
|
|
| 588 |
Logger.info("开始处理流式响应", 'Server');
|
| 589 |
|
| 590 |
return new Promise((resolve, reject) => {
|
|
@@ -595,50 +690,46 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 595 |
|
| 596 |
for (const line of lines) {
|
| 597 |
if (!line.trim()) continue;
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
if (
|
| 603 |
-
|
| 604 |
-
const linejosn = JSON.parse(data);
|
| 605 |
-
if (linejosn?.error) {
|
| 606 |
-
Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
|
| 607 |
-
if(linejosn.error?.name === "RateLimitError"){
|
| 608 |
-
CONFIG.API.TEMP_COOKIE = null;
|
| 609 |
-
}
|
| 610 |
-
stream.destroy();
|
| 611 |
-
reject(new Error("RateLimitError"));
|
| 612 |
-
return;
|
| 613 |
-
}
|
| 614 |
-
if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
|
| 615 |
-
CONFIG.IS_IMG_GEN = true;
|
| 616 |
}
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 626 |
}
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
}
|
| 636 |
-
}
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
}
|
| 643 |
}
|
| 644 |
});
|
|
@@ -654,8 +745,6 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 654 |
res.json(MessageProcessor.createChatResponse(fullResponse, model));
|
| 655 |
}
|
| 656 |
}
|
| 657 |
-
CONFIG.IS_IMG_GEN = false;
|
| 658 |
-
CONFIG.IS_IMG_GEN2 = false;
|
| 659 |
resolve();
|
| 660 |
} catch (error) {
|
| 661 |
Logger.error(error, 'Server');
|
|
@@ -670,8 +759,6 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 670 |
});
|
| 671 |
} catch (error) {
|
| 672 |
Logger.error(error, 'Server');
|
| 673 |
-
CONFIG.IS_IMG_GEN = false;
|
| 674 |
-
CONFIG.IS_IMG_GEN2 = false;
|
| 675 |
throw new Error(error);
|
| 676 |
}
|
| 677 |
}
|
|
@@ -687,7 +774,7 @@ async function handleImageResponse(imageUrl) {
|
|
| 687 |
method: 'GET',
|
| 688 |
headers: {
|
| 689 |
...DEFAULT_HEADERS,
|
| 690 |
-
|
| 691 |
}
|
| 692 |
});
|
| 693 |
|
|
@@ -718,9 +805,9 @@ async function handleImageResponse(imageUrl) {
|
|
| 718 |
return ``
|
| 719 |
}
|
| 720 |
|
| 721 |
-
Logger.info("
|
| 722 |
const formData = new FormData();
|
| 723 |
-
if(CONFIG.API.PICGO_KEY){
|
| 724 |
formData.append('source', imageBuffer, {
|
| 725 |
filename: `image-${Date.now()}.jpg`,
|
| 726 |
contentType: 'image/jpeg'
|
|
@@ -729,11 +816,11 @@ async function handleImageResponse(imageUrl) {
|
|
| 729 |
const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
|
| 730 |
method: "POST",
|
| 731 |
headers: {
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
});
|
| 738 |
if (!responseURL.ok) {
|
| 739 |
return "生图失败,请查看PICGO图床密钥是否设置正确"
|
|
@@ -742,7 +829,7 @@ async function handleImageResponse(imageUrl) {
|
|
| 742 |
const result = await responseURL.json();
|
| 743 |
return ``
|
| 744 |
}
|
| 745 |
-
}else if(CONFIG.API.TUMY_KEY){
|
| 746 |
const formData = new FormData();
|
| 747 |
formData.append('file', imageBuffer, {
|
| 748 |
filename: `image-${Date.now()}.jpg`,
|
|
@@ -761,19 +848,20 @@ async function handleImageResponse(imageUrl) {
|
|
| 761 |
if (!responseURL.ok) {
|
| 762 |
return "生图失败,请查看TUMY图床密钥是否设置正确"
|
| 763 |
} else {
|
| 764 |
-
Logger.info("生图成功", 'Server');
|
| 765 |
-
const result = await responseURL.json();
|
| 766 |
try {
|
| 767 |
-
|
| 768 |
-
|
|
|
|
| 769 |
} catch (error) {
|
| 770 |
-
|
|
|
|
| 771 |
}
|
| 772 |
}
|
| 773 |
}
|
| 774 |
}
|
| 775 |
|
| 776 |
const tokenManager = new AuthTokenManager();
|
|
|
|
| 777 |
await initialization();
|
| 778 |
|
| 779 |
// 中间件配置
|
|
@@ -813,18 +901,14 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
|
|
| 813 |
} else if (authToken !== CONFIG.API.API_KEY) {
|
| 814 |
return res.status(401).json({ error: 'Unauthorized' });
|
| 815 |
}
|
| 816 |
-
let isTempCookie = req.body.model.includes("grok-2");
|
| 817 |
let retryCount = 0;
|
| 818 |
const grokClient = new GrokApiClient(req.body.model);
|
| 819 |
const requestPayload = await grokClient.prepareChatRequest(req.body);
|
| 820 |
-
Logger.info(`请求体: ${JSON.stringify(requestPayload,null,2)}`, 'Server');
|
| 821 |
|
| 822 |
while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
|
| 823 |
retryCount++;
|
| 824 |
-
if (!CONFIG.API.TEMP_COOKIE) {
|
| 825 |
-
await Utils.get_signature();
|
| 826 |
-
}
|
| 827 |
-
|
| 828 |
if (isTempCookie) {
|
| 829 |
CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
|
| 830 |
Logger.info(`已切换为临时令牌`, 'Server');
|
|
@@ -835,38 +919,15 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
|
|
| 835 |
throw new Error('该模型无可用令牌');
|
| 836 |
}
|
| 837 |
Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
|
| 838 |
-
Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE,null,2)}`, 'Server');
|
| 839 |
-
const
|
| 840 |
-
method: 'POST',
|
| 841 |
-
headers: {
|
| 842 |
-
...DEFAULT_HEADERS,
|
| 843 |
-
...CONFIG.API.SIGNATURE_COOKIE
|
| 844 |
-
},
|
| 845 |
-
body: JSON.stringify({
|
| 846 |
-
rpc: "createConversation",
|
| 847 |
-
req: {
|
| 848 |
-
temporary: false
|
| 849 |
-
}
|
| 850 |
-
})
|
| 851 |
-
});
|
| 852 |
-
let conversationId;
|
| 853 |
-
var responseText2 = await newMessageReq.clone().text();
|
| 854 |
-
if (newMessageReq.status === 200) {
|
| 855 |
-
const responseText = await newMessageReq.json();
|
| 856 |
-
conversationId = responseText.conversationId;
|
| 857 |
-
} else {
|
| 858 |
-
Logger.error(`创建会话响应错误: ${responseText2}`, 'Server');
|
| 859 |
-
throw new Error(`创建会话响应错误: ${responseText2}`);
|
| 860 |
-
}
|
| 861 |
-
|
| 862 |
-
const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
|
| 863 |
method: 'POST',
|
| 864 |
headers: {
|
| 865 |
"accept": "text/event-stream",
|
| 866 |
"baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
|
| 867 |
"content-type": "text/plain;charset=UTF-8",
|
| 868 |
"Connection": "keep-alive",
|
| 869 |
-
|
| 870 |
},
|
| 871 |
body: JSON.stringify(requestPayload)
|
| 872 |
});
|
|
@@ -882,7 +943,21 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
|
|
| 882 |
} catch (error) {
|
| 883 |
Logger.error(error, 'Server');
|
| 884 |
if (isTempCookie) {
|
| 885 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 886 |
} else {
|
| 887 |
tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
|
| 888 |
for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
|
|
@@ -898,8 +973,22 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
|
|
| 898 |
} else {
|
| 899 |
if (response.status === 429) {
|
| 900 |
if (isTempCookie) {
|
| 901 |
-
|
| 902 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 903 |
tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
|
| 904 |
for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
|
| 905 |
CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
|
|
@@ -910,10 +999,24 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
|
|
| 910 |
}
|
| 911 |
}
|
| 912 |
}
|
| 913 |
-
} else {
|
| 914 |
// 非429错误直接抛出
|
| 915 |
if (isTempCookie) {
|
| 916 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 917 |
} else {
|
| 918 |
Logger.error(`令牌异常错误状态!status: ${response.status}, 已移除当前令牌${CONFIG.SSO_INDEX.cookie}`, 'Server');
|
| 919 |
tokenManager.removeTokenByIndex(CONFIG.SSO_INDEX);
|
|
|
|
| 23 |
"grok-3-reasoning": "grok-3"
|
| 24 |
},
|
| 25 |
API: {
|
| 26 |
+
IS_TEMP_GROK2: process.env.IS_TEMP_GROK2 == undefined ? true : process.env.IS_TEMP_GROK2 == 'true',
|
| 27 |
+
GROK2_CONCURRENCY_LEVEL: process.env.GROK2_CONCURRENCY_LEVEL || 4,
|
| 28 |
+
IS_CUSTOM_SSO: process.env.IS_CUSTOM_SSO == undefined ? false : process.env.IS_CUSTOM_SSO == 'true',
|
| 29 |
BASE_URL: "https://grok.com",
|
| 30 |
API_KEY: process.env.API_KEY || "sk-123456",
|
| 31 |
SIGNATURE_COOKIE: null,
|
|
|
|
| 45 |
IS_IMG_GEN: false,
|
| 46 |
IS_IMG_GEN2: false,
|
| 47 |
SSO_INDEX: 0,//sso的索引
|
| 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': '*/*',
|
|
|
|
| 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(',');
|
|
|
|
| 91 |
tokenManager.addToken(`sso-rw=${sso};sso=${sso}`);
|
| 92 |
});
|
| 93 |
console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
|
| 94 |
+
Logger.info(`令牌加载完成,共加载: ${tokenManager.getTokenCount()}个令牌`, 'Server');
|
| 95 |
+
if (CONFIG.API.IS_TEMP_GROK2) {
|
| 96 |
+
await tempCookieManager.ensureCookies();
|
| 97 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 98 |
+
}
|
| 99 |
Logger.info("初始化完成", 'Server');
|
| 100 |
}
|
| 101 |
|
|
|
|
| 105 |
this.activeTokens = [];
|
| 106 |
this.expiredTokens = new Map();
|
| 107 |
this.tokenModelFrequency = new Map();
|
| 108 |
+
if (CONFIG.API.IS_TEMP_GROK2) {
|
| 109 |
+
this.modelRateLimit = {
|
| 110 |
+
"grok-3": { RequestFrequency: 20 },
|
| 111 |
+
"grok-3-deepsearch": { RequestFrequency: 10 },
|
| 112 |
+
"grok-3-reasoning": { RequestFrequency: 10 }
|
| 113 |
+
};
|
| 114 |
+
this.modelInitFrequency = {
|
| 115 |
+
"grok-3": 0,
|
| 116 |
+
"grok-3-deepsearch": 0,
|
| 117 |
+
"grok-3-reasoning": 0
|
| 118 |
+
};
|
| 119 |
+
} else {
|
| 120 |
+
this.modelRateLimit = {
|
| 121 |
+
"grok-2": { RequestFrequency: 20 },
|
| 122 |
+
"grok-3": { RequestFrequency: 20 },
|
| 123 |
+
"grok-3-deepsearch": { RequestFrequency: 5 },
|
| 124 |
+
"grok-3-reasoning": { RequestFrequency: 5 }
|
| 125 |
+
};
|
| 126 |
+
this.modelInitFrequency = {
|
| 127 |
+
"grok-2": 0,
|
| 128 |
+
"grok-3": 0,
|
| 129 |
+
"grok-3-deepsearch": 0,
|
| 130 |
+
"grok-3-reasoning": 0
|
| 131 |
+
};
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
}
|
| 135 |
|
| 136 |
addToken(token) {
|
| 137 |
if (!this.activeTokens.includes(token)) {
|
| 138 |
this.activeTokens.push(token);
|
| 139 |
+
this.tokenModelFrequency.set(token, this.modelInitFrequency);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
}
|
| 141 |
}
|
| 142 |
setToken(token) {
|
| 143 |
this.activeTokens = [token];
|
| 144 |
+
this.tokenModelFrequency.set(token, this.modelInitFrequency);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
}
|
| 146 |
|
| 147 |
getTokenByIndex(index, model) {
|
|
|
|
| 154 |
}
|
| 155 |
|
| 156 |
recordModelRequest(token, model) {
|
| 157 |
+
if (model.startsWith('grok-') && (model.includes('search') || model.includes('imageGen'))) {
|
| 158 |
+
model = model.split('-')[0] + '-' + model.split('-')[1];
|
| 159 |
}
|
| 160 |
|
| 161 |
if (!this.modelRateLimit[model]) return;
|
|
|
|
| 166 |
this.checkAndRemoveTokenIfLimitReached(token);
|
| 167 |
}
|
| 168 |
setModelLimit(index, model) {
|
| 169 |
+
if (model.startsWith('grok-') && (model.includes('search') || model.includes('imageGen'))) {
|
| 170 |
+
model = model.split('-')[0] + '-' + model.split('-')[1];
|
| 171 |
}
|
| 172 |
if (!this.modelRateLimit[model]) return;
|
| 173 |
const tokenFrequency = this.tokenModelFrequency.get(this.activeTokens[index]);
|
| 174 |
tokenFrequency[model] = 9999;
|
| 175 |
}
|
| 176 |
isTokenModelLimitReached(index, model) {
|
| 177 |
+
if (model.startsWith('grok-') && (model.includes('search') || model.includes('imageGen'))) {
|
| 178 |
+
model = model.split('-')[0] + '-' + model.split('-')[1];
|
| 179 |
}
|
| 180 |
if (!this.modelRateLimit[model]) return;
|
| 181 |
const token = this.activeTokens[index];
|
|
|
|
| 219 |
const now = Date.now();
|
| 220 |
for (const [token, expiredTime] of this.expiredTokens.entries()) {
|
| 221 |
if (now - expiredTime >= 2 * 60 * 60 * 1000) {
|
| 222 |
+
this.tokenModelFrequency.set(token, this.modelInitFrequency);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
this.activeTokens.push(token);
|
| 224 |
this.expiredTokens.delete(token);
|
| 225 |
Logger.info(`令牌${token}已恢复,已添加到可用令牌列表`, 'TokenManager');
|
|
|
|
| 238 |
}
|
| 239 |
|
| 240 |
class Utils {
|
| 241 |
+
static delay(time) {
|
| 242 |
+
return new Promise(function (resolve) {
|
| 243 |
+
setTimeout(resolve, time)
|
| 244 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
}
|
| 246 |
static async organizeSearchResults(searchResults) {
|
| 247 |
// 确保传入的是有效的搜索结果对象
|
|
|
|
| 261 |
return formattedResults.join('\n\n');
|
| 262 |
}
|
| 263 |
static async createAuthHeaders(model) {
|
| 264 |
+
return await tokenManager.getTokenByIndex(CONFIG.SSO_INDEX, model);
|
|
|
|
|
|
|
| 265 |
}
|
| 266 |
}
|
| 267 |
+
class GrokTempCookieManager {
|
| 268 |
+
constructor() {
|
| 269 |
+
this.cookies = [];
|
| 270 |
+
this.currentIndex = 0;
|
| 271 |
+
this.isRefreshing = false;
|
| 272 |
+
this.initialCookieCount = CONFIG.API.GROK2_CONCURRENCY_LEVEL;
|
| 273 |
+
this.extractCount = 0;
|
| 274 |
+
}
|
| 275 |
|
| 276 |
+
async ensureCookies() {
|
| 277 |
+
// 如果 cookies 数量不足,则重新获取
|
| 278 |
+
if (this.cookies.length < this.initialCookieCount) {
|
| 279 |
+
await this.refreshCookies();
|
| 280 |
+
}
|
| 281 |
+
}
|
| 282 |
+
async extractGrokHeaders(browser) {
|
| 283 |
+
Logger.info("开始提取头信息", 'Server');
|
| 284 |
+
try {
|
| 285 |
+
const page = await browser.newPage();
|
| 286 |
+
await page.goto('https://grok.com/', { waitUntil: 'domcontentloaded' });
|
| 287 |
+
let waitTime = 0;
|
| 288 |
+
const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
|
| 289 |
+
|
| 290 |
+
while (true) {
|
| 291 |
+
const cookies = await page.cookies();
|
| 292 |
+
const extractedHeaders = cookies
|
| 293 |
+
.filter(cookie => targetHeaders.includes(cookie.name.toLowerCase()))
|
| 294 |
+
.map(cookie => `${cookie.name}=${cookie.value}`);
|
| 295 |
+
|
| 296 |
+
if (targetHeaders.every(header =>
|
| 297 |
+
extractedHeaders.some(cookie => cookie && cookie.startsWith(header + '='))
|
| 298 |
+
)) {
|
| 299 |
+
await browser.close();
|
| 300 |
+
Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
|
| 301 |
+
this.cookies.push(extractedHeaders.join(';'));
|
| 302 |
+
this.extractCount++;
|
| 303 |
+
return true;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
await Utils.delay(500);
|
| 307 |
+
waitTime += 500;
|
| 308 |
+
if (waitTime >= 10000) {
|
| 309 |
+
await browser.close();
|
| 310 |
+
return null;
|
| 311 |
+
}
|
| 312 |
+
}
|
| 313 |
+
} catch (error) {
|
| 314 |
+
Logger.error('获取头信息出错:', error, 'Server');
|
| 315 |
+
return null;
|
| 316 |
+
}
|
| 317 |
+
}
|
| 318 |
+
async initializeTempCookies(count = 1) {
|
| 319 |
+
Logger.info(`初始化 ${count} 个认证信息`, 'Server');
|
| 320 |
+
const browserOptions = {
|
| 321 |
+
headless: true,
|
| 322 |
+
args: [
|
| 323 |
+
'--no-sandbox',
|
| 324 |
+
'--disable-setuid-sandbox',
|
| 325 |
+
'--disable-dev-shm-usage',
|
| 326 |
+
'--disable-gpu'
|
| 327 |
+
],
|
| 328 |
+
executablePath: CONFIG.CHROME_PATH
|
| 329 |
+
};
|
| 330 |
+
|
| 331 |
+
const browsers = await Promise.all(
|
| 332 |
+
Array.from({ length: count }, () => puppeteer.launch(browserOptions))
|
| 333 |
+
);
|
| 334 |
+
|
| 335 |
+
const cookiePromises = browsers.map(browser => this.extractGrokHeaders(browser));
|
| 336 |
+
return Promise.all(cookiePromises);
|
| 337 |
+
}
|
| 338 |
+
async refreshCookies() {
|
| 339 |
+
if (this.isRefreshing) return;
|
| 340 |
+
this.isRefreshing = true;
|
| 341 |
+
this.extractCount = 0;
|
| 342 |
+
try {
|
| 343 |
+
// 获取新的 cookies
|
| 344 |
+
let retryCount = 0;
|
| 345 |
+
let remainingCount = this.initialCookieCount - this.cookies.length;
|
| 346 |
+
|
| 347 |
+
while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
|
| 348 |
+
await this.initializeTempCookies(remainingCount);
|
| 349 |
+
if (this.extractCount != remainingCount) {
|
| 350 |
+
if (this.extractCount == 0) {
|
| 351 |
+
Logger.error(`无法获取足够的有效 TempCookies,可能网络存在问题,当前数量:${this.cookies.length}`);
|
| 352 |
+
} else if (this.extractCount < remainingCount) {
|
| 353 |
+
remainingCount -= this.extractCount;
|
| 354 |
+
this.extractCount = 0;
|
| 355 |
+
retryCount++;
|
| 356 |
+
await Utils.delay(1000 * retryCount);
|
| 357 |
+
} else {
|
| 358 |
+
break;
|
| 359 |
+
}
|
| 360 |
+
} else {
|
| 361 |
+
break;
|
| 362 |
+
}
|
| 363 |
+
}
|
| 364 |
+
if (this.currentIndex >= this.cookies.length) {
|
| 365 |
+
this.currentIndex = 0;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
if (this.cookies.length < this.initialCookieCount) {
|
| 369 |
+
if (this.cookies.length !== 0) {
|
| 370 |
+
// 如果已经获取到一些 TempCookies,则只提示警告错误
|
| 371 |
+
Logger.error(`无法获取足够的有效 TempCookies,可能网络存在问题,当前数量:${this.cookies.length}`);
|
| 372 |
+
} else {
|
| 373 |
+
// 如果未获取到任何 TempCookies,则抛出错误
|
| 374 |
+
throw new Error(`无法获取足够的有效 TempCookies,可能网络存在问题,当前数量:${this.cookies.length}`);
|
| 375 |
+
}
|
| 376 |
+
}
|
| 377 |
+
} catch (error) {
|
| 378 |
+
Logger.error('刷新 cookies 失败:', error);
|
| 379 |
+
} finally {
|
| 380 |
+
Logger.info(`已提取${this.cookies.length}个TempCookies`, 'Server');
|
| 381 |
+
Logger.info(`提取的TempCookies为${JSON.stringify(this.cookies, null, 2)}`, 'Server');
|
| 382 |
+
this.isRefreshing = false;
|
| 383 |
+
}
|
| 384 |
+
}
|
| 385 |
+
}
|
| 386 |
|
| 387 |
class GrokApiClient {
|
| 388 |
constructor(modelId) {
|
|
|
|
| 438 |
method: 'POST',
|
| 439 |
headers: {
|
| 440 |
...CONFIG.DEFAULT_HEADERS,
|
| 441 |
+
"cookie": CONFIG.API.SIGNATURE_COOKIE
|
| 442 |
},
|
| 443 |
body: JSON.stringify(uploadData)
|
| 444 |
});
|
|
|
|
| 460 |
|
| 461 |
async prepareChatRequest(request) {
|
| 462 |
if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY && request.stream) {
|
| 463 |
+
throw new Error(`该模型流式输出需要配置PICGO或者TUMY图床密钥!`);
|
| 464 |
}
|
| 465 |
|
| 466 |
// 处理画图模型的消息限制
|
|
|
|
| 619 |
};
|
| 620 |
}
|
| 621 |
}
|
| 622 |
+
async function processModelResponse(response, model) {
|
| 623 |
let result = { token: null, imageUrl: null }
|
| 624 |
if (CONFIG.IS_IMG_GEN) {
|
| 625 |
+
if (response?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
|
| 626 |
+
result.imageUrl = response.cachedImageGenerationResponse.imageUrl;
|
| 627 |
}
|
| 628 |
return result;
|
| 629 |
}
|
|
|
|
| 631 |
//非生图模型的处理
|
| 632 |
switch (model) {
|
| 633 |
case 'grok-2':
|
| 634 |
+
result.token = response?.token;
|
| 635 |
return result;
|
| 636 |
case 'grok-2-search':
|
| 637 |
case 'grok-3-search':
|
| 638 |
+
if (response?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
|
| 639 |
+
result.token = `\r\n<think>${await Utils.organizeSearchResults(response.webSearchResults)}</think>\r\n`;
|
| 640 |
} else {
|
| 641 |
+
result.token = response?.token;
|
| 642 |
}
|
| 643 |
return result;
|
| 644 |
case 'grok-3':
|
| 645 |
+
result.token = response?.token;
|
| 646 |
return result;
|
| 647 |
case 'grok-3-deepsearch':
|
| 648 |
+
if (response?.messageTag === "final") {
|
| 649 |
+
result.token = response?.token;
|
| 650 |
}
|
| 651 |
return result;
|
| 652 |
case 'grok-3-reasoning':
|
| 653 |
+
if (response?.isThinking && !CONFIG.SHOW_THINKING) return result;
|
| 654 |
|
| 655 |
+
if (response?.isThinking && !CONFIG.IS_THINKING) {
|
| 656 |
+
result.token = "<think>" + response?.token;
|
| 657 |
CONFIG.IS_THINKING = true;
|
| 658 |
+
} else if (!response.isThinking && CONFIG.IS_THINKING) {
|
| 659 |
+
result.token = "</think>" + response?.token;
|
| 660 |
CONFIG.IS_THINKING = false;
|
| 661 |
} else {
|
| 662 |
+
result.token = response?.token;
|
| 663 |
}
|
| 664 |
return result;
|
| 665 |
}
|
|
|
|
| 677 |
res.setHeader('Cache-Control', 'no-cache');
|
| 678 |
res.setHeader('Connection', 'keep-alive');
|
| 679 |
}
|
| 680 |
+
CONFIG.IS_THINKING = false;
|
| 681 |
+
CONFIG.IS_IMG_GEN = false;
|
| 682 |
+
CONFIG.IS_IMG_GEN2 = false;
|
| 683 |
Logger.info("开始处理流式响应", 'Server');
|
| 684 |
|
| 685 |
return new Promise((resolve, reject) => {
|
|
|
|
| 690 |
|
| 691 |
for (const line of lines) {
|
| 692 |
if (!line.trim()) continue;
|
| 693 |
+
try {
|
| 694 |
+
const linejosn = JSON.parse(line.trim());
|
| 695 |
+
if (linejosn?.error) {
|
| 696 |
+
Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
|
| 697 |
+
if (linejosn.error?.name === "RateLimitError") {
|
| 698 |
+
CONFIG.API.TEMP_COOKIE = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 699 |
}
|
| 700 |
+
stream.destroy();
|
| 701 |
+
reject(new Error("RateLimitError"));
|
| 702 |
+
return;
|
| 703 |
+
}
|
| 704 |
+
let response = linejosn?.result?.response;
|
| 705 |
+
if (!response) continue;
|
| 706 |
+
if (response?.doImgGen || response?.imageAttachmentInfo) {
|
| 707 |
+
CONFIG.IS_IMG_GEN = true;
|
| 708 |
+
}
|
| 709 |
+
const processPromise = (async () => {
|
| 710 |
+
const result = await processModelResponse(response, model);
|
| 711 |
+
|
| 712 |
+
if (result.token) {
|
| 713 |
+
if (isStream) {
|
| 714 |
+
res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
|
| 715 |
+
} else {
|
| 716 |
+
fullResponse += result.token;
|
| 717 |
}
|
| 718 |
+
}
|
| 719 |
+
if (result.imageUrl) {
|
| 720 |
+
CONFIG.IS_IMG_GEN2 = true;
|
| 721 |
+
const dataImage = await handleImageResponse(result.imageUrl);
|
| 722 |
+
if (isStream) {
|
| 723 |
+
res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(dataImage, model, true))}\n\n`);
|
| 724 |
+
} else {
|
| 725 |
+
res.json(MessageProcessor.createChatResponse(dataImage, model));
|
| 726 |
}
|
| 727 |
+
}
|
| 728 |
+
})();
|
| 729 |
+
dataPromises.push(processPromise);
|
| 730 |
+
} catch (error) {
|
| 731 |
+
Logger.error(error, 'Server');
|
| 732 |
+
continue;
|
| 733 |
}
|
| 734 |
}
|
| 735 |
});
|
|
|
|
| 745 |
res.json(MessageProcessor.createChatResponse(fullResponse, model));
|
| 746 |
}
|
| 747 |
}
|
|
|
|
|
|
|
| 748 |
resolve();
|
| 749 |
} catch (error) {
|
| 750 |
Logger.error(error, 'Server');
|
|
|
|
| 759 |
});
|
| 760 |
} catch (error) {
|
| 761 |
Logger.error(error, 'Server');
|
|
|
|
|
|
|
| 762 |
throw new Error(error);
|
| 763 |
}
|
| 764 |
}
|
|
|
|
| 774 |
method: 'GET',
|
| 775 |
headers: {
|
| 776 |
...DEFAULT_HEADERS,
|
| 777 |
+
"cookie": CONFIG.API.SIGNATURE_COOKIE
|
| 778 |
}
|
| 779 |
});
|
| 780 |
|
|
|
|
| 805 |
return ``
|
| 806 |
}
|
| 807 |
|
| 808 |
+
Logger.info("开始上传图床", 'Server');
|
| 809 |
const formData = new FormData();
|
| 810 |
+
if (CONFIG.API.PICGO_KEY) {
|
| 811 |
formData.append('source', imageBuffer, {
|
| 812 |
filename: `image-${Date.now()}.jpg`,
|
| 813 |
contentType: 'image/jpeg'
|
|
|
|
| 816 |
const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
|
| 817 |
method: "POST",
|
| 818 |
headers: {
|
| 819 |
+
...formDataHeaders,
|
| 820 |
+
"Content-Type": "multipart/form-data",
|
| 821 |
+
"X-API-Key": CONFIG.API.PICGO_KEY
|
| 822 |
+
},
|
| 823 |
+
body: formData
|
| 824 |
});
|
| 825 |
if (!responseURL.ok) {
|
| 826 |
return "生图失败,请查看PICGO图床密钥是否设置正确"
|
|
|
|
| 829 |
const result = await responseURL.json();
|
| 830 |
return ``
|
| 831 |
}
|
| 832 |
+
} else if (CONFIG.API.TUMY_KEY) {
|
| 833 |
const formData = new FormData();
|
| 834 |
formData.append('file', imageBuffer, {
|
| 835 |
filename: `image-${Date.now()}.jpg`,
|
|
|
|
| 848 |
if (!responseURL.ok) {
|
| 849 |
return "生图失败,请查看TUMY图床密钥是否设置正确"
|
| 850 |
} else {
|
|
|
|
|
|
|
| 851 |
try {
|
| 852 |
+
const result = await responseURL.json();
|
| 853 |
+
Logger.info("生图成功", 'Server');
|
| 854 |
+
return ``
|
| 855 |
} catch (error) {
|
| 856 |
+
Logger.error(error, 'Server');
|
| 857 |
+
return "生图失败,请查看TUMY图床密钥是否设置正确"
|
| 858 |
}
|
| 859 |
}
|
| 860 |
}
|
| 861 |
}
|
| 862 |
|
| 863 |
const tokenManager = new AuthTokenManager();
|
| 864 |
+
const tempCookieManager = new GrokTempCookieManager();
|
| 865 |
await initialization();
|
| 866 |
|
| 867 |
// 中间件配置
|
|
|
|
| 901 |
} else if (authToken !== CONFIG.API.API_KEY) {
|
| 902 |
return res.status(401).json({ error: 'Unauthorized' });
|
| 903 |
}
|
| 904 |
+
let isTempCookie = req.body.model.includes("grok-2") && CONFIG.API.IS_TEMP_GROK2;
|
| 905 |
let retryCount = 0;
|
| 906 |
const grokClient = new GrokApiClient(req.body.model);
|
| 907 |
const requestPayload = await grokClient.prepareChatRequest(req.body);
|
| 908 |
+
Logger.info(`请求体: ${JSON.stringify(requestPayload, null, 2)}`, 'Server');
|
| 909 |
|
| 910 |
while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
|
| 911 |
retryCount++;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 912 |
if (isTempCookie) {
|
| 913 |
CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
|
| 914 |
Logger.info(`已切换为临时令牌`, 'Server');
|
|
|
|
| 919 |
throw new Error('该模型无可用令牌');
|
| 920 |
}
|
| 921 |
Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
|
| 922 |
+
Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE, null, 2)}`, 'Server');
|
| 923 |
+
const response = await fetch(`${CONFIG.API.BASE_URL}/rest/app-chat/conversations/new`, {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 924 |
method: 'POST',
|
| 925 |
headers: {
|
| 926 |
"accept": "text/event-stream",
|
| 927 |
"baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
|
| 928 |
"content-type": "text/plain;charset=UTF-8",
|
| 929 |
"Connection": "keep-alive",
|
| 930 |
+
"cookie": CONFIG.API.SIGNATURE_COOKIE
|
| 931 |
},
|
| 932 |
body: JSON.stringify(requestPayload)
|
| 933 |
});
|
|
|
|
| 943 |
} catch (error) {
|
| 944 |
Logger.error(error, 'Server');
|
| 945 |
if (isTempCookie) {
|
| 946 |
+
// 移除当前失效的 cookie
|
| 947 |
+
tempCookieManager.cookies.splice(tempCookieManager.currentIndex, 1);
|
| 948 |
+
if (tempCookieManager.cookies.length != 0) {
|
| 949 |
+
tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
|
| 950 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 951 |
+
tempCookieManager.ensureCookies()
|
| 952 |
+
} else {
|
| 953 |
+
try {
|
| 954 |
+
await tempCookieManager.ensureCookies();
|
| 955 |
+
tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
|
| 956 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 957 |
+
} catch (error) {
|
| 958 |
+
throw error;
|
| 959 |
+
}
|
| 960 |
+
}
|
| 961 |
} else {
|
| 962 |
tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
|
| 963 |
for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
|
|
|
|
| 973 |
} else {
|
| 974 |
if (response.status === 429) {
|
| 975 |
if (isTempCookie) {
|
| 976 |
+
// 移除当前失效的 cookie
|
| 977 |
+
tempCookieManager.cookies.splice(tempCookieManager.currentIndex, 1);
|
| 978 |
+
if (tempCookieManager.cookies.length != 0) {
|
| 979 |
+
tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
|
| 980 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 981 |
+
tempCookieManager.ensureCookies()
|
| 982 |
+
} else {
|
| 983 |
+
try {
|
| 984 |
+
await tempCookieManager.ensureCookies();
|
| 985 |
+
tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
|
| 986 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 987 |
+
} catch (error) {
|
| 988 |
+
throw error;
|
| 989 |
+
}
|
| 990 |
+
}
|
| 991 |
+
} else {
|
| 992 |
tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
|
| 993 |
for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
|
| 994 |
CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
|
|
|
|
| 999 |
}
|
| 1000 |
}
|
| 1001 |
}
|
| 1002 |
+
} else {
|
| 1003 |
// 非429错误直接抛出
|
| 1004 |
if (isTempCookie) {
|
| 1005 |
+
// 移除当前失效的 cookie
|
| 1006 |
+
tempCookieManager.cookies.splice(tempCookieManager.currentIndex, 1);
|
| 1007 |
+
if (tempCookieManager.cookies.length != 0) {
|
| 1008 |
+
tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
|
| 1009 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 1010 |
+
tempCookieManager.ensureCookies()
|
| 1011 |
+
} else {
|
| 1012 |
+
try {
|
| 1013 |
+
await tempCookieManager.ensureCookies();
|
| 1014 |
+
tempCookieManager.currentIndex = tempCookieManager.currentIndex % tempCookieManager.cookies.length;
|
| 1015 |
+
CONFIG.API.TEMP_COOKIE = tempCookieManager.cookies[tempCookieManager.currentIndex];
|
| 1016 |
+
} catch (error) {
|
| 1017 |
+
throw error;
|
| 1018 |
+
}
|
| 1019 |
+
}
|
| 1020 |
} else {
|
| 1021 |
Logger.error(`令牌异常错误状态!status: ${response.status}, 已移除当前令牌${CONFIG.SSO_INDEX.cookie}`, 'Server');
|
| 1022 |
tokenManager.removeTokenByIndex(CONFIG.SSO_INDEX);
|