Add files via upload

This commit is contained in:
被遗忘的记忆
2025-03-14 09:46:59 +08:00
committed by GitHub
parent ec716a3d86
commit ee9a10800d
+262 -262
View File
@@ -1,262 +1,262 @@
import {EventEmitter} from "events";
import {connect} from "puppeteer-real-browser";
import {v4 as uuidV4} from "uuid";
import path from "path";
import {fileURLToPath} from "url";
import {createDirectoryIfNotExists, sleep} from "../utils.mjs";
import '../proxyAgent.mjs';
import NetworkMonitor from "../networkMonitor.mjs";
import {detectBrowser} from "../utils/browserDetector.mjs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class HappyApiProvider {
constructor(config) {
this.config = config;
this.sessions = {};
this.preferredBrowser = 'auto';
this.networkMonitor = new NetworkMonitor();
}
async init() {
console.log(`本项目依赖 Chrome 或 Edge 浏览器,请勿关闭弹出的浏览器窗口。如果出现错误请检查是否已安装 Chrome 或 Edge 浏览器。`);
const browserPath = detectBrowser(this.preferredBrowser);
this.sessions = {};
// 手动登录
const currentUsername = 'manual_login';
createDirectoryIfNotExists(path.join(__dirname, "browser_profiles", currentUsername));
try {
const response = await connect({
headless: "auto",
turnstile: true,
customConfig: {
userDataDir: path.join(__dirname, "browser_profiles", currentUsername),
executablePath: browserPath,
},
});
const {page, browser} = response;
console.log(`请在打开的浏览器窗口中手动登录 happyapi.org`);
await page.goto("https://happyapi.org", {waitUntil: 'networkidle0'});
await sleep(3000); // 等待页面加载完毕
const sessionCookie = await this.waitForManualLogin(page);
if (sessionCookie) {
this.sessions[currentUsername] = {browser, page, sessionCookie, valid: true};
console.log(`成功获取会话 cookie`);
} else {
console.error(`未能检测到登录状态,请确保已成功登录`);
await browser.close();
}
} catch (error) {
console.error(`初始化浏览器失败`);
console.error(error);
}
// 开始网络监控
await this.networkMonitor.startMonitoring();
}
async waitForManualLogin(page) {
return new Promise((resolve) => {
const checkLoginStatus = async () => {
// 检查是否存在特定的登录成功标识元素
const isLoggedIn = await this.checkLoginStatus(page);
if (isLoggedIn) {
console.log(`检测到登录成功`);
const cookies = await page.cookies();
const sessionCookie = this.extractSessionCookie(cookies);
resolve(sessionCookie);
} else {
setTimeout(checkLoginStatus, 1000);
}
};
page.on('request', (request) => {
if (request.url() === 'https://happyapi.org/api/v1/auths/signin' && request.method() === 'POST') {
console.log('检测到登录请求');
checkLoginStatus();
}
});
checkLoginStatus();
});
}
extractSessionCookie(cookies) {
const cfClearance = cookies.find(c => c.name === 'cf_clearance')?.value;
const token = cookies.find(c => c.name === 'token')?.value;
if (cfClearance && token) {
return [
{
name: 'cf_clearance',
value: cfClearance,
domain: 'happyapi.org',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'Lax',
},
{
name: 'token',
value: token,
domain: 'happyapi.org',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'Lax',
}
];
} else {
console.error('无法提取有效的会话 cookie');
return null;
}
}
async checkLoginStatus(page) {
try {
const userNameElement = await page.waitForSelector('div.self-center.font-medium', {timeout: 5000});
const userName = await page.evaluate(element => element.textContent, userNameElement);
return true;
} catch {
return false;
}
}
async getCompletion({username, messages, stream = false, proxyModel}) {
const session = this.sessions[username];
if (!session || !session.valid) {
throw new Error(`用户 ${username} 的会话无效`);
}
//刷新页面
await session.page.goto("https://happyapi.org", {waitUntil: 'domcontentloaded'});
const {page} = session;
const emitter = new EventEmitter();
try {
let userQuery = messages.map(msg => msg.content).join("\n\n");
const inputSelector = 'textarea#chat-textarea';
const sendButtonSelector = 'button#send-message-button';
// 输入消息
await page.waitForSelector(inputSelector, {visible: true});
await page.evaluate((selector, message) => {
const textarea = document.querySelector(selector);
textarea.value = message;
// 手动触发事件
textarea.dispatchEvent(new Event('input', {bubbles: true}));
textarea.dispatchEvent(new Event('change', {bubbles: true}));
}, inputSelector, userQuery.trim());
await page.waitForTimeout(100);
// 点击发送按钮
await page.waitForSelector(sendButtonSelector, {visible: true});
await page.click(sendButtonSelector);
// 监听请求
const targetUrl = 'https://happyapi.org/api/chat/completions';
const traceId = uuidV4();
let finalResponse = ""; // 存储最终响应
let responseStarted = false; // 是否开始接收
let isEnding = false; // 是否正在结束
// 清理函数
const cleanup = async () => {
clearTimeout(responseTimeout);
page.removeListener('response', responseHandler);
};
const responseTimeout = setTimeout(() => {
if (!isEnding) {
isEnding = true;
emitter.emit('error', new Error('请求超时'));
cleanup();
}
}, 60000);
const responseHandler = async (response) => {
if (response.url() === targetUrl) {
try {
const buffer = await response.buffer();
const text = buffer.toString('utf8');
// 处理流式数据
const lines = text.split('\n\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.substring(6).trim();
if (data === '[DONE]') {
if (!isEnding) {
isEnding = true;
console.log("请求结束");
emitter.emit('end');
cleanup();
}
return;
}
if (data.length === 0) {
continue;
}
const parsedData = JSON.parse(data);
const contentDelta = parsedData.choices[0].delta.content || '';
if (stream) {
emitter.emit('completion', traceId, contentDelta);
} else {
finalResponse += contentDelta;
}
}
}
} catch (e) {
if (!isEnding) {
isEnding = true;
console.error("请求发生错误", e);
emitter.emit('error', e);
cleanup();
}
}
}
};
// 监听response
page.on('response', responseHandler);
// 清除计时器和监听器
emitter.on('end', () => {
cleanup();
});
emitter.on('error', () => {
cleanup();
});
return {
completion: emitter,
cancel: () => {
}
};
} catch (error) {
emitter.emit('error', error);
return {
completion: emitter,
cancel: () => { }
};
}
}
}
export default HappyApiProvider;
import {EventEmitter} from "events";
import {connect} from "puppeteer-real-browser";
import {v4 as uuidV4} from "uuid";
import path from "path";
import {fileURLToPath} from "url";
import {createDirectoryIfNotExists, sleep} from "../utils/cookieUtils.mjs";
import '../proxyAgent.mjs';
import NetworkMonitor from "../networkMonitor.mjs";
import {detectBrowser} from "../utils/browserDetector.mjs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class HappyApiProvider {
constructor(config) {
this.config = config;
this.sessions = {};
this.preferredBrowser = 'auto';
this.networkMonitor = new NetworkMonitor();
}
async init() {
console.log(`本项目依赖 Chrome 或 Edge 浏览器,请勿关闭弹出的浏览器窗口。如果出现错误请检查是否已安装 Chrome 或 Edge 浏览器。`);
const browserPath = detectBrowser(this.preferredBrowser);
this.sessions = {};
// 手动登录
const currentUsername = 'manual_login';
createDirectoryIfNotExists(path.join(__dirname, "browser_profiles", currentUsername));
try {
const response = await connect({
headless: "auto",
turnstile: true,
customConfig: {
userDataDir: path.join(__dirname, "browser_profiles", currentUsername),
executablePath: browserPath,
},
});
const {page, browser} = response;
console.log(`请在打开的浏览器窗口中手动登录 happyapi.org`);
await page.goto("https://happyapi.org", {waitUntil: 'networkidle0'});
await sleep(3000); // 等待页面加载完毕
const sessionCookie = await this.waitForManualLogin(page);
if (sessionCookie) {
this.sessions[currentUsername] = {browser, page, sessionCookie, valid: true};
console.log(`成功获取会话 cookie`);
} else {
console.error(`未能检测到登录状态,请确保已成功登录`);
await browser.close();
}
} catch (error) {
console.error(`初始化浏览器失败`);
console.error(error);
}
// 开始网络监控
await this.networkMonitor.startMonitoring();
}
async waitForManualLogin(page) {
return new Promise((resolve) => {
const checkLoginStatus = async () => {
// 检查是否存在特定的登录成功标识元素
const isLoggedIn = await this.checkLoginStatus(page);
if (isLoggedIn) {
console.log(`检测到登录成功`);
const cookies = await page.cookies();
const sessionCookie = this.extractSessionCookie(cookies);
resolve(sessionCookie);
} else {
setTimeout(checkLoginStatus, 1000);
}
};
page.on('request', (request) => {
if (request.url() === 'https://happyapi.org/api/v1/auths/signin' && request.method() === 'POST') {
console.log('检测到登录请求');
checkLoginStatus();
}
});
checkLoginStatus();
});
}
extractSessionCookie(cookies) {
const cfClearance = cookies.find(c => c.name === 'cf_clearance')?.value;
const token = cookies.find(c => c.name === 'token')?.value;
if (cfClearance && token) {
return [
{
name: 'cf_clearance',
value: cfClearance,
domain: 'happyapi.org',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'Lax',
},
{
name: 'token',
value: token,
domain: 'happyapi.org',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'Lax',
}
];
} else {
console.error('无法提取有效的会话 cookie');
return null;
}
}
async checkLoginStatus(page) {
try {
const userNameElement = await page.waitForSelector('div.self-center.font-medium', {timeout: 5000});
const userName = await page.evaluate(element => element.textContent, userNameElement);
return true;
} catch {
return false;
}
}
async getCompletion({username, messages, stream = false, proxyModel}) {
const session = this.sessions[username];
if (!session || !session.valid) {
throw new Error(`用户 ${username} 的会话无效`);
}
//刷新页面
await session.page.goto("https://happyapi.org", {waitUntil: 'domcontentloaded'});
const {page} = session;
const emitter = new EventEmitter();
try {
let userQuery = messages.map(msg => msg.content).join("\n\n");
const inputSelector = 'textarea#chat-textarea';
const sendButtonSelector = 'button#send-message-button';
// 输入消息
await page.waitForSelector(inputSelector, {visible: true});
await page.evaluate((selector, message) => {
const textarea = document.querySelector(selector);
textarea.value = message;
// 手动触发事件
textarea.dispatchEvent(new Event('input', {bubbles: true}));
textarea.dispatchEvent(new Event('change', {bubbles: true}));
}, inputSelector, userQuery.trim());
await page.waitForTimeout(100);
// 点击发送按钮
await page.waitForSelector(sendButtonSelector, {visible: true});
await page.click(sendButtonSelector);
// 监听请求
const targetUrl = 'https://happyapi.org/api/chat/completions';
const traceId = uuidV4();
let finalResponse = ""; // 存储最终响应
let responseStarted = false; // 是否开始接收
let isEnding = false; // 是否正在结束
// 清理函数
const cleanup = async () => {
clearTimeout(responseTimeout);
page.removeListener('response', responseHandler);
};
const responseTimeout = setTimeout(() => {
if (!isEnding) {
isEnding = true;
emitter.emit('error', new Error('请求超时'));
cleanup();
}
}, 60000); // 60秒,可根据需要调整
const responseHandler = async (response) => {
if (response.url() === targetUrl) {
try {
const buffer = await response.buffer();
const text = buffer.toString('utf8');
// 处理流式数据
const lines = text.split('\n\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.substring(6).trim();
if (data === '[DONE]') {
if (!isEnding) {
isEnding = true;
console.log("请求结束");
emitter.emit('end');
cleanup();
}
return;
}
if (data.length === 0) {
continue;
}
const parsedData = JSON.parse(data);
const contentDelta = parsedData.choices[0].delta.content || '';
if (stream) {
emitter.emit('completion', traceId, contentDelta);
} else {
finalResponse += contentDelta;
}
}
}
} catch (e) {
if (!isEnding) {
isEnding = true;
console.error("请求发生错误", e);
emitter.emit('error', e);
cleanup();
}
}
}
};
// 监听response
page.on('response', responseHandler);
// 清除计时器和监听器
emitter.on('end', () => {
cleanup();
});
emitter.on('error', () => {
cleanup();
});
return {
completion: emitter,
cancel: () => {
}
};
} catch (error) {
emitter.emit('error', error);
return {
completion: emitter,
cancel: () => { }
};
}
}
}
export default HappyApiProvider;