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; }