fix browser verification

This commit is contained in:
被遗忘的记忆
2025-03-14 05:53:43 +08:00
committed by GitHub
parent d7ae6d4ded
commit e00cdc814c
5 changed files with 2342 additions and 115 deletions
+115 -115
View File
@@ -1,115 +1,115 @@
import os from 'os';
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
export function detectBrowser(preferredBrowser = 'auto') {
const platform = os.platform();
let browsers = {
'chrome': null,
'edge': null
};
if (platform === 'win32') {
browsers.chrome = findWindowsBrowser('Chrome');
browsers.edge = findWindowsBrowser('Edge');
} else if (platform === 'darwin') {
browsers.chrome = findMacOSBrowser('Google Chrome');
browsers.edge = findMacOSBrowser('Microsoft Edge');
} else if (platform === 'linux') {
browsers.chrome = findLinuxBrowser('google-chrome');
//Arch下AUR安装的chrome为google-chrome-stable
if(browsers.chrome == null){
browsers.chrome = findLinuxBrowser('google-chrome-stable');
}
browsers.edge = findLinuxBrowser('microsoft-edge');
}
if (preferredBrowser === 'auto' || preferredBrowser === undefined) {
if (browsers.chrome) {
return browsers.chrome;
} else if (browsers.edge) {
return browsers.edge;
}
} else if (browsers[preferredBrowser]) {
console.log(`使用${preferredBrowser === 'chrome' ? 'Chrome' : 'Edge'}浏览器`);
return browsers[preferredBrowser];
}
console.error('未找到Chrome或Edge浏览器,请确保已安装其中之一');
process.exit(1);
}
function findWindowsBrowser(browserName) {
const regKeys = {
'Chrome': ['chrome.exe', 'Google\\Chrome'],
'Edge': ['msedge.exe', 'Microsoft\\Edge']
};
const [exeName, folderName] = regKeys[browserName];
const regQuery = (key) => {
try {
return execSync(`reg query "${key}" /ve`).toString().trim().split('\r\n').pop().split(' ').pop();
} catch (error) {
return null;
}
};
let browserPath = regQuery(`HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${exeName}`) ||
regQuery(`HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${exeName}`);
if (browserPath && fs.existsSync(browserPath)) {
return browserPath;
}
const commonPaths = [
`C:\\Program Files\\${browserName}\\Application\\${exeName}`,
`C:\\Program Files (x86)\\${browserName}\\Application\\${exeName}`,
`C:\\Program Files (x86)\\Microsoft\\${browserName}\\Application\\${exeName}`,
`${process.env.LOCALAPPDATA}\\${browserName}\\Application\\${exeName}`,
`${process.env.USERPROFILE}\\AppData\\Local\\${browserName}\\Application\\${exeName}`,
];
const foundPath = commonPaths.find(path => fs.existsSync(path));
if (foundPath) {
return foundPath;
}
const userAppDataPath = process.env.LOCALAPPDATA || `${process.env.USERPROFILE}\\AppData\\Local`;
const appDataPath = path.join(userAppDataPath, folderName, 'Application');
if (fs.existsSync(appDataPath)) {
const files = fs.readdirSync(appDataPath);
const exePath = files.find(file => file.toLowerCase() === exeName.toLowerCase());
if (exePath) {
return path.join(appDataPath, exePath);
}
}
return null;
}
function findMacOSBrowser(browserName) {
const paths = [
`/Applications/${browserName}.app/Contents/MacOS/${browserName}`,
`${os.homedir()}/Applications/${browserName}.app/Contents/MacOS/${browserName}`,
];
for (const path of paths) {
if (fs.existsSync(path)) {
return path;
}
}
return null;
}
function findLinuxBrowser(browserName) {
try {
return execSync(`which ${browserName}`).toString().trim();
} catch (error) {
return null;
}
}
import os from 'os';
import fs from 'fs';
import path from 'path';
import {execSync} from 'child_process';
export function detectBrowser(preferredBrowser = 'auto') {
const platform = os.platform();
let browsers = {
'chrome': null,
'edge': null
};
if (platform === 'win32') {
browsers.chrome = findWindowsBrowser('Chrome');
browsers.edge = findWindowsBrowser('Edge');
} else if (platform === 'darwin') {
browsers.chrome = findMacOSBrowser('Google Chrome');
browsers.edge = findMacOSBrowser('Microsoft Edge');
} else if (platform === 'linux') {
browsers.chrome = findLinuxBrowser('google-chrome');
//Arch下AUR安装的chrome为google-chrome-stable
if (browsers.chrome == null) {
browsers.chrome = findLinuxBrowser('google-chrome-stable');
}
browsers.edge = findLinuxBrowser('microsoft-edge');
}
if (preferredBrowser === 'auto' || preferredBrowser === undefined) {
if (browsers.chrome) {
return browsers.chrome;
} else if (browsers.edge) {
return browsers.edge;
}
} else if (browsers[preferredBrowser]) {
console.log(`使用${preferredBrowser === 'chrome' ? 'Chrome' : 'Edge'}浏览器`);
return browsers[preferredBrowser];
}
console.error('未找到Chrome或Edge浏览器,请确保已安装其中之一');
process.exit(1);
}
function findWindowsBrowser(browserName) {
const regKeys = {
'Chrome': ['chrome.exe', 'Google\\Chrome'],
'Edge': ['msedge.exe', 'Microsoft\\Edge']
};
const [exeName, folderName] = regKeys[browserName];
const regQuery = (key) => {
try {
return execSync(`reg query "${key}" /ve`).toString().trim().split('\r\n').pop().split(' ').pop();
} catch (error) {
return null;
}
};
let browserPath = regQuery(`HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${exeName}`) ||
regQuery(`HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${exeName}`);
if (browserPath && fs.existsSync(browserPath)) {
return browserPath;
}
const commonPaths = [
`C:\\Program Files\\${browserName}\\Application\\${exeName}`,
`C:\\Program Files (x86)\\${browserName}\\Application\\${exeName}`,
`C:\\Program Files (x86)\\Microsoft\\${browserName}\\Application\\${exeName}`,
`${process.env.LOCALAPPDATA}\\${browserName}\\Application\\${exeName}`,
`${process.env.USERPROFILE}\\AppData\\Local\\${browserName}\\Application\\${exeName}`,
];
const foundPath = commonPaths.find(path => fs.existsSync(path));
if (foundPath) {
return foundPath;
}
const userAppDataPath = process.env.LOCALAPPDATA || `${process.env.USERPROFILE}\\AppData\\Local`;
const appDataPath = path.join(userAppDataPath, folderName, 'Application');
if (fs.existsSync(appDataPath)) {
const files = fs.readdirSync(appDataPath);
const exePath = files.find(file => file.toLowerCase() === exeName.toLowerCase());
if (exePath) {
return path.join(appDataPath, exePath);
}
}
return null;
}
function findMacOSBrowser(browserName) {
const paths = [
`/Applications/${browserName}.app/Contents/MacOS/${browserName}`,
`${os.homedir()}/Applications/${browserName}.app/Contents/MacOS/${browserName}`,
];
for (const path of paths) {
if (fs.existsSync(path)) {
return path;
}
}
return null;
}
function findLinuxBrowser(browserName) {
try {
return execSync(`which ${browserName}`).toString().trim();
} catch (error) {
return null;
}
}
+206
View File
@@ -0,0 +1,206 @@
/**
* @param {Object} page - Puppeteer
* @param {Object} options - 配置选项
* @param {number} options.width - 视口宽度
* @param {number} options.height - 视口高度
* @param {number} options.deviceScaleFactor - 设备缩放因子
* @param {boolean} options.isMobile - 模拟移动设备
* @param {boolean} options.hasTouch - 支持触摸
* @param {boolean} options.isLandscape - 横屏
* @returns {Promise<void>}
*/
export async function fixBrowserDisplay(page, options = {}) {
if (!page) {
console.error('页面对象为空,无法修复显示');
return;
}
const defaultOptions = {
width: 1280,
height: 800,
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
isLandscape: true
};
const settings = {...defaultOptions, ...options};
try {
// 设置视口大小和设备比例
await page.setViewport({
width: settings.width,
height: settings.height,
deviceScaleFactor: settings.deviceScaleFactor,
isMobile: settings.isMobile,
hasTouch: settings.hasTouch,
isLandscape: settings.isLandscape
});
// 尝试调整窗口大小
const session = await page.target().createCDPSession();
await session.send('Emulation.setDeviceMetricsOverride', {
width: settings.width,
height: settings.height,
deviceScaleFactor: settings.deviceScaleFactor,
mobile: settings.isMobile,
screenWidth: settings.width,
screenHeight: settings.height
});
// 重置页面缩放
await page.evaluate(() => {
document.body.style.zoom = '100%';
document.body.style.transform = 'scale(1)';
document.body.style.transformOrigin = '0 0';
// 尝试修复可能存在的CSS
const styleElement = document.createElement('style');
styleElement.textContent = `
html, body {
width: 100% !important;
height: 100% !important;
overflow: auto !important;
}
.container, .main, #app, #root {
max-width: 100% !important;
width: auto !important;
}
`;
document.head.appendChild(styleElement);
window.dispatchEvent(new Event('resize'));
});
} catch (error) {
console.error('修复浏览器显示时出错:', error);
}
}
/**
* 调整CSS比例
* @param {Object} page - Puppeteer
* @param {number} scale - 缩放比例
* @returns {Promise<void>}
*/
export async function adjustCssScaling(page, scale = 1) {
if (!page) return;
try {
await page.evaluate((scale) => {
const styleElem = document.createElement('style');
styleElem.id = 'puppeteer-display-fix';
styleElem.textContent = `
html {
transform: scale(${scale});
transform-origin: top left;
width: ${100 / scale}% !important;
height: ${100 / scale}% !important;
}
`;
document.head.appendChild(styleElem);
// 重新计算布局
window.dispatchEvent(new Event('resize'));
}, scale);
} catch (error) {
console.error('调整CSS比例时出错:', error);
}
}
/**
* 修复高DPI
* @param {Object} page - Puppeteer
* @returns {Promise<void>}
*/
export async function fixHighDpiDisplay(page) {
if (!page) return;
try {
// 检测设备像素比
const devicePixelRatio = await page.evaluate(() => window.devicePixelRatio);
if (devicePixelRatio > 1) {
await page.setViewport({
width: 1280,
height: 800,
deviceScaleFactor: devicePixelRatio
});
await page.evaluate((dpr) => {
const meta = document.createElement('meta');
meta.setAttribute('name', 'viewport');
meta.setAttribute('content', `initial-scale=1, minimum-scale=1, maximum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi, user-scalable=no`);
document.head.appendChild(meta);
}, devicePixelRatio);
}
} catch (error) {
console.error('修复高DPI显示时出错:', error);
}
}
/**
* 完整浏览器显示优化
* @param {Object} page - Puppeteer
* @param {Object} options - 配置
* @returns {Promise<void>}
*/
export async function optimizeBrowserDisplay(page, options = {}) {
const defaultOptions = {
width: 1280,
height: 800,
deviceScaleFactor: 1,
cssScale: null,
fixHighDpi: true,
forceResize: true
};
const config = {...defaultOptions, ...options};
try {
// 基本显示修复
await fixBrowserDisplay(page, {
width: config.width,
height: config.height,
deviceScaleFactor: config.deviceScaleFactor
});
// 修复高DPI显示
if (config.fixHighDpi) {
await fixHighDpiDisplay(page);
}
if (config.cssScale !== null) {
await adjustCssScaling(page, config.cssScale);
}
// 如果强制调整窗口大小
if (config.forceResize && !config.isHeadless) {
try {
const client = await page.target().createCDPSession();
await client.send('Browser.getWindowForTarget');
await client.send('Browser.setWindowBounds', {
windowId: 1,
bounds: {
width: config.width,
height: config.height
}
});
} catch (resizeError) {
// console.log('无法调整窗口大小:', resizeError.message);
try {
await page.evaluate((width, height) => {
window.resizeTo(width, height);
}, config.width, config.height);
} catch (altError) {
console.log('失败:', altError.message);
}
}
}
} catch (error) {
console.error('浏览器显示优化失败:', error);
}
}
File diff suppressed because it is too large Load Diff
+242
View File
@@ -0,0 +1,242 @@
import * as docx from "docx";
import cookie from "cookie";
import fs from "fs";
import {execSync} from "child_process";
function getGitRevision() {
// get git revision and branch
try {
const revision = execSync("git rev-parse --short HEAD", {stdio: "pipe"}).toString().trim();
const branch = execSync("git rev-parse --abbrev-ref HEAD", {stdio: "pipe"}).toString().trim();
return {revision, branch};
} catch (e) {
return {revision: "unknown", branch: "unknown"};
}
}
// 创建目录
function createDirectoryIfNotExists(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, {recursive: true});
}
}
function extractCookie(cookies) {
let jwtSession = null;
let jwtToken = null;
let ds = null;
let dsr = null;
let you_subscription = null;
let youpro_subscription = null;
cookies = cookie.parse(cookies);
if (cookies["stytch_session"]) jwtSession = cookies["stytch_session"];
if (cookies["stytch_session_jwt"]) jwtToken = cookies["stytch_session_jwt"];
if (cookies["DS"]) ds = cookies["DS"];
if (cookies["DSR"]) dsr = cookies["DSR"];
if (cookies["you_subscription"]) you_subscription = cookies["you_subscription"];
if (cookies["youpro_subscription"]) youpro_subscription = cookies["youpro_subscription"];
return {jwtSession, jwtToken, ds, dsr, you_subscription, youpro_subscription};
}
function getSessionCookie(jwtSession, jwtToken, ds, dsr, you_subscription, youpro_subscription) {
let sessionCookie = [];
// 处理旧版 cookie
if (jwtSession && jwtToken) {
sessionCookie = [
{
name: "stytch_session",
value: jwtSession,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
},
{
name: "ydc_stytch_session",
value: jwtSession,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: true,
secure: true,
sameSite: "Lax",
},
{
name: "stytch_session_jwt",
value: jwtToken,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
},
{
name: "ydc_stytch_session_jwt",
value: jwtToken,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: true,
secure: true,
sameSite: "Lax",
}
];
}
// 处理新版 cookie
if (ds) {
sessionCookie.push({
name: "DS",
value: ds,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
});
}
if (dsr) {
sessionCookie.push({
name: "DSR",
value: dsr,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
});
}
if (you_subscription) {
sessionCookie.push({
name: "you_subscription",
value: you_subscription,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
});
}
if (youpro_subscription) {
sessionCookie.push({
name: "youpro_subscription",
value: youpro_subscription,
domain: "you.com",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
});
}
// 添加隐身模式 cookie(如果启用)
if (process.env.INCOGNITO_MODE === "true") {
sessionCookie.push({
name: "incognito",
value: "true",
domain: "you.com",
path: "/",
expires: 1800000000,
secure: true,
});
}
return sessionCookie;
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function createDocx(content) {
let paragraphs = [];
content.split("\n").forEach((line) => {
paragraphs.push(
new docx.Paragraph({
children: [new docx.TextRun(line)],
})
);
});
let doc = new docx.Document({
sections: [
{
properties: {},
children: paragraphs,
},
],
});
return docx.Packer.toBuffer(doc).then((buffer) => buffer);
}
// eventStream util
function createEvent(event, data) {
// if data is object, stringify it
if (typeof data === "object") {
data = JSON.stringify(data);
}
return `event: ${event}\ndata: ${data}\n\n`;
}
function extractPerplexityCookie(cookieString) {
const cookies = cookie.parse(cookieString);
return {
sessionToken: cookies['__Secure-next-auth.session-token'],
isIncognito: cookies['pplx.is-incognito'] === 'true'
};
}
function getPerplexitySessionCookie(extractedCookie) {
let sessionCookie = [];
if (extractedCookie.sessionToken) {
sessionCookie.push({
name: "__Secure-next-auth.session-token",
value: extractedCookie.sessionToken,
domain: "www.perplexity.ai",
path: "/",
expires: 1800000000,
httpOnly: true,
secure: true,
sameSite: "Lax",
});
}
// 添加无痕模式 cookie (如果启用)
if (process.env.INCOGNITO_MODE === "true") {
sessionCookie.push({
name: "pplx.is-incognito",
value: "true",
domain: "www.perplexity.ai",
path: "/",
expires: 1800000000,
httpOnly: false,
secure: true,
sameSite: "Lax",
});
}
return sessionCookie;
}
export {
createEvent,
createDirectoryIfNotExists,
sleep,
extractCookie,
getSessionCookie,
createDocx,
getGitRevision,
extractPerplexityCookie,
getPerplexitySessionCookie
};
+153
View File
@@ -0,0 +1,153 @@
import {exec, execSync} from 'child_process';
import {promisify} from 'util';
import fs from 'fs';
import path from 'path';
import os from 'os';
import {fileURLToPath} from 'url';
import {setupBrowserFingerprint} from './browserFingerprint.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const execPromise = promisify(exec);
/**
* @param {string} userDataDir - 用户数据目录
* @param {string} edgePath - Edge浏览器路径
* @param {number} debugPort - 调试端口
* @returns {Promise<object>} - browser和page
*/
export async function launchEdgeBrowser(userDataDir, edgePath, debugPort = 9222) {
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir, {recursive: true});
}
// 关闭可能的Edge进程
try {
if (os.platform() === 'win32') {
await execPromise('taskkill /f /im msedge.exe').catch(() => {
});
} else if (os.platform() === 'darwin') {
// macOS
await execPromise('pkill -f "Microsoft Edge"').catch(() => {
});
} else {
// Linux
await execPromise('pkill -f "microsoft-edge"').catch(() => {
});
}
} catch (e) {
}
const remoteDebuggingPort = debugPort;
let edgeProcess;
const args = [
`--remote-debugging-port=${remoteDebuggingPort}`,
`--user-data-dir="${userDataDir}"`,
'--no-first-run',
'--no-default-browser-check',
'--disable-popup-blocking',
'--disable-infobars',
'--disable-translate',
'--disable-sync',
'--window-size=1280,850',
'--force-device-scale-factor=1',
'about:blank' // 打开空白页
];
try {
// 启动Edge浏览器
const cmdArgs = args.join(' ');
const cmd = `"${edgePath}" ${cmdArgs}`;
edgeProcess = exec(cmd);
console.log(`等待Edge浏览器启动...`);
await new Promise(resolve => setTimeout(resolve, 3000));
const puppeteer = await import('puppeteer-core');
// 连接到浏览器
const browser = await puppeteer.connect({
browserURL: `http://127.0.0.1:${remoteDebuggingPort}`,
defaultViewport: {width: 1280, height: 850}
});
// 获取第一个页面
const pages = await browser.pages();
let page = pages[0];
if (!page) {
page = await browser.newPage();
}
const originalUserAgent = await page.evaluate(() => navigator.userAgent);
// console.log(`Edge浏览器原始用户代理: ${originalUserAgent}`);
// 应用随机指纹
const fingerprint = await setupBrowserFingerprint(page, 'edge');
// 验证指纹是否成功应用
const newUserAgent = await page.evaluate(() => navigator.userAgent);
// console.log(`Edge浏览器应用指纹后用户代理: ${newUserAgent}`);
if (!newUserAgent.includes('Edg')) {
console.warn(`警告: 浏览器可能不是Edge。`);
}
return {
browser,
page,
process: edgeProcess,
fingerprint: fingerprint
};
} catch (error) {
console.error(`启动Edge浏览器失败:`, error);
if (edgeProcess) {
try {
edgeProcess.kill();
} catch (e) {
}
}
throw error;
}
}
/**
* 查找Edge浏览器路径
* @returns {string|null}
*/
export function findEdgePath() {
const platform = os.platform();
if (platform === 'win32') {
const commonPaths = [
`${process.env['ProgramFiles(x86)']}\\Microsoft\\Edge\\Application\\msedge.exe`,
`${process.env.ProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
`${process.env.LOCALAPPDATA}\\Microsoft\\Edge\\Application\\msedge.exe`,
`C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe`,
`C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe`
];
for (const path of commonPaths) {
if (fs.existsSync(path)) {
return path;
}
}
} else if (platform === 'darwin') {
const macPath = '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge';
if (fs.existsSync(macPath)) {
return macPath;
}
} else {
// Linux
try {
const {stdout} = execSync('which microsoft-edge');
if (stdout && stdout.trim()) {
return stdout.trim();
}
} catch (e) {
}
}
return null;
}