fix browser verification
This commit is contained in:
@@ -0,0 +1,204 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import {fileURLToPath} from "url";
|
||||||
|
import {Mutex} from "async-mutex";
|
||||||
|
|
||||||
|
const configMutex = new Mutex(); // 互斥锁
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const CONFIG_FILE_PATH = path.join(__dirname, "../config.mjs");
|
||||||
|
|
||||||
|
// 仅在 USE_MANUAL_LOGIN 为 false 且 ENABLE_AUTO_COOKIE_UPDATE 为 true 时生效
|
||||||
|
const ENABLE_AUTO_COOKIE_UPDATE = process.env.ENABLE_AUTO_COOKIE_UPDATE === "true";
|
||||||
|
|
||||||
|
function unifyQuotesForJSON(str) {
|
||||||
|
// 正则匹配 `` `...` ``
|
||||||
|
let out = str.replace(/`([^`]*)`/g, (match, p1) => {
|
||||||
|
const safe = p1.replace(/"/g, '\\"');
|
||||||
|
return `"${safe}"`;
|
||||||
|
});
|
||||||
|
out = out.replace(/'([^']*)'/g, (match, p1) => {
|
||||||
|
const safe = p1.replace(/"/g, '\\"');
|
||||||
|
return `"${safe}"`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cookies 解析出 DS 与 DSR
|
||||||
|
* @param {Array} cookies 获取到的 cookie 数组
|
||||||
|
* @returns {{ ds?: string, dsr?: string }}
|
||||||
|
*/
|
||||||
|
function parseDSAndDSR(cookies) {
|
||||||
|
let dsValue, dsrValue;
|
||||||
|
for (const c of cookies) {
|
||||||
|
if (c.name === "DS") {
|
||||||
|
dsValue = c.value;
|
||||||
|
} else if (c.name === "DSR") {
|
||||||
|
dsrValue = c.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {ds: dsValue, dsr: dsrValue};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 DS 中解析 email 字段
|
||||||
|
* @param {string} dsToken DS cookie
|
||||||
|
* @returns {string|null} 返回 email或null
|
||||||
|
*/
|
||||||
|
function decodeEmailFromDs(dsToken) {
|
||||||
|
try {
|
||||||
|
const parts = dsToken.split(".");
|
||||||
|
if (parts.length < 2) return null;
|
||||||
|
const payload = JSON.parse(Buffer.from(parts[1], "base64").toString("utf-8"));
|
||||||
|
return payload.email || null;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cookie 数组转换 "name=value; name=value"
|
||||||
|
* @param {Array} cookies
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function cookiesToStringAll(cookies) {
|
||||||
|
return cookies.map(c => `${c.name}=${c.value}`).join("; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cookie 转换数组
|
||||||
|
* 每个对象如 { name, value }
|
||||||
|
* @param {string} cookieStr
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function parseCookieString(cookieStr) {
|
||||||
|
return cookieStr.split("; ").map(entry => {
|
||||||
|
const [name, value] = entry.split("=", 2);
|
||||||
|
return {name, value};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地 configObj.sessions 查找与指定 email 匹配的 session,
|
||||||
|
* @param {object} configObj 解析后 config
|
||||||
|
* @param {string} email 匹配的邮箱
|
||||||
|
* @returns {{ index: number, oldCookie: string, ds: string, dsr: string } | null}
|
||||||
|
*/
|
||||||
|
function findSessionByEmail(configObj, email) {
|
||||||
|
if (!Array.isArray(configObj.sessions)) return null;
|
||||||
|
for (let i = 0; i < configObj.sessions.length; i++) {
|
||||||
|
const cookieStr = configObj.sessions[i].cookie || "";
|
||||||
|
const dsMatch = /DS=([^;\s]+)/.exec(cookieStr);
|
||||||
|
if (!dsMatch) continue;
|
||||||
|
const dsValue = dsMatch[1];
|
||||||
|
const dsEmail = decodeEmailFromDs(dsValue);
|
||||||
|
if (dsEmail && dsEmail.toLowerCase() === email.toLowerCase()) {
|
||||||
|
const dsrMatch = /DSR=([^;\s]+)/.exec(cookieStr);
|
||||||
|
const dsrValue = dsrMatch ? dsrMatch[1] : "";
|
||||||
|
return {
|
||||||
|
index: i,
|
||||||
|
oldCookie: cookieStr,
|
||||||
|
ds: dsValue,
|
||||||
|
dsr: dsrValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* config.mjs 中匹配相同 email 的 session,若 DS 或 DSR 有变化,则更新整个 cookie
|
||||||
|
* @param {import('puppeteer-core').Page} page
|
||||||
|
*/
|
||||||
|
export async function updateLocalConfigCookieByEmail(page) {
|
||||||
|
if (!ENABLE_AUTO_COOKIE_UPDATE || process.env.USE_MANUAL_LOGIN === "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 尝试从 “https://you.com/api/instrumentation” 获取 cookie
|
||||||
|
let cookieStringFromInstrumentation = "";
|
||||||
|
try {
|
||||||
|
const instrRequest = await page.waitForRequest(
|
||||||
|
req => req.url().includes("/api/instrumentation"),
|
||||||
|
{timeout: 5000}
|
||||||
|
);
|
||||||
|
if (instrRequest) {
|
||||||
|
cookieStringFromInstrumentation = instrRequest.headers()["cookie"];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
}
|
||||||
|
|
||||||
|
let allCookiesString = "";
|
||||||
|
if (cookieStringFromInstrumentation) {
|
||||||
|
allCookiesString = cookieStringFromInstrumentation;
|
||||||
|
} else {
|
||||||
|
// 使用 page.cookies() 获取
|
||||||
|
const cookies = await page.cookies("https://you.com");
|
||||||
|
allCookiesString = cookiesToStringAll(cookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookieArray = parseCookieString(allCookiesString);
|
||||||
|
const {ds: newDs, dsr: newDsr} = parseDSAndDSR(cookieArray);
|
||||||
|
if (!newDs) {
|
||||||
|
console.log("网页未找到 DS,跳过更新。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newEmail = decodeEmailFromDs(newDs);
|
||||||
|
if (!newEmail) {
|
||||||
|
console.log("[网页无法从 DS 解出 email,跳过更新。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 互斥区
|
||||||
|
await configMutex.runExclusive(async () => {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(CONFIG_FILE_PATH)) {
|
||||||
|
console.warn(`找不到 config.mjs: ${CONFIG_FILE_PATH}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const raw = fs.readFileSync(CONFIG_FILE_PATH, "utf8");
|
||||||
|
// 去掉 export const config =
|
||||||
|
let jsonString = raw.replace(/^export const config\s*=\s*/, "").trim();
|
||||||
|
|
||||||
|
jsonString = unifyQuotesForJSON(jsonString);
|
||||||
|
|
||||||
|
const configObj = JSON.parse(jsonString);
|
||||||
|
|
||||||
|
const found = findSessionByEmail(configObj, newEmail);
|
||||||
|
if (!found) {
|
||||||
|
console.log(`未能在 config 中找到 email=${newEmail} 的 session,跳过更新。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found.ds === newDs && found.dsr === newDsr) {
|
||||||
|
console.log(`DS/DSR 未变化(email=${newEmail}),不更新。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
configObj.sessions[found.index].cookie = allCookiesString;
|
||||||
|
|
||||||
|
const newFileContent = "export const config = " + JSON.stringify(configObj, null, 4);
|
||||||
|
fs.writeFileSync(CONFIG_FILE_PATH, newFileContent, "utf8");
|
||||||
|
|
||||||
|
console.log(`Cookie已更新(email=${newEmail})`);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Cookie更新过程出错:", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非阻塞
|
||||||
|
* @param {import('puppeteer-core').Page} page
|
||||||
|
*/
|
||||||
|
export function updateLocalConfigCookieByEmailNonBlocking(page) {
|
||||||
|
// 保证异步
|
||||||
|
setImmediate(() => {
|
||||||
|
updateLocalConfigCookieByEmail(page).catch(err =>
|
||||||
|
console.error("Cookie update error:", err)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
+225
-179
@@ -3,7 +3,7 @@ import {v4 as uuidV4} from "uuid";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import {fileURLToPath} from "url";
|
import {fileURLToPath} from "url";
|
||||||
import {createDocx, extractCookie, getSessionCookie, sleep} from "../utils.mjs";
|
import {createDocx, extractCookie, getSessionCookie, sleep} from "../utils/cookieUtils.mjs";
|
||||||
import {exec} from 'child_process';
|
import {exec} from 'child_process';
|
||||||
import '../proxyAgent.mjs';
|
import '../proxyAgent.mjs';
|
||||||
import {formatMessages} from '../formatMessages.mjs';
|
import {formatMessages} from '../formatMessages.mjs';
|
||||||
@@ -109,14 +109,30 @@ class YouProvider {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 使用配置文件中的 cookie
|
// 使用配置文件中的 cookie
|
||||||
|
// 检查 invalid_accounts 字段
|
||||||
|
const invalidAccounts = config.invalid_accounts || {};
|
||||||
|
|
||||||
for (let index = 0; index < config.sessions.length; index++) {
|
for (let index = 0; index < config.sessions.length; index++) {
|
||||||
const session = config.sessions[index];
|
const session = config.sessions[index];
|
||||||
const {jwtSession, jwtToken, ds, dsr, you_subscription, youpro_subscription} = extractCookie(session.cookie);
|
const {
|
||||||
|
jwtSession,
|
||||||
|
jwtToken,
|
||||||
|
ds,
|
||||||
|
dsr,
|
||||||
|
you_subscription,
|
||||||
|
youpro_subscription
|
||||||
|
} = extractCookie(session.cookie);
|
||||||
if (jwtSession && jwtToken) {
|
if (jwtSession && jwtToken) {
|
||||||
// 旧版cookie处理
|
// 旧版cookie处理
|
||||||
try {
|
try {
|
||||||
const jwt = JSON.parse(Buffer.from(jwtToken.split(".")[1], "base64").toString());
|
const jwt = JSON.parse(Buffer.from(jwtToken.split(".")[1], "base64").toString());
|
||||||
const username = jwt.user.name;
|
const username = jwt.user.name;
|
||||||
|
|
||||||
|
if (invalidAccounts[username]) {
|
||||||
|
console.log(`跳过标记失效账号 #${index} ${username} (${invalidAccounts[username]})`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.sessions[username] = {
|
this.sessions[username] = {
|
||||||
configIndex: index,
|
configIndex: index,
|
||||||
jwtSession,
|
jwtSession,
|
||||||
@@ -137,6 +153,12 @@ class YouProvider {
|
|||||||
try {
|
try {
|
||||||
const jwt = JSON.parse(Buffer.from(ds.split(".")[1], "base64").toString());
|
const jwt = JSON.parse(Buffer.from(ds.split(".")[1], "base64").toString());
|
||||||
const username = jwt.email;
|
const username = jwt.email;
|
||||||
|
|
||||||
|
if (invalidAccounts[username]) {
|
||||||
|
console.log(`跳过标记失效账号 #${index} ${username} (${invalidAccounts[username]})`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.sessions[username] = {
|
this.sessions[username] = {
|
||||||
configIndex: index,
|
configIndex: index,
|
||||||
ds,
|
ds,
|
||||||
@@ -371,12 +393,18 @@ class YouProvider {
|
|||||||
console.log(`${currentUsername} 无有效订阅`);
|
console.log(`${currentUsername} 无有效订阅`);
|
||||||
console.warn(`警告: ${currentUsername} 可能没有有效的订阅。请检查You是否有有效的Pro或Team订阅。`);
|
console.warn(`警告: ${currentUsername} 可能没有有效的订阅。请检查You是否有有效的Pro或Team订阅。`);
|
||||||
session.valid = false;
|
session.valid = false;
|
||||||
|
|
||||||
|
// 标记为失效
|
||||||
|
await markAccountAsInvalid(currentUsername, this.config);
|
||||||
}
|
}
|
||||||
} catch (parseErr) {
|
} catch (parseErr) {
|
||||||
console.log(`${currentUsername} 已失效 (fetchYouProState 异常)`);
|
console.log(`${currentUsername} 已失效 (fetchYouProState 异常)`);
|
||||||
console.warn(`警告: ${currentUsername} 验证失败。请检查cookie是否有效。`);
|
console.warn(`警告: ${currentUsername} 验证失败。请检查cookie是否有效。`);
|
||||||
console.error(parseErr);
|
console.error(parseErr);
|
||||||
session.valid = false;
|
session.valid = false;
|
||||||
|
|
||||||
|
// 标记为失效
|
||||||
|
await markAccountAsInvalid(currentUsername, this.config);
|
||||||
}
|
}
|
||||||
} catch (errorVisit) {
|
} catch (errorVisit) {
|
||||||
console.error(`验证账户 ${currentUsername} 时出错:`, errorVisit);
|
console.error(`验证账户 ${currentUsername} 时出错:`, errorVisit);
|
||||||
@@ -1135,182 +1163,6 @@ class YouProvider {
|
|||||||
let errorCount = 0; // 错误计数器
|
let errorCount = 0; // 错误计数器
|
||||||
const ERROR_TIMEOUT = (proxyModel === "openai_o1" || proxyModel === "openai_o1_preview") ? 60000 : 20000; // 错误超时时间
|
const ERROR_TIMEOUT = (proxyModel === "openai_o1" || proxyModel === "openai_o1_preview") ? 60000 : 20000; // 错误超时时间
|
||||||
const self = this;
|
const self = this;
|
||||||
page.exposeFunction("callback" + traceId, async (event, data) => {
|
|
||||||
if (isEnding) return;
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case "youChatToken": {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
let tokenContent = data.youChatToken;
|
|
||||||
buffer += tokenContent;
|
|
||||||
|
|
||||||
if (buffer.endsWith('\\') && !buffer.endsWith('\\\\')) {
|
|
||||||
// 等待下一个字符
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let processedContent = unescapeContent(buffer);
|
|
||||||
buffer = '';
|
|
||||||
|
|
||||||
if (!responseStarted) {
|
|
||||||
responseStarted = true;
|
|
||||||
|
|
||||||
startTime = Date.now();
|
|
||||||
clearTimeout(responseTimeout);
|
|
||||||
// 自定义终止符延迟触发
|
|
||||||
customEndMarkerTimer = setTimeout(() => {
|
|
||||||
customEndMarkerEnabled = true;
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
// 停止
|
|
||||||
if (heartbeatInterval) {
|
|
||||||
clearInterval(heartbeatInterval);
|
|
||||||
heartbeatInterval = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置错误计时器
|
|
||||||
if (errorTimer) {
|
|
||||||
clearTimeout(errorTimer);
|
|
||||||
errorTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测 'unusual query volume'
|
|
||||||
if (processedContent.includes('unusual query volume')) {
|
|
||||||
const warningMessage = "您在 you.com 账号的使用已达上限,当前(default/agent)模式已进入冷却期(CD)。请切换模式(default/agent[custom])或耐心等待冷却结束后再继续使用。";
|
|
||||||
emitter.emit("completion", traceId, warningMessage);
|
|
||||||
unusualQueryVolumeTriggered = true; // 更新标志位
|
|
||||||
|
|
||||||
if (self.isRotationEnabled) {
|
|
||||||
session.modeStatus[session.currentMode] = false;
|
|
||||||
self.checkAndSwitchMode();
|
|
||||||
if (Object.values(session.modeStatus).some(status => status)) {
|
|
||||||
console.log(`模式达到请求上限,已切换模式 ${session.currentMode},请重试请求。`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("检测到请求量异常提示,请求终止。");
|
|
||||||
}
|
|
||||||
isEnding = true;
|
|
||||||
// 终止
|
|
||||||
setTimeout(async () => {
|
|
||||||
await cleanup();
|
|
||||||
emitter.emit("end", traceId);
|
|
||||||
}, 1000);
|
|
||||||
self.logger.logRequest({
|
|
||||||
email: username,
|
|
||||||
time: requestTime,
|
|
||||||
mode: session.currentMode,
|
|
||||||
model: proxyModel,
|
|
||||||
completed: true,
|
|
||||||
unusualQueryVolume: true,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.stdout.write(processedContent);
|
|
||||||
accumulatedResponse += processedContent;
|
|
||||||
|
|
||||||
if (Date.now() - startTime >= 20000) {
|
|
||||||
responseAfter20Seconds += processedContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream) {
|
|
||||||
emitter.emit("completion", traceId, processedContent);
|
|
||||||
} else {
|
|
||||||
finalResponse += processedContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查自定义结束标记
|
|
||||||
if (customEndMarkerEnabled && customEndMarker && checkEndMarker(responseAfter20Seconds, customEndMarker)) {
|
|
||||||
isEnding = true;
|
|
||||||
console.log("检测到自定义终止,关闭请求");
|
|
||||||
setTimeout(async () => {
|
|
||||||
await cleanup();
|
|
||||||
emitter.emit(stream ? "end" : "completion", traceId, stream ? undefined : finalResponse);
|
|
||||||
}, 1000);
|
|
||||||
self.logger.logRequest({
|
|
||||||
email: username,
|
|
||||||
time: requestTime,
|
|
||||||
mode: session.currentMode,
|
|
||||||
model: proxyModel,
|
|
||||||
completed: true,
|
|
||||||
unusualQueryVolume: unusualQueryVolumeTriggered,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "customEndMarkerEnabled":
|
|
||||||
customEndMarkerEnabled = true;
|
|
||||||
break;
|
|
||||||
case "done":
|
|
||||||
if (isEnding) return;
|
|
||||||
console.log("请求结束");
|
|
||||||
isEnding = true;
|
|
||||||
await cleanup(); // 清理
|
|
||||||
emitter.emit(stream ? "end" : "completion", traceId, stream ? undefined : finalResponse);
|
|
||||||
self.logger.logRequest({
|
|
||||||
email: username,
|
|
||||||
time: requestTime,
|
|
||||||
mode: session.currentMode,
|
|
||||||
model: proxyModel,
|
|
||||||
completed: true,
|
|
||||||
unusualQueryVolume: unusualQueryVolumeTriggered,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "error": {
|
|
||||||
if (isEnding) return; // 已结束则忽略
|
|
||||||
|
|
||||||
console.error("请求发生错误", data);
|
|
||||||
errorCount++;
|
|
||||||
if (errorCount >= 3) {
|
|
||||||
const errorMessage = "连接中断,未收到服务器响应";
|
|
||||||
if (errorTimer) {
|
|
||||||
clearTimeout(errorTimer);
|
|
||||||
errorTimer = null;
|
|
||||||
}
|
|
||||||
isEnding = true;
|
|
||||||
finalResponse += ` (${errorMessage})`;
|
|
||||||
await cleanup();
|
|
||||||
emitter.emit("completion", traceId, errorMessage);
|
|
||||||
emitter.emit("end", traceId);
|
|
||||||
|
|
||||||
// 记录日志
|
|
||||||
self.logger.logRequest({
|
|
||||||
email: username,
|
|
||||||
time: requestTime,
|
|
||||||
mode: session.currentMode,
|
|
||||||
model: proxyModel,
|
|
||||||
completed: false,
|
|
||||||
unusualQueryVolume: unusualQueryVolumeTriggered,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (errorTimer) {
|
|
||||||
clearTimeout(errorTimer);
|
|
||||||
}
|
|
||||||
errorTimer = setTimeout(async () => {
|
|
||||||
console.log("连接超时,终止请求");
|
|
||||||
const errorMessage = "连接中断,未收到服务器响应";
|
|
||||||
|
|
||||||
emitter.emit("completion", traceId, errorMessage);
|
|
||||||
finalResponse += ` (${errorMessage})`;
|
|
||||||
|
|
||||||
isEnding = true;
|
|
||||||
await cleanup();
|
|
||||||
|
|
||||||
emitter.emit("end", traceId);
|
|
||||||
self.logger.logRequest({
|
|
||||||
email: username,
|
|
||||||
time: requestTime,
|
|
||||||
mode: session.currentMode,
|
|
||||||
model: proxyModel,
|
|
||||||
completed: false,
|
|
||||||
unusualQueryVolume: unusualQueryVolumeTriggered,
|
|
||||||
});
|
|
||||||
}, ERROR_TIMEOUT);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// proxy response
|
// proxy response
|
||||||
const req_param = new URLSearchParams();
|
const req_param = new URLSearchParams();
|
||||||
@@ -1517,7 +1369,7 @@ class YouProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseTimeoutTimer = (proxyModel === "openai_o1" || proxyModel === "openai_o1_preview" || proxyModel === "claude_3_7_sonnet_thinking")? 140000 : 60000; // 响应超时时间
|
const responseTimeoutTimer = (proxyModel === "openai_o1" || proxyModel === "openai_o1_preview" || proxyModel === "claude_3_7_sonnet_thinking") ? 140000 : 60000; // 响应超时时间
|
||||||
|
|
||||||
// 重新发送请求
|
// 重新发送请求
|
||||||
async function resendPreviousRequest() {
|
async function resendPreviousRequest() {
|
||||||
@@ -1582,6 +1434,183 @@ class YouProvider {
|
|||||||
await page.goto(`https://you.com/search?q=&fromSearchBar=true&tbm=youchat&chatMode=${userChatModeId}&cid=c0_${traceId}`, {waitUntil: "domcontentloaded"});
|
await page.goto(`https://you.com/search?q=&fromSearchBar=true&tbm=youchat&chatMode=${userChatModeId}&cid=c0_${traceId}`, {waitUntil: "domcontentloaded"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await page.exposeFunction("callback" + traceId, async (event, data) => {
|
||||||
|
if (isEnding) return;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case "youChatToken": {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
let tokenContent = data.youChatToken;
|
||||||
|
buffer += tokenContent;
|
||||||
|
|
||||||
|
if (buffer.endsWith('\\') && !buffer.endsWith('\\\\')) {
|
||||||
|
// 等待下一个字符
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let processedContent = unescapeContent(buffer);
|
||||||
|
buffer = '';
|
||||||
|
|
||||||
|
if (!responseStarted) {
|
||||||
|
responseStarted = true;
|
||||||
|
|
||||||
|
startTime = Date.now();
|
||||||
|
clearTimeout(responseTimeout);
|
||||||
|
// 自定义终止符延迟触发
|
||||||
|
customEndMarkerTimer = setTimeout(() => {
|
||||||
|
customEndMarkerEnabled = true;
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
// 停止
|
||||||
|
if (heartbeatInterval) {
|
||||||
|
clearInterval(heartbeatInterval);
|
||||||
|
heartbeatInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置错误计时器
|
||||||
|
if (errorTimer) {
|
||||||
|
clearTimeout(errorTimer);
|
||||||
|
errorTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测 'unusual query volume'
|
||||||
|
if (processedContent.includes('unusual query volume')) {
|
||||||
|
const warningMessage = "您在 you.com 账号的使用已达上限,当前(default/agent)模式已进入冷却期(CD)。请切换模式(default/agent[custom])或耐心等待冷却结束后再继续使用。";
|
||||||
|
emitter.emit("completion", traceId, warningMessage);
|
||||||
|
unusualQueryVolumeTriggered = true; // 更新标志位
|
||||||
|
|
||||||
|
if (self.isRotationEnabled) {
|
||||||
|
session.modeStatus[session.currentMode] = false;
|
||||||
|
self.checkAndSwitchMode();
|
||||||
|
if (Object.values(session.modeStatus).some(status => status)) {
|
||||||
|
console.log(`模式达到请求上限,已切换模式 ${session.currentMode},请重试请求。`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("检测到请求量异常提示,请求终止。");
|
||||||
|
}
|
||||||
|
isEnding = true;
|
||||||
|
// 终止
|
||||||
|
setTimeout(async () => {
|
||||||
|
await cleanup();
|
||||||
|
emitter.emit("end", traceId);
|
||||||
|
}, 1000);
|
||||||
|
self.logger.logRequest({
|
||||||
|
email: username,
|
||||||
|
time: requestTime,
|
||||||
|
mode: session.currentMode,
|
||||||
|
model: proxyModel,
|
||||||
|
completed: true,
|
||||||
|
unusualQueryVolume: true,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(processedContent);
|
||||||
|
accumulatedResponse += processedContent;
|
||||||
|
|
||||||
|
if (Date.now() - startTime >= 20000) {
|
||||||
|
responseAfter20Seconds += processedContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
emitter.emit("completion", traceId, processedContent);
|
||||||
|
} else {
|
||||||
|
finalResponse += processedContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查自定义结束标记
|
||||||
|
if (customEndMarkerEnabled && customEndMarker && checkEndMarker(responseAfter20Seconds, customEndMarker)) {
|
||||||
|
isEnding = true;
|
||||||
|
console.log("检测到自定义终止,关闭请求");
|
||||||
|
setTimeout(async () => {
|
||||||
|
await cleanup();
|
||||||
|
emitter.emit(stream ? "end" : "completion", traceId, stream ? undefined : finalResponse);
|
||||||
|
}, 1000);
|
||||||
|
self.logger.logRequest({
|
||||||
|
email: username,
|
||||||
|
time: requestTime,
|
||||||
|
mode: session.currentMode,
|
||||||
|
model: proxyModel,
|
||||||
|
completed: true,
|
||||||
|
unusualQueryVolume: unusualQueryVolumeTriggered,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "customEndMarkerEnabled":
|
||||||
|
customEndMarkerEnabled = true;
|
||||||
|
break;
|
||||||
|
case "done":
|
||||||
|
if (isEnding) return;
|
||||||
|
console.log("请求结束");
|
||||||
|
isEnding = true;
|
||||||
|
await cleanup(); // 清理
|
||||||
|
emitter.emit(stream ? "end" : "completion", traceId, stream ? undefined : finalResponse);
|
||||||
|
self.logger.logRequest({
|
||||||
|
email: username,
|
||||||
|
time: requestTime,
|
||||||
|
mode: session.currentMode,
|
||||||
|
model: proxyModel,
|
||||||
|
completed: true,
|
||||||
|
unusualQueryVolume: unusualQueryVolumeTriggered,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "error": {
|
||||||
|
if (isEnding) return; // 已结束则忽略
|
||||||
|
|
||||||
|
console.error("请求发生错误", data);
|
||||||
|
errorCount++;
|
||||||
|
if (errorCount >= 3) {
|
||||||
|
const errorMessage = "连接中断,未收到服务器响应";
|
||||||
|
if (errorTimer) {
|
||||||
|
clearTimeout(errorTimer);
|
||||||
|
errorTimer = null;
|
||||||
|
}
|
||||||
|
isEnding = true;
|
||||||
|
finalResponse += ` (${errorMessage})`;
|
||||||
|
await cleanup();
|
||||||
|
emitter.emit("completion", traceId, errorMessage);
|
||||||
|
emitter.emit("end", traceId);
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
self.logger.logRequest({
|
||||||
|
email: username,
|
||||||
|
time: requestTime,
|
||||||
|
mode: session.currentMode,
|
||||||
|
model: proxyModel,
|
||||||
|
completed: false,
|
||||||
|
unusualQueryVolume: unusualQueryVolumeTriggered,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (errorTimer) {
|
||||||
|
clearTimeout(errorTimer);
|
||||||
|
}
|
||||||
|
errorTimer = setTimeout(async () => {
|
||||||
|
console.log("连接超时,终止请求");
|
||||||
|
const errorMessage = "连接中断,未收到服务器响应";
|
||||||
|
|
||||||
|
emitter.emit("completion", traceId, errorMessage);
|
||||||
|
finalResponse += ` (${errorMessage})`;
|
||||||
|
|
||||||
|
isEnding = true;
|
||||||
|
await cleanup();
|
||||||
|
|
||||||
|
emitter.emit("end", traceId);
|
||||||
|
self.logger.logRequest({
|
||||||
|
email: username,
|
||||||
|
time: requestTime,
|
||||||
|
mode: session.currentMode,
|
||||||
|
model: proxyModel,
|
||||||
|
completed: false,
|
||||||
|
unusualQueryVolume: unusualQueryVolumeTriggered,
|
||||||
|
});
|
||||||
|
}, ERROR_TIMEOUT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
responseTimeout = setTimeout(async () => {
|
responseTimeout = setTimeout(async () => {
|
||||||
if (!responseStarted && !clientState.isClosed()) {
|
if (!responseStarted && !clientState.isClosed()) {
|
||||||
console.log(`${responseTimeoutTimer / 1000}秒内没有收到响应,尝试重新发送请求`);
|
console.log(`${responseTimeoutTimer / 1000}秒内没有收到响应,尝试重新发送请求`);
|
||||||
@@ -1710,3 +1739,20 @@ function randomSelect(input) {
|
|||||||
return words[randomIndex];
|
return words[randomIndex];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号标记失效并保存
|
||||||
|
* @param {string} username - 账号邮箱
|
||||||
|
* @param {Object} config - 配置对象
|
||||||
|
*/
|
||||||
|
async function markAccountAsInvalid(username, config) {
|
||||||
|
if (!config.invalid_accounts) {
|
||||||
|
config.invalid_accounts = {};
|
||||||
|
}
|
||||||
|
config.invalid_accounts[username] = "已失效";
|
||||||
|
try {
|
||||||
|
fs.writeFileSync("./config.mjs", `export const config = ${JSON.stringify(config, null, 4)}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`保存失效账号信息失败:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user