QwenAI / src /browser /browser.js
imseldrith's picture
Initial upload from Google Colab
9de864e verified
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import { saveSession, loadSession, saveAuthToken } from './session.js';
import { checkAuthentication, startManualAuthentication } from './auth.js';
import { clearPagePool, getAuthToken } from '../api/chat.js';
import fs from 'fs';
import path from 'path';
puppeteer.use(StealthPlugin());
let browserInstance = null;
let browserContext = null;
export let isAuthenticated = false;
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
export async function initBrowser(visibleMode = true, skipManualRestart = false) {
if (!browserInstance) {
console.log('Инициализация браузера с Puppeteer Stealth...');
try {
browserInstance = await puppeteer.launch({
headless: !visibleMode,
slowMo: visibleMode ? 30 : 0,
executablePath: process.env.CHROME_PATH || undefined,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled',
'--disable-dev-shm-usage',
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process',
'--window-size=1920,1080',
'--start-maximized',
'--disable-infobars',
'--disable-extensions',
'--disable-gpu',
'--no-first-run',
'--no-default-browser-check',
'--ignore-certificate-errors',
'--ignore-certificate-errors-spki-list'
],
defaultViewport: {
width: 1920,
height: 1080
},
ignoreHTTPSErrors: true
});
const pages = await browserInstance.pages();
const page = pages.length > 0 ? pages[0] : await browserInstance.newPage();
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36');
await page.setViewport({
width: 1920,
height: 1080,
deviceScaleFactor: 1
});
await page.setExtraHTTPHeaders({
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
});
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'platform', {
get: () => 'Win32'
});
Object.defineProperty(navigator, 'hardwareConcurrency', {
get: () => 8
});
Object.defineProperty(navigator, 'deviceMemory', {
get: () => 8
});
Object.defineProperty(navigator, 'plugins', {
get: () => [
{
0: { type: 'application/x-google-chrome-pdf', suffixes: 'pdf', description: 'Portable Document Format' },
description: 'Portable Document Format',
filename: 'internal-pdf-viewer',
length: 1,
name: 'Chrome PDF Plugin'
}
]
});
Object.defineProperty(navigator, 'connection', {
get: () => ({
effectiveType: '4g',
rtt: 50,
downlink: 10,
saveData: false
})
});
if (!navigator.getBattery) {
navigator.getBattery = () => Promise.resolve({
charging: true,
chargingTime: 0,
dischargingTime: Infinity,
level: 1
});
}
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
if (type === 'mousemove' || type === 'mousedown' || type === 'mouseup') {
const wrappedListener = function(event) {
const delay = Math.random() * 3;
setTimeout(() => {
listener.call(this, event);
}, delay);
};
return originalAddEventListener.call(this, type, wrappedListener, options);
}
return originalAddEventListener.call(this, type, listener, options);
};
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = function(type) {
const context = this.getContext('2d');
if (context) {
const imageData = context.getImageData(0, 0, this.width, this.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const noise = Math.floor(Math.random() * 5) - 2;
data[i] = Math.max(0, Math.min(255, data[i] + noise));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise));
}
context.putImageData(imageData, 0, 0);
}
return originalToDataURL.apply(this, arguments);
};
console.log('Puppeteer Stealth активирован');
});
browserContext = page;
console.log('Браузер инициализирован с максимальной защитой от обнаружения');
if (visibleMode) {
await startManualAuthenticationPuppeteer(page, skipManualRestart);
} else {
const sessionLoaded = await loadSessionPuppeteer(page);
if (sessionLoaded) {
setAuthenticationStatus(true);
console.log('Сессия успешно загружена');
}
}
return true;
} catch (error) {
console.error('Ошибка при инициализации браузера:', error);
return false;
}
}
return true;
}
async function saveSessionPuppeteer(page) {
try {
const cookies = await page.cookies();
const sessionDir = path.join(process.cwd(), 'session', 'accounts');
if (!fs.existsSync(sessionDir)) {
fs.mkdirSync(sessionDir, { recursive: true });
}
const accountId = `acc_${Date.now()}`;
const accountDir = path.join(sessionDir, accountId);
if (!fs.existsSync(accountDir)) {
fs.mkdirSync(accountDir, { recursive: true });
}
fs.writeFileSync(
path.join(accountDir, 'cookies.json'),
JSON.stringify(cookies, null, 2)
);
console.log(`Cookies сохранены для аккаунта ${accountId}`);
return accountId;
} catch (error) {
console.error('Ошибка при сохранении сессии:', error);
return null;
}
}
async function startManualAuthenticationPuppeteer(page, skipManualRestart) {
try {
console.log('Открытие страницы для ручной авторизации...');
await page.goto('https://chat.qwen.ai/', {
waitUntil: 'networkidle2',
timeout: 60000
});
await delay(5000);
console.log('------------------------------------------------------');
console.log(' НЕОБХОДИМА АВТОРИЗАЦИЯ');
console.log('------------------------------------------------------');
console.log('Пожалуйста, выполните следующие действия:');
console.log('1. Войдите в систему в открытом браузере');
console.log('2. ВАЖНО: Двигайте мышью естественно, не спешите');
console.log('3. Если появится слайдер капчи - решите её медленно');
console.log('4. Дождитесь полной загрузки главной страницы');
console.log('5. После успешной авторизации нажмите ENTER в консоли');
console.log('------------------------------------------------------');
console.log('После успешной авторизации нажмите ENTER для продолжения...');
await new Promise((resolve) => {
if (process.stdin.isTTY) {
process.stdin.setRawMode(false);
}
process.stdin.resume();
process.stdin.setEncoding('utf8');
const onData = (key) => {
if (key === '\n' || key === '\r' || key.charCodeAt(0) === 13) {
process.stdin.pause();
process.stdin.removeListener('data', onData);
console.log('\nПолучено подтверждение, продолжаем...');
resolve();
}
};
process.stdin.on('data', onData);
});
const cookies = await page.cookies();
console.log(`Сохранено ${cookies.length} cookies`);
const token = await page.evaluate(() => {
return localStorage.getItem('token') ||
localStorage.getItem('auth_token') ||
localStorage.getItem('access_token') ||
sessionStorage.getItem('token') ||
sessionStorage.getItem('auth_token') ||
null;
});
if (token) {
console.log('Токен найден и будет сохранен');
saveAuthToken(token);
} else {
console.log('Токен не найден в localStorage/sessionStorage');
console.log('Попытка извлечь токен из cookies...');
const tokenCookie = cookies.find(c =>
c.name.toLowerCase().includes('token') ||
c.name.toLowerCase().includes('auth')
);
if (tokenCookie) {
console.log(`Токен найден в cookie: ${tokenCookie.name}`);
saveAuthToken(tokenCookie.value);
}
}
const accountId = await saveSessionPuppeteer(page);
if (accountId) {
console.log(`Сессия сохранена с ID: ${accountId}`);
}
setAuthenticationStatus(true);
console.log('Авторизация завершена успешно');
if (!skipManualRestart) {
await restartBrowserInHeadlessMode();
}
} catch (error) {
console.error('Ошибка при ручной авторизации:', error);
throw error;
}
}
async function loadSessionPuppeteer(page) {
try {
return false;
} catch (error) {
console.error('Ошибка при загрузке сессии:', error);
return false;
}
}
export async function restartBrowserInHeadlessMode() {
console.log('Перезапуск браузера в фоновом режиме...');
const token = getAuthToken();
if (token) {
console.log('Сохранение токена...');
saveAuthToken(token);
await delay(1000);
}
await shutdownBrowser();
await delay(2000);
const success = await initBrowser(false);
if (success) {
console.log('Браузер перезапущен в фоновом режиме');
} else {
console.error('Ошибка при перезапуске браузера');
}
}
export async function shutdownBrowser() {
try {
// Сначала очищаем пул страниц
try {
await clearPagePool();
} catch (e) {
console.error('Ошибка при очистке пула страниц:', e);
}
// Закрываем контекст браузера
if (browserInstance) {
try {
const pages = await browserInstance.pages();
for (const page of pages) {
await page.close().catch(() => {});
}
await browserInstance.close();
} catch (e) {
// Игнорируем ошибку, если контекст уже закрыт
console.error('Ошибка при закрытии браузера:', e);
}
}
// Сбрасываем переменные
browserContext = null;
browserInstance = null;
console.log('Браузер закрыт');
} catch (error) {
console.error('Ошибка при завершении работы браузера:', error);
}
}
export function getBrowserContext() {
return browserContext;
}
// Установить статус авторизации
export function setAuthenticationStatus(status) {
isAuthenticated = status;
}
// Получить статус авторизации
export function getAuthenticationStatus() {
return isAuthenticated;
}