|
import express from 'express';
|
|
import http, { get } from 'http';
|
|
import https from 'https';
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
import cors from 'cors';
|
|
import fs from 'fs';
|
|
import fsPromises from 'fs/promises';
|
|
import os from 'os';
|
|
import { createRequire } from 'module';
|
|
import EventSource from'eventsource';
|
|
import HttpsProxyAgent from 'https-proxy-agent';
|
|
import axios from 'axios';
|
|
import si from 'systeminformation';
|
|
import crypto from 'crypto';
|
|
import { chromium } from '@playwright/test';
|
|
import { Readable } from 'stream';
|
|
import { createServer } from 'http';
|
|
|
|
const banbenhao = "1.4";
|
|
|
|
class MemoryMonitor {
|
|
constructor(page) {
|
|
this.page = page;
|
|
this.warningThreshold = 200 * 1024 * 1024;
|
|
this.criticalThreshold = 400 * 1024 * 1024;
|
|
}
|
|
|
|
async checkMemory() {
|
|
try {
|
|
const metrics = await this.page.evaluate(() => {
|
|
if (!performance.memory) return null;
|
|
return {
|
|
usedJSHeapSize: performance.memory.usedJSHeapSize,
|
|
totalJSHeapSize: performance.memory.totalJSHeapSize,
|
|
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit
|
|
};
|
|
});
|
|
|
|
if (!metrics) {
|
|
console.log('Memory metrics not available');
|
|
return null;
|
|
}
|
|
|
|
|
|
const usedMB = Math.round(metrics.usedJSHeapSize / (1024 * 1024));
|
|
const totalMB = Math.round(metrics.totalJSHeapSize / (1024 * 1024));
|
|
const limitMB = Math.round(metrics.jsHeapSizeLimit / (1024 * 1024));
|
|
|
|
console.log(`Memory Usage: ${usedMB}MB / ${totalMB}MB (Limit: ${limitMB}MB)`);
|
|
|
|
|
|
if (metrics.usedJSHeapSize > this.warningThreshold) {
|
|
console.warn('High memory usage detected!');
|
|
await this.optimizeMemory();
|
|
}
|
|
|
|
|
|
if (metrics.usedJSHeapSize > this.criticalThreshold) {
|
|
console.error('Critical memory usage! Forcing garbage collection...');
|
|
await this.forceGC();
|
|
}
|
|
|
|
return metrics;
|
|
} catch (error) {
|
|
console.error('Error checking memory:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async optimizeMemory() {
|
|
try {
|
|
await this.page.evaluate(() => {
|
|
|
|
console.clear();
|
|
|
|
|
|
const images = document.getElementsByTagName('img');
|
|
for (let img of images) {
|
|
if (!img.isConnected) {
|
|
img.src = '';
|
|
}
|
|
}
|
|
|
|
|
|
if (window.gc) {
|
|
window.gc();
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Error optimizing memory:', error);
|
|
}
|
|
}
|
|
|
|
async forceGC() {
|
|
try {
|
|
await this.page.evaluate(() => {
|
|
if (window.gc) {
|
|
window.gc();
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Error forcing GC:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function setupMemoryMonitoring(page) {
|
|
const monitor = new MemoryMonitor(page);
|
|
|
|
|
|
setInterval(async () => {
|
|
await monitor.checkMemory();
|
|
}, 1 * 60 * 1000);
|
|
|
|
|
|
return monitor;
|
|
}
|
|
|
|
|
|
let memoryMonitor;
|
|
|
|
let memoryMonitor2;
|
|
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const cookiesjson = require('./cookies.json');
|
|
|
|
const config = require('./config.json');
|
|
const app = express();
|
|
const server = http.createServer(app);
|
|
let requestId = null;
|
|
let resssss = null;
|
|
let Aborted=false;
|
|
let Message;
|
|
let userId;
|
|
|
|
const proxyUrl = config.proxyUrl;
|
|
const proxyAgent = config.proxy ? new HttpsProxyAgent(proxyUrl) : null;
|
|
|
|
|
|
const EventEmitter = require('events');
|
|
const URL = require('url').URL;
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
app.use(cors());
|
|
app.use(express.json({ limit: '50mb' }));
|
|
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let browser = null;
|
|
let page = null;
|
|
let customEventSource;
|
|
let isRestarting;
|
|
let rrreeeqqq;
|
|
let reqmessage="";
|
|
let isstream=false;
|
|
let nowcookie="";
|
|
let nowcount=0;
|
|
let nowfilename="";
|
|
let One=true;
|
|
let cookiesCount=0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
|
|
|
|
});
|
|
|
|
|
|
process.on('uncaughtException', (error) => {
|
|
console.error('Uncaught Exception:', error);
|
|
|
|
|
|
});
|
|
function updateCookiesJson(key, value) {
|
|
|
|
console.log(key,cookiesjson[key]);
|
|
cookiesjson[key].count= value;
|
|
|
|
|
|
fs.writeFileSync(
|
|
path.join(__dirname, 'cookies.json'),
|
|
JSON.stringify(cookiesjson, null, 2)
|
|
);
|
|
}
|
|
|
|
|
|
|
|
function processFileContents(fileContents, cookiesjson) {
|
|
|
|
let cookieNowCount=0;
|
|
console.log(`cookiesCount:::111`,cookiesCount);
|
|
|
|
console.log(`cookiesjsonaaaaaaaaaaaaaaaaaaaa`,fileContents.length);
|
|
|
|
if(cookiesCount >= (fileContents.length)){
|
|
cookiesCount=0;
|
|
}
|
|
console.log(`cookiesCount:::`,cookiesCount);
|
|
|
|
const currentTimestamp = Math.floor(Date.now() / 1000);
|
|
|
|
for (const [fileName, content] of Object.entries(fileContents)) {
|
|
|
|
if (!cookiesjson[content.fileName]) {
|
|
console.log(`filename`,content.fileName);
|
|
console.log(`content`,content);
|
|
|
|
cookiesjson[content.fileName] = {
|
|
timestamp: currentTimestamp,
|
|
count: 0
|
|
};
|
|
|
|
|
|
fs.writeFileSync(
|
|
path.join(__dirname, 'cookies.json'),
|
|
JSON.stringify(cookiesjson, null, 2)
|
|
);
|
|
|
|
|
|
nowfilename=content.fileName;
|
|
nowcount=0;
|
|
if(cookieNowCount >= cookiesCount){
|
|
cookiesCount=cookiesCount+1;
|
|
console.log(`cookiesCount`,cookiesCount);
|
|
return content;
|
|
}
|
|
|
|
} else {
|
|
|
|
const fileRecord = cookiesjson[content.fileName];
|
|
console.log(`fileRecord`,fileRecord);
|
|
const timeDiff = currentTimestamp - fileRecord.timestamp;
|
|
|
|
|
|
if (fileRecord.count < config.Hours24||config.pro) {
|
|
|
|
nowcount=fileRecord.count;
|
|
|
|
|
|
|
|
|
|
|
|
nowfilename=content.fileName;
|
|
if(cookieNowCount >= cookiesCount){
|
|
cookiesCount=cookiesCount+1;
|
|
console.log(`cookiesCount`,cookiesCount);
|
|
const time11 = currentTimestamp - fileRecord.timestamp;
|
|
|
|
console.log(`currentTimestamp`,currentTimestamp);
|
|
console.log("fileRecord.timestamp",fileRecord.timestamp);
|
|
|
|
console.log(`time11`,time11);
|
|
if(time11 > 7200){
|
|
fileRecord.count = 0;
|
|
fileRecord.timestamp = currentTimestamp;
|
|
updateCookiesJson(nowfilename,0);
|
|
}
|
|
return content;
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
if (timeDiff > 7200) {
|
|
|
|
fileRecord.count = 0;
|
|
fileRecord.timestamp = currentTimestamp;
|
|
|
|
fs.writeFileSync(
|
|
path.join(__dirname, 'cookies.json'),
|
|
JSON.stringify(cookiesjson, null, 2)
|
|
);
|
|
nowcount=fileRecord.count;
|
|
nowfilename=content.fileName;
|
|
if(cookieNowCount >= cookiesCount){
|
|
cookiesCount=cookiesCount+1;
|
|
console.log(`cookiesCount`,cookiesCount);
|
|
const time11 = currentTimestamp - fileRecord.timestamp;
|
|
console.log(`time11`,time11);
|
|
if(time11 > 7200){
|
|
updateCookiesJson(nowfilename,0);
|
|
}
|
|
return content;
|
|
}
|
|
|
|
}
|
|
}
|
|
cookieNowCount=cookieNowCount+1;
|
|
}
|
|
}
|
|
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCookiesFiles() {
|
|
|
|
const cookiesPath = path.join(__dirname, 'cookies');
|
|
|
|
try {
|
|
|
|
const files = fs.readdirSync(cookiesPath);
|
|
|
|
|
|
const txtFiles = files.filter(file => path.extname(file).toLowerCase() === '.txt');
|
|
|
|
|
|
const fileContents = [];
|
|
|
|
|
|
txtFiles.forEach(file => {
|
|
const filePath = path.join(cookiesPath, file);
|
|
|
|
|
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
|
|
fileContents.push({
|
|
fileName: file,
|
|
content: content
|
|
});
|
|
});
|
|
return fileContents;
|
|
} catch (error) {
|
|
console.error('读取 cookies 文件夹失败:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function getSessionCookie(cookieString) {
|
|
var sessionCookie = cookieString.split('; ').map(pair => {
|
|
const [name, value] = pair.split('=');
|
|
return { name, value, domain: '.x.com', path: '/' };
|
|
});
|
|
return sessionCookie;
|
|
}
|
|
|
|
|
|
let fileContents=getCookiesFiles();
|
|
|
|
fileContents.forEach(file => {
|
|
try {
|
|
getSessionCookie(file.content)
|
|
} catch (error) {
|
|
|
|
console.error('Error parsing cookies:', "cookies文件出错"+file.fileName);
|
|
return;
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
let context="";
|
|
|
|
|
|
async function initializeBrowser() {
|
|
try {
|
|
let viewportSize = { width: 900, height: 700 };
|
|
|
|
const possiblePaths = [
|
|
'/ms-playwright/chromium-1046/chrome-linux/chrome',
|
|
'/ms-playwright/chromium-1060/chrome-linux/chrome',
|
|
'/ms-playwright/chromium-1080/chrome-linux/chrome',
|
|
'/ms-playwright/chromium-1140/chrome-linux/chrome'
|
|
];
|
|
let options={};
|
|
if(config.channel=="chromium"&&config.wutou==true){
|
|
|
|
options = {
|
|
headless: config.headless,
|
|
args: [
|
|
'--no-sandbox',
|
|
'--disable-setuid-sandbox',
|
|
'--disable-gpu',
|
|
'--disable-dev-shm-usage',
|
|
'--window-size=900,700',
|
|
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
|
|
'--disable-dev-shm-usage',
|
|
'--js-flags="--max_old_space_size=4096"' ]
|
|
|
|
};
|
|
}else{
|
|
options = {
|
|
headless: config.wutou,
|
|
channel: config.channel,
|
|
args: [
|
|
'--no-sandbox',
|
|
'--disable-setuid-sandbox',
|
|
'--disable-gpu',
|
|
'--disable-dev-shm-usage',
|
|
'--window-size=900,700',
|
|
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
|
|
'--disable-dev-shm-usage',
|
|
'--js-flags="--max_old_space_size=4096"' ]
|
|
|
|
};
|
|
}
|
|
|
|
|
|
let executablePath = null;
|
|
for (const path of possiblePaths) {
|
|
if (fs.existsSync(path)) {
|
|
executablePath = path;
|
|
console.log(`找到浏览器路径: ${path}`);
|
|
break;
|
|
}
|
|
}
|
|
if (executablePath) {
|
|
options.executablePath = executablePath;
|
|
}
|
|
browser = await chromium.launch(options);
|
|
|
|
|
|
context = await browser.newContext(
|
|
{ viewport: { width: 900, height: 700 },
|
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
);
|
|
page = await context.newPage();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memoryMonitor = await setupMemoryMonitoring(page);
|
|
|
|
|
|
|
|
|
|
nowcookie=await processFileContents(fileContents,cookiesjson);
|
|
if(!nowcookie){
|
|
|
|
console.log("无cookie使用");
|
|
return;
|
|
}
|
|
const sessionCookie=getSessionCookie(nowcookie.content)
|
|
|
|
|
|
await context.addCookies(sessionCookie);
|
|
|
|
await page.addInitScript(() => {
|
|
|
|
if ('serviceWorker' in navigator) {
|
|
Object.defineProperty(navigator, 'serviceWorker', {
|
|
value: {
|
|
register: () => Promise.reject('Service Worker disabled'),
|
|
getRegistration: () => Promise.resolve(null)
|
|
},
|
|
writable: false
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await context.route('**/*', async (route, request) => {
|
|
|
|
await route.continue();
|
|
|
|
|
|
if (request.resourceType() === 'document' &&
|
|
request.url().includes('grok')) {
|
|
console.log('检测到页面加载或刷新:', request.url());
|
|
|
|
|
|
await page.waitForLoadState('domcontentloaded');
|
|
|
|
|
|
await injectFetchInterceptor(page);
|
|
}
|
|
});
|
|
|
|
|
|
try {
|
|
|
|
page.on('requestfailed', (request) => {
|
|
console.error('Failed Request:', request.url(), request.failure().errorText);
|
|
});
|
|
|
|
await page.goto('https://x.com/i/grok');
|
|
console.log('Successfully opened x.com');
|
|
let textarea=null;
|
|
|
|
try {
|
|
textarea = await page.getByPlaceholder('提出任何問題').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
console.log("登录成功");
|
|
console.log('欢迎使用grok反代,成功启动!By从前跟你一样');
|
|
|
|
} catch (error) {
|
|
|
|
try {
|
|
textarea = await page.getByPlaceholder('随便问点什么').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
console.log("登录成功");
|
|
console.log('欢迎使用grok反代,成功启动!By从前跟你一样');
|
|
} catch (error) {
|
|
try {
|
|
textarea = await page.getByPlaceholder('Ask anything').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
console.log("登录成功");
|
|
console.log('欢迎使用grok反代,成功启动!By从前跟你一样');
|
|
} catch (error) {
|
|
|
|
console.log("登录失败");
|
|
console.log('登录失败!!!By从前跟你一样');
|
|
console.error('操作超时:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
console.error('An error occurred during browser initialization:', error);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function injectFetchInterceptor(page) {
|
|
console.log('注入 fetch 拦截器...');
|
|
|
|
|
|
const alreadyInjected = await page.evaluate(() => {
|
|
return window._fetchInterceptorInjected === true;
|
|
}).catch(() => false);
|
|
|
|
if (alreadyInjected) {
|
|
console.log('拦截器已经注入,跳过');
|
|
return;
|
|
}
|
|
|
|
|
|
await page.evaluate(() => {
|
|
console.log('设置 fetch 拦截器...');
|
|
|
|
|
|
window._fetchInterceptorInjected = true;
|
|
|
|
|
|
const originalFetch = window.fetch;
|
|
|
|
|
|
window.fetch = async function(...args) {
|
|
const [resource, config] = args;
|
|
|
|
console.log("resource",resource);
|
|
console.log("typeof",typeof resource);
|
|
console.log("config",config);
|
|
|
|
|
|
if (resource.href.includes('/2/grok/add_response.json') &&
|
|
config && config.method === 'POST') {
|
|
console.log('拦截到 fetch 请求:', resource);
|
|
|
|
try {
|
|
|
|
|
|
|
|
const controller = new AbortController();
|
|
window._activeFetchController = controller;
|
|
|
|
const newConfig = {
|
|
...config,
|
|
signal: controller.signal
|
|
};
|
|
const response = await originalFetch.call(this, resource, newConfig);
|
|
|
|
const clonedResponse = response.clone();
|
|
|
|
|
|
if (true) {
|
|
console.log('检测到流式响应');
|
|
|
|
|
|
const reader = clonedResponse.body.getReader();
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
|
|
|
(async () => {
|
|
let buffer = '';
|
|
try {
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
|
|
if (done) {
|
|
console.log('流读取完成');
|
|
if (buffer.length > 0) {
|
|
processEventData(buffer);
|
|
}
|
|
|
|
|
|
const event = new CustomEvent('streamDataEnd');
|
|
window.dispatchEvent(event);
|
|
window._activeFetchController = null;
|
|
break;
|
|
}
|
|
|
|
|
|
const chunk = decoder.decode(value, { stream: true });
|
|
console.log('接收到数据块:', chunk.length, '字节');
|
|
buffer += chunk;
|
|
let data = chunk.toString('utf-8');
|
|
console.log('Received data:', data);
|
|
|
|
const lines = data.split('\n');
|
|
buffer = lines.pop();
|
|
|
|
lines.forEach(line => {
|
|
let shujudata=JSON.parse(line);
|
|
console.log("shuju",shujudata);
|
|
if(shujudata.hasOwnProperty('result')&&shujudata.result.hasOwnProperty("message")){
|
|
console.log("shuju.result.message",shujudata.result.message);
|
|
processEventData(shujudata);
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error('读取流时出错:', error);
|
|
window._activeFetchController = null;
|
|
}
|
|
})();
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error('拦截请求时出错:', error);
|
|
window._activeFetchController = null;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
|
|
return originalFetch.apply(this, args);
|
|
};
|
|
|
|
|
|
async function processEventData(line) {
|
|
|
|
|
|
try {
|
|
|
|
let jsonData={};
|
|
try{
|
|
jsonData=JSON.parse(line);
|
|
}catch(error){
|
|
console.error('解析 JSON 时出错:', error, '原始数据:', line);
|
|
jsonData=line;
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('处理事件数据类型:', jsonData);
|
|
|
|
|
|
if (jsonData.hasOwnProperty("result")&&jsonData.result.hasOwnProperty("isSoftStop")) {
|
|
console.log('检测到流结束信号');
|
|
|
|
|
|
const event = new CustomEvent('streamDataEnd');
|
|
window.dispatchEvent(event);
|
|
return;
|
|
}
|
|
|
|
|
|
if(jsonData.hasOwnProperty("result")&&jsonData.result.hasOwnProperty("responseType")&&jsonData.result.responseType=="limiter"){
|
|
|
|
|
|
const event = new CustomEvent('streamDataReceived', {
|
|
detail: "ACTION_QUOTA_EXCEEDED"
|
|
});
|
|
window.dispatchEvent(event);
|
|
|
|
return;
|
|
}
|
|
|
|
console.log("jsonDataresult",jsonData.result);
|
|
|
|
|
|
if (jsonData.hasOwnProperty("result")&&jsonData.result.hasOwnProperty("message")) {
|
|
console.log('检测到增量更新:', jsonData.result);
|
|
|
|
|
|
const event = new CustomEvent('streamDataReceived', {
|
|
detail: jsonData.result.message
|
|
});
|
|
window.dispatchEvent(event);
|
|
}
|
|
} catch (error) {
|
|
console.error('解析 JSON 时出错:', error);
|
|
}
|
|
|
|
}
|
|
|
|
console.log('fetch 拦截器设置完成');
|
|
});
|
|
|
|
|
|
await page.exposeFunction('receiveStreamData', (data) => {
|
|
console.log('从浏览器接收到的数据:', data);
|
|
|
|
|
|
processStreamData(data);
|
|
}).catch(e => {
|
|
|
|
console.log('函数已经暴露,跳过');
|
|
});
|
|
|
|
|
|
await page.exposeFunction('handleStreamEnd', async () => {
|
|
|
|
|
|
|
|
if (typeof resssss !== 'undefined') {
|
|
if(!isstream){
|
|
let response=await createChatCompletion(reqmessage);
|
|
|
|
console.log("response",response);
|
|
|
|
resssss.write(JSON.stringify(response));
|
|
}
|
|
console.log('流数据传输结束');
|
|
resssss.end();
|
|
}
|
|
}).catch(e => {
|
|
|
|
console.log('函数已经暴露,跳过');
|
|
});
|
|
|
|
|
|
await page.evaluate(() => {
|
|
|
|
if (!window._eventListenersSet) {
|
|
window._eventListenersSet = true;
|
|
|
|
|
|
window.addEventListener('streamDataReceived', (event) => {
|
|
|
|
window.receiveStreamData(event.detail);
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener('streamDataEnd', () => {
|
|
window.handleStreamEnd();
|
|
});
|
|
|
|
console.log('事件监听器设置完成');
|
|
}
|
|
});
|
|
|
|
console.log('请求拦截器注入完成');
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function restartBrowser() {
|
|
console.log('Restarting browser...');
|
|
isRestarting = true;
|
|
if (browser) {
|
|
await browser.close();
|
|
}
|
|
await initializeBrowser();
|
|
isRestarting = false;
|
|
console.log('Browser restarted successfully');
|
|
}
|
|
|
|
initializeBrowser();
|
|
|
|
|
|
process.on('SIGINT', async () => {
|
|
if (browser) {
|
|
await browser.close();
|
|
}
|
|
process.exit();
|
|
});
|
|
|
|
const availableModels = [
|
|
{ id: "默认GROK输入框", name: "默认GROK输入框" },
|
|
{ id: "grok-3-文件模式", name: "grok-3-文件模式" }
|
|
];
|
|
|
|
let Upload=false;
|
|
|
|
app.post('/v1/chat/completions', async (req, res) => {
|
|
console.log('Received chat request');
|
|
reqmessage="";
|
|
One=true;
|
|
resssss = res;
|
|
Aborted = false;
|
|
|
|
res.on('close', async () => {
|
|
console.log('Client disconnected');
|
|
Aborted = true;
|
|
await page.evaluate(() => {
|
|
if (window._activeFetchController) {
|
|
console.log('中止正在进行的请求');
|
|
window._activeFetchController.abort();
|
|
window._activeFetchController = null;
|
|
}
|
|
|
|
|
|
const event = new CustomEvent('clientDisconnected');
|
|
window.dispatchEvent(event);
|
|
}).catch(err => {
|
|
console.error('中止请求时出错:', err);
|
|
});
|
|
|
|
});
|
|
|
|
let body=req.body
|
|
|
|
if(req.body.model=="grok-3-文件模式"){
|
|
|
|
Upload=true;
|
|
|
|
}else{
|
|
|
|
Upload=false;
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!body.hasOwnProperty('stream')||!body["stream"]){
|
|
isstream=false;
|
|
}else{
|
|
isstream=true;
|
|
}
|
|
res.setHeader("Content-Type", "text/event-stream;charset=utf-8");
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
req.setEncoding("utf8");
|
|
console.log("isstream",isstream)
|
|
|
|
await sendMessage(res, req.body);
|
|
});
|
|
|
|
app.get('/v1/models', (req, res) => {
|
|
Aborted = false;
|
|
res.json({
|
|
object: "list",
|
|
data: availableModels.map(model => ({
|
|
id: model.id,
|
|
object: "model",
|
|
created: 1623168000,
|
|
owned_by: "openai",
|
|
permission: [],
|
|
root: model.id,
|
|
parent: null
|
|
})),
|
|
});
|
|
res.on('close', () => {
|
|
console.log('Client disconnected');
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let localCopyPath="";
|
|
|
|
async function sendMessage(res3, message) {
|
|
|
|
|
|
|
|
let isResponseEnded = false;
|
|
if(config.pro){
|
|
|
|
}else{
|
|
fileContents=getCookiesFiles();
|
|
nowcookie=processFileContents(fileContents,cookiesjson);
|
|
if(!nowcookie){
|
|
cookiesCount=0;
|
|
nowcookie=processFileContents(fileContents,cookiesjson);
|
|
}
|
|
|
|
if(!nowcookie){
|
|
const text = "没有cookie用了";
|
|
const response = {
|
|
id: "chatcmpl-" + Math.random().toString(36).substr(2, 9),
|
|
object: "chat.completion",
|
|
created: Date.now(),
|
|
model: "gpt-3.5-turbo-0613",
|
|
usage: {
|
|
prompt_tokens: 9,
|
|
completion_tokens: text.length,
|
|
total_tokens: 9 + text.length
|
|
},
|
|
choices: [{
|
|
delta: {
|
|
role: 'assistant',
|
|
content: text || null
|
|
},
|
|
finish_reason: null,
|
|
index: 0
|
|
}]
|
|
};
|
|
resssss.write(`data: ${JSON.stringify(response).replace("\\n", "\\n ")}\n\n`);
|
|
resssss.end();
|
|
return;
|
|
}
|
|
}
|
|
|
|
const sessionCookie=getSessionCookie(nowcookie.content)
|
|
console.log("nowcookie",nowfilename);
|
|
console.log("sessionCookie",sessionCookie);
|
|
|
|
await context.addCookies(sessionCookie);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
const manageSourcesButton = await page.waitForSelector(
|
|
'button[aria-label="新聊天"][role="button"]',
|
|
{
|
|
state: 'visible',
|
|
timeout: 100
|
|
}
|
|
);
|
|
if(manageSourcesButton){
|
|
await page.evaluate(() => {
|
|
const button = document.querySelector(
|
|
'button[aria-label="新聊天"][role="button"]'
|
|
);
|
|
if (button) button.click();
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
try {
|
|
|
|
const manageSourcesButton = await page.waitForSelector(
|
|
'button[aria-label="New Chat"][role="button"]',
|
|
{
|
|
state: 'visible',
|
|
timeout: 100
|
|
}
|
|
);
|
|
if(manageSourcesButton){
|
|
await page.evaluate(() => {
|
|
const button = document.querySelector(
|
|
'button[aria-label="New Chat"][role="button"]'
|
|
);
|
|
if (button) button.click();
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
try {
|
|
|
|
const manageSourcesButton = await page.waitForSelector(
|
|
'button[aria-label="新聊天"][role="button"]',
|
|
{
|
|
state: 'visible',
|
|
timeout: 100
|
|
}
|
|
);
|
|
if(manageSourcesButton){
|
|
await page.evaluate(() => {
|
|
const button = document.querySelector(
|
|
'button[aria-label="新聊天"][role="button"]'
|
|
);
|
|
if (button) button.click();
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log("无需开启新聊天");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(config.refresh){
|
|
await page.reload();
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
}else{
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
}
|
|
|
|
try {
|
|
message = message.messages;
|
|
message = simplifyJsonString(message)
|
|
function simplifyJsonString(message) {
|
|
try {
|
|
|
|
|
|
let simplifiedMessages = message.map(msg => {
|
|
|
|
if(config.tohuman){
|
|
|
|
return `${msg.role.replace("user","Human").replace("assistant","Assistant")}: ${msg.content}`;
|
|
|
|
}else{
|
|
return `${msg.role}: ${msg.content}`;
|
|
}
|
|
});
|
|
|
|
|
|
return simplifiedMessages.join('\n\n');
|
|
} catch (error) {
|
|
console.error("Error parsing JSON:", error);
|
|
return "Error: Invalid JSON string";
|
|
}
|
|
}
|
|
Message = message;
|
|
|
|
if(Upload){
|
|
let yuyan="提出任何問題";
|
|
|
|
const txtname= Math.random().toString(36).substring(3);
|
|
localCopyPath = path.join(__dirname, `${txtname+".txt"}`);
|
|
fs.writeFileSync(localCopyPath, message);
|
|
|
|
let textarea=null;
|
|
|
|
try {
|
|
textarea = await page.getByPlaceholder('提出任何問題').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
|
|
} catch (error) {
|
|
|
|
try {
|
|
textarea = await page.getByPlaceholder('随便问点什么').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
yuyan="随便问点什么";
|
|
|
|
} catch (error) {
|
|
try {
|
|
textarea = await page.getByPlaceholder('Ask anything').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
yuyan="Ask anything";
|
|
|
|
} catch (error) {
|
|
|
|
console.error('操作超时:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!textarea) {
|
|
|
|
console.log('textarea not found');
|
|
return false;
|
|
|
|
}
|
|
try {
|
|
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
|
|
const fileInput = await page.locator('input[type="file"]');
|
|
|
|
try {
|
|
|
|
await page.waitForSelector('button[aria-label="Remove"]', {
|
|
timeout: 100,
|
|
state: 'visible'
|
|
});
|
|
|
|
|
|
await page.getByLabel('Remove').click();
|
|
} catch (error) {
|
|
|
|
console.log('无需清除文件');
|
|
}
|
|
|
|
|
|
await fileInput.setInputFiles(localCopyPath);
|
|
|
|
|
|
|
|
await page.evaluate((text) => {
|
|
navigator.clipboard.writeText(text);
|
|
}, config.Prompt);
|
|
await textarea.click();
|
|
|
|
await page.keyboard.press('Control+A');
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
await page.keyboard.press('Control+V');
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error:', error);
|
|
}
|
|
|
|
|
|
|
|
}else{
|
|
|
|
let textarea=null;
|
|
|
|
try {
|
|
textarea = await page.getByPlaceholder('提出任何問題').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
|
|
} catch (error) {
|
|
|
|
try {
|
|
textarea = await page.getByPlaceholder('随便问点什么').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
|
|
} catch (error) {
|
|
try {
|
|
textarea = await page.getByPlaceholder('Ask anything').first();
|
|
await textarea.waitFor({ state: 'visible', timeout: 100 });
|
|
|
|
} catch (error) {
|
|
|
|
console.error('操作超时:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!textarea) {
|
|
|
|
console.log('textarea not found');
|
|
|
|
}
|
|
try {
|
|
|
|
|
|
|
|
|
|
|
|
await page.evaluate((text) => {
|
|
navigator.clipboard.writeText(text);
|
|
}, Message);
|
|
await textarea.click();
|
|
|
|
await page.keyboard.press('Control+A');
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
await page.keyboard.press('Control+V');
|
|
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Aborted) {
|
|
console.log('guanbi!!!!');
|
|
customEventSource.close();
|
|
fs.unlink(localCopyPath, (err) => {
|
|
if (err) {
|
|
console.error('删除文件时出错:', err);
|
|
return;
|
|
}
|
|
console.log('文件已成功删除');
|
|
});
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
const manageSourcesButton = await page.waitForSelector(
|
|
'button[aria-label="Grok something"][role="button"]',
|
|
{
|
|
state: 'visible',
|
|
timeout: 500
|
|
}
|
|
);
|
|
if(manageSourcesButton){
|
|
await page.evaluate(() => {
|
|
const button = document.querySelector(
|
|
'button[aria-label="Grok something"][role="button"]'
|
|
);
|
|
if (button) button.click();
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log("语言不是英语")
|
|
try {
|
|
|
|
const manageSourcesButton = await page.waitForSelector(
|
|
'button[aria-label="问 Grok 问题"][role="button"]',
|
|
{
|
|
state: 'visible',
|
|
timeout: 500
|
|
}
|
|
);
|
|
if(manageSourcesButton){
|
|
await page.evaluate(() => {
|
|
const button = document.querySelector(
|
|
'button[aria-label="问 Grok 问题"][role="button"]'
|
|
);
|
|
if (button) button.click();
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log("语言不是繁体")
|
|
try {
|
|
|
|
const manageSourcesButton = await page.waitForSelector(
|
|
'button[aria-label="問 Grok 一些問題"][role="button"]',
|
|
{
|
|
state: 'visible',
|
|
timeout: 500
|
|
}
|
|
);
|
|
if(manageSourcesButton){
|
|
await page.evaluate(() => {
|
|
const button = document.querySelector(
|
|
'button[aria-label="問 Grok 一些問題"][role="button"]'
|
|
);
|
|
if (button) button.click();
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('发送点击错误', error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve();
|
|
}, 1000);
|
|
});
|
|
|
|
try {
|
|
if(Upload){
|
|
fs.unlink(localCopyPath, (err) => {
|
|
if (err) {
|
|
console.error('删除文件时出错:', err);
|
|
}
|
|
console.log('文件已成功删除');
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('删除文件时出错:', err);
|
|
}
|
|
|
|
|
|
nowcount=nowcount+1
|
|
updateCookiesJson(nowfilename,nowcount);
|
|
|
|
console.log('nowfilename',nowfilename);
|
|
console.log('nowcount',nowcount);
|
|
|
|
|
|
if (Aborted) {
|
|
console.log('guanbi!!!!');
|
|
customEventSource.close();
|
|
fs.unlink(localCopyPath, (err) => {
|
|
if (err) {
|
|
console.error('删除文件时出错:', err);
|
|
return;
|
|
}
|
|
console.log('文件已成功删除');
|
|
});
|
|
return false;
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
console.error('Error in sendMessage:', error);
|
|
if (!isResponseEnded) {
|
|
res3.write(`data: [ERROR]\n\n`);
|
|
res3.end();
|
|
}
|
|
}
|
|
}
|
|
async function clickElement(selector, page) {
|
|
await page.waitForSelector(selector, { timeout: 10000 });
|
|
const element = await page.$(selector);
|
|
if (element) {
|
|
await element.click();
|
|
console.log(`Successfully clicked the element with class "${selector}"`);
|
|
} else {
|
|
console.log(`Element with class "${selector}" not found`);
|
|
}
|
|
}
|
|
|
|
async function uploadFile(selector, filePath, page) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
const fileContent = await fsPromises.readFile(filePath);
|
|
const fileName = path.basename(filePath);
|
|
|
|
console.log(`File size: ${fileContent.length} bytes`);
|
|
|
|
|
|
const fileType = getFileType(fileName);
|
|
|
|
|
|
await page.evaluate(async ({ fileName, fileContent, fileType,selector}) => {
|
|
|
|
const uint8Array = new Uint8Array(fileContent);
|
|
|
|
|
|
const blob = new Blob([uint8Array], { type: fileType });
|
|
|
|
console.log(`Blob size: ${blob.size} bytes`);
|
|
|
|
|
|
const file = new File([blob], fileName, { type: fileType });
|
|
|
|
console.log(`File size: ${file.size} bytes`);
|
|
|
|
|
|
const dataTransfer = new DataTransfer();
|
|
dataTransfer.items.add(file);
|
|
|
|
|
|
const createDragEvent = (type) => {
|
|
return new DragEvent(type, {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
dataTransfer: dataTransfer
|
|
});
|
|
};
|
|
|
|
console.log('File upload simulation started for:', selector);
|
|
|
|
const dropZone = document.querySelector(`[placeholder="${selector}"]`);
|
|
|
|
dropZone.dispatchEvent(createDragEvent('dragenter'));
|
|
dropZone.dispatchEvent(createDragEvent('dragover'));
|
|
dropZone.dispatchEvent(createDragEvent('drop'));
|
|
|
|
console.log('File upload simulation completed for:', fileName);
|
|
}, { fileName, fileContent: Array.from(fileContent), fileType,selector });
|
|
|
|
console.log('File upload process completed successfully.');
|
|
} catch (error) {
|
|
console.error('Error during file upload:', error);
|
|
throw error;
|
|
}
|
|
|
|
function getFileType(fileName) {
|
|
const extension = path.extname(fileName).toLowerCase();
|
|
switch (extension) {
|
|
case '.jpg':
|
|
case '.jpeg':
|
|
return 'image/jpeg';
|
|
case '.png':
|
|
return 'image/png';
|
|
case '.gif':
|
|
return 'image/gif';
|
|
case '.pdf':
|
|
return 'application/pdf';
|
|
default:
|
|
return 'application/octet-stream';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getFileType(fileName) {
|
|
const extension = path.extname(fileName).toLowerCase();
|
|
switch (extension) {
|
|
case '.jpg':
|
|
case '.jpeg':
|
|
return 'image/jpeg';
|
|
case '.png':
|
|
return 'image/png';
|
|
case '.gif':
|
|
return 'image/gif';
|
|
case '.pdf':
|
|
return 'application/pdf';
|
|
default:
|
|
return 'application/octet-stream';
|
|
}
|
|
}
|
|
|
|
async function setupresponseInterception(page, res4, setResponseEnded) {
|
|
|
|
|
|
page.on('response', async (response) => {
|
|
if (response.url().includes('/api/streamingSearch')) {
|
|
const reader = response.body().getReader();
|
|
|
|
let done, value;
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
|
while (true) {
|
|
|
|
({ done, value } = await reader.read());
|
|
|
|
if (done) {
|
|
console.log('流结束');
|
|
break;
|
|
}
|
|
|
|
|
|
const chunkString = decoder.decode(value, { stream: true });
|
|
console.log('接收到的数据块:', chunkString);
|
|
|
|
|
|
try {
|
|
const jsonData = JSON.parse(chunkString);
|
|
console.log('解析的 JSON 数据:', jsonData);
|
|
} catch (error) {
|
|
console.error('JSON 解析错误:', error);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.get('/', (req, res) => {
|
|
res.send('Genspark AI Proxy is running');
|
|
});
|
|
|
|
|
|
|
|
|
|
app.use((err, req, res, next) => {
|
|
console.error('Error:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
});
|
|
|
|
|
|
app.use((req, res) => {
|
|
console.log(`Route not found: ${req.method} ${req.url}`);
|
|
res.status(404).json({ error: 'Not found' });
|
|
});
|
|
|
|
|
|
|
|
|
|
server.listen(config.port, '0.0.0.0', () => {
|
|
console.log(`服务器运行在 http://localhost:${config.port}`);
|
|
});
|
|
function createChatCompletion(content){
|
|
const completionTokens = content.length;
|
|
console.log("content",content);
|
|
|
|
return {
|
|
id: generateId(),
|
|
object: "chat.completion",
|
|
created: Math.floor(Date.now() / 1000),
|
|
model: "gpt-3.5-turbo",
|
|
system_fingerprint: "fp_44709d6fcb",
|
|
choices: [
|
|
{
|
|
index: 0,
|
|
message: {
|
|
role: "assistant",
|
|
content: content
|
|
},
|
|
logprobs: null,
|
|
finish_reason: "stop"
|
|
}
|
|
],
|
|
usage: {
|
|
prompt_tokens: completionTokens,
|
|
completion_tokens: completionTokens,
|
|
total_tokens: completionTokens
|
|
}
|
|
};
|
|
};
|
|
const generateId = () => 'chatcmpl-' + Math.random().toString(36).substring(2, 15);
|
|
|
|
async function processStreamData(message) {
|
|
if (Aborted) {
|
|
console.log('Request aborted, stopping data processing');
|
|
|
|
return;
|
|
}
|
|
if(message=="ACTION_QUOTA_EXCEEDED"){
|
|
updateCookiesJson(nowfilename,10000);
|
|
|
|
const text = "这个号次数上限了";
|
|
|
|
const response = {
|
|
id: "chatcmpl-" + Math.random().toString(36).substr(2, 9),
|
|
object: "chat.completion",
|
|
created: Date.now(),
|
|
model: "gpt-3.5-turbo-0613",
|
|
usage: {
|
|
prompt_tokens: 9,
|
|
completion_tokens: text.length,
|
|
total_tokens: 9 + text.length
|
|
},
|
|
choices: [{
|
|
delta: {
|
|
role: 'assistant',
|
|
content: text || null
|
|
},
|
|
finish_reason: null,
|
|
index: 0
|
|
}]
|
|
};
|
|
resssss.write(`data: ${JSON.stringify(response).replace("\\n", "\\n ")}\n\n`);
|
|
resssss.end();
|
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
await page.evaluate(() => {
|
|
const close_button = document.querySelector('[class="button ok"]');
|
|
if (close_button) {
|
|
const event = new MouseEvent('click', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
close_button.dispatchEvent(event);
|
|
}
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (message){
|
|
try {
|
|
|
|
const text = message;
|
|
const response = {
|
|
id: "chatcmpl-" + Math.random().toString(36).substr(2, 9),
|
|
object: "chat.completion",
|
|
created: Date.now(),
|
|
model: "gpt-3.5-turbo-0613",
|
|
usage: {
|
|
prompt_tokens: 9,
|
|
completion_tokens: text.length,
|
|
total_tokens: 9 + text.length
|
|
},
|
|
choices: [{
|
|
delta: {
|
|
role: 'assistant',
|
|
content: text || null
|
|
},
|
|
finish_reason: null,
|
|
index: 0
|
|
}]
|
|
};
|
|
|
|
if (resssss) {
|
|
console.log('Sending response:', JSON.stringify(response));
|
|
if (isstream) {
|
|
|
|
reqmessage += text;
|
|
resssss.flushHeaders();
|
|
resssss.write(`data: ${JSON.stringify(response)}\n\n`);
|
|
resssss.flushHeaders();
|
|
} else {
|
|
reqmessage += text;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error processing message:', error);
|
|
console.log('Client disconnected');
|
|
Aborted = true;
|
|
if (rrreeeqqq) {
|
|
customEventSource.close();
|
|
resssss = null;
|
|
}
|
|
}
|
|
}
|
|
} |