'use strict'; const path = require('path'); const { chromium } = require('playwright'); const db = require('../src/core/database'); const { openBrowserWithRetry, closeBrowser } = require('../src/core/bit_api'); const { createGmailAppPassword } = require('../src/services/gmail_app_password_service'); async function inspectPage(page, email) { return page.evaluate(targetEmail => { const visible = element => { const style = window.getComputedStyle(element); return style.display !== 'none' && style.visibility !== 'hidden'; }; const clean = value => String(value || '') .replace(new RegExp(targetEmail.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'ig'), '[email]') .replace(/\s+/g, ' ') .trim(); return { title: document.title, bodyText: clean(document.body && document.body.innerText).slice(0, 1200), inputs: [...document.querySelectorAll('input')].filter(visible).map(input => ({ type: input.type, name: input.name, id: input.id, ariaLabel: input.getAttribute('aria-label'), placeholder: input.getAttribute('placeholder'), hasValue: !!input.value, })), buttons: [...document.querySelectorAll('button, [role="button"]')].filter(visible) .map(button => clean(button.innerText || button.getAttribute('aria-label'))) .filter(Boolean) .slice(0, 30), }; }, email).catch(() => ({ title: '', bodyText: '', inputs: [], buttons: [] })); } async function main() { const email = String(process.argv[2] || '').trim(); if (!email) throw new Error('用法: node scripts/obtain_gmail_app_password.js '); db.initDB(); const account = db.getAllAccounts().find(item => String(item.email || '').toLowerCase() === email.toLowerCase()); if (!account) throw new Error('数据库中不存在该邮箱'); if (!account.browser_id) throw new Error('该邮箱没有浏览器编号'); if (db.hasGmailAppPassword(email)) { console.log(JSON.stringify({ ok: true, alreadySaved: true, email })); return; } const opened = await openBrowserWithRetry(account.browser_id, { email, logCallback: message => console.log(message), }); if (!opened || !opened.success || !opened.data || !opened.data.ws) { throw new Error(`打开比特浏览器失败:${opened && opened.msg ? opened.msg : '无 ws 端点'}`); } let browser; let page; let succeeded = false; try { browser = await chromium.connectOverCDP(opened.data.ws); const context = browser.contexts()[0]; page = await context.newPage(); await page.bringToFront().catch(() => {}); const result = await createGmailAppPassword(page, account, message => console.log(message), { appName: 'zhaoqiaoqing2001', timeoutMs: 60000, captchaWaitMs: 5 * 60 * 1000, }); if (result.ok && result.appPassword) { db.upsertGmailAppPassword(email, result.appPassword); succeeded = true; console.log(JSON.stringify({ ok: true, saved: true, email, passwordLength: result.appPassword.length })); return; } const inspection = await inspectPage(page, email); const screenshotPath = path.join( db.getDb().name ? path.dirname(db.getDb().name) : path.join(__dirname, '..', 'data'), 'screenshots', `gmail_app_password_${email.replace(/[^a-z0-9]+/gi, '_')}.png`, ); await page.screenshot({ path: screenshotPath, fullPage: true }).catch(() => {}); console.log(JSON.stringify({ ok: false, email, message: result.msg, url: page.url(), inspection, screenshotPath, })); process.exitCode = 2; } finally { if (browser) await browser.close().catch(() => {}); if (succeeded) await closeBrowser(account.browser_id).catch(() => {}); } } main().catch(error => { console.error(JSON.stringify({ ok: false, message: error.message })); process.exitCode = 1; });