import { Hono } from 'hono' import { serve } from '@hono/node-server' import { serveStatic } from '@hono/node-server/serve-static' import { chromium, type Browser, type BrowserContext, type Route, type Page } from 'playwright' import process from 'process' import fs from 'fs' const app = new Hono() // 浏览器实例 let browser: Browser | null = null let gensparkContext: BrowserContext | null = null const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'; // 初始化浏览器 async function initBrowser() { if (!browser) { browser = await chromium.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-blink-features=AutomationControlled', // 禁用自动化特征 '--disable-infobars', '--window-size=1920,1080' ], executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH, // 使用系统 Chromium }) } return browser } // 初始化genspark页面 async function initGensparkContext() { const browser = await initBrowser() if (!gensparkContext) { gensparkContext = await browser.newContext({ userAgent: userAgent, viewport: { width: 1920, height: 1080 }, extraHTTPHeaders: { 'Accept-Language': 'en-US,en;q=0.9' }, deviceScaleFactor: 1, locale: 'en-US', timezoneId: 'America/New_York', geolocation: { longitude: -73.935242, latitude: 40.730610 }, // 纽约坐标,可根据需要调整 permissions: ['geolocation'], javaScriptEnabled: true, bypassCSP: true, // 绕过内容安全策略 colorScheme: 'light', acceptDownloads: true, }) // 注入反检测脚本 await gensparkContext.addInitScript(() => { Object.defineProperty(navigator, 'webdriver', { get: () => false }) Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] }) //@ts-ignore window.navigator.chrome = { runtime: {} } Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }) }) } return gensparkContext } // 验证响应头值是否有效 function isValidHeaderValue(value: string): boolean { // 检查值是否为空或包含无效字符 if (!value || typeof value !== 'string') return false; // 检查是否包含换行符或回车符 if (/[\r\n]/.test(value)) return false; return true; } // 处理请求转发 async function handleRequest(url: string, method: string, headers: any, body?: any) { const browser = await initBrowser() const page = await browser.newPage() try { // 只移除确实需要移除的请求头 delete headers['host'] delete headers['connection'] delete headers['content-length'] delete headers['accept-encoding'] // 移除cf相关的头 delete headers['cdn-loop'] delete headers['cf-connecting-ip'] delete headers['cf-connecting-o2o'] delete headers['cf-ew-via'] delete headers['cf-ray'] delete headers['cf-visitor'] delete headers['cf-worker'] //移除其他无效的请求头 delete headers['x-direct-url'] delete headers['x-forwarded-for'] delete headers['x-forwarded-port'] delete headers['x-forwarded-proto'] headers['user-agent'] = userAgent console.log('处理请求:', method, url, headers, body) // 设置请求拦截器 await page.route('**/*', async (route: Route) => { const request = route.request() if (request.url() === url) { await route.continue({ method: method, headers: { ...request.headers(), ...headers }, postData: body }) } else { // 允许其他资源加载 await route.continue() } }) // 配置页面请求选项 const response = await page.goto(url, { waitUntil: 'domcontentloaded', // 改为更快的加载策略 timeout: 600000 }) if (!response) { throw new Error('未收到响应') } // 等待页面加载完成 await page.waitForLoadState('networkidle', { timeout: 600000 }).catch(() => { console.log('等待页面加载超时,继续处理') }) // 获取响应数据 const status = response.status() const responseHeaders = response.headers() // 确保移除可能导致解码问题的响应头 delete responseHeaders['content-encoding'] delete responseHeaders['content-length'] // 过滤无效的响应头 const validHeaders: Record = {} for (const [key, value] of Object.entries(responseHeaders)) { if (isValidHeaderValue(value as string)) { validHeaders[key] = value as string } else { console.warn(`跳过无效的响应头: ${key}: ${value}`) } } // 直接获取响应体的二进制数据 const responseBody = await response.body() console.log('请求处理完成:', status, responseBody.toString()) await page.close() return { status, headers: validHeaders, body: responseBody } } catch (error: any) { await page.close() console.error('请求处理错误:', error) throw new Error(`请求失败: ${error.message}`) } } // 添加静态文件服务 app.use('/public/*', serveStatic({ root: './' })) // 修改点 1: 处理根路由直接返回 index.html 内容,而不是重定向 app.get('/', async (c) => { try { const htmlContent = fs.readFileSync('./index.html', 'utf-8') return c.html(htmlContent) } catch (error) { console.error('读取index.html失败:', error) return c.text('无法读取主页', 500) } }) // 修改点 2: 添加 /genspark 路由来获取reCAPTCHA令牌 app.get('/genspark', async (c) => { const headers = Object.fromEntries(c.req.raw.headers) // Get the cookie string from headers const cookieString = headers.cookie || ''; // Parse cookies into an array of objects with name and value properties const cookies = cookieString.split(';').map(cookie => { const [name, value] = cookie.trim().split('='); if (name.startsWith("_ga")) { return { name, value, domain: 'genspark.ai', path: '/' }; } return { name, value, domain: 'www.genspark.ai', path: '/' }; }).filter(cookie => cookie.name && cookie.value); gensparkContext = await initGensparkContext() console.log('Cookies:', cookies) if (cookies && cookies.length > 0) { await gensparkContext.clearCookies() await gensparkContext.addCookies(cookies); } const gensparkPage = await gensparkContext.newPage() try { //刷新页面以确保获取新令牌 await gensparkPage.goto('https://www.genspark.ai/agents?type=moa_chat', { waitUntil: 'networkidle', timeout: 3600000 }) // 执行脚本获取令牌 const token = await gensparkPage.evaluate(() => { return new Promise((resolve, reject) => { // @ts-ignore window.grecaptcha.ready(function () { // @ts-ignore grecaptcha.execute( "6Leq7KYqAAAAAGdd1NaUBJF9dHTPAKP7DcnaRc66", { action: 'copilot' }, ).then(function (token: string) { resolve(token) }).catch(function (error: Error) { reject(error) }); }); // 设置超时 setTimeout(() => reject(new Error("获取令牌超时")), 10000); }); }).catch(error => { return c.json({ code: 500, message: '获取令牌失败' }) }); console.log('token:', token) return c.json({ code: 200, message: '获取令牌成功', token: token }) } catch (error) { console.error('获取令牌失败:', error) if (gensparkContext) { await gensparkContext.close().catch(() => { }); gensparkContext = null; } } finally { await gensparkPage.close().catch(() => { }); } console.log('token:', "获取令牌失败") return c.json({ code: 500, message: '获取令牌失败' }) }) // 处理所有 HTTP 方法 app.all('*', async (c) => { const url = c.req.query('url') if (!url) { return c.text('Missing url parameter', 400) } try { const method = c.req.method const headers = Object.fromEntries(c.req.raw.headers) const body = method !== 'GET' ? await c.req.text() : undefined const result = await handleRequest(url, method, headers, body) // 创建标准响应 const response = new Response(result.body, { status: result.status, headers: new Headers({ ...result.headers, 'content-encoding': 'identity' // 显式设置不使用压缩 }) }) return response } catch (error) { console.error('Error:', error) return new Response('Internal Server Error', { status: 500, headers: new Headers({ 'content-type': 'text/plain' }) }) } }) // 清理函数 async function cleanup() { if (gensparkContext) { await gensparkContext.close().catch(() => { }); gensparkContext = null; } if (browser) { await browser.close().catch(() => { }); browser = null; } process.exit(0) } // 监听进程退出信号 process.on('SIGINT', cleanup) process.on('SIGTERM', cleanup) const port = Number(process.env.PORT || '7860'); console.log(`Server is running on port http://localhost:${port}`) // 启动服务器 serve({ fetch: app.fetch, port: port })