import { config } from './config.js'; /** * 设置临时邮箱并获取验证码 * @param {Page} page - Playwright 页面对象 * @returns {Promise<{email: string, code: string}>} */ export async function setupTempMail(page) { console.log('📧 开始设置临时邮箱...'); // 访问 tempmail.plus(增加超时时间,因为网络较慢) await page.goto(config.tempmail.url, { waitUntil: 'domcontentloaded', // 改为 domcontentloaded,不等待所有网络请求 timeout: 60000 // 60秒超时 }); await page.waitForTimeout(3000); // 等待页面稳定 // 输入邮箱用户名(固定为配置的 qichen111) console.log('输入邮箱用户名:', config.tempmail.username); await page.fill('#pre_button', config.tempmail.username); // 点击页面其他位置或按回车来确认输入,触发邮箱地址更新 console.log('确认邮箱用户名...'); await page.click('body'); // 点击页面空白处 await page.waitForTimeout(2000); // 检查 PIN 码弹窗是否已经显示 console.log('[LOG] 检查 PIN 码弹窗状态...'); const pinModal = page.locator('#modal-verify'); await page.waitForTimeout(2000); // 等待页面稳定 const initialModalClass = await pinModal.getAttribute('class'); console.log('[LOG] 弹窗 class:', initialModalClass); // 判断弹窗是否已经显示(包含 "show" class) const isModalShown = initialModalClass && initialModalClass.includes('show'); console.log('[LOG] 弹窗是否已显示:', isModalShown); if (!isModalShown) { // 如果弹窗未显示,才需要点击 PIN 保护元素 console.log('[LOG] 弹窗未显示,点击 PIN 码保护元素...'); const pinProtectSpan = page.locator('span.pin-text[data-tr="box_protected"]'); await pinProtectSpan.waitFor({ state: 'visible', timeout: 15000 }); await pinProtectSpan.click({ force: true }); console.log('[LOG] ✅ 已点击 PIN 保护元素'); await page.waitForTimeout(2000); } else { console.log('[LOG] ✅ 弹窗已自动显示,无需点击'); } // 查找 PIN 码输入框 console.log('[LOG] 查找 PIN 码输入框...'); const pinInput = page.locator('#pin'); const pinInputCount = await pinInput.count(); console.log('[LOG] 找到 PIN 输入框数量:', pinInputCount); if (pinInputCount > 0) { // 检查输入框是否可见 const isVisible = await pinInput.isVisible(); console.log('[LOG] PIN 输入框是否可见:', isVisible); // 输入 PIN 码 console.log('[LOG] 输入 PIN 码:', config.tempmail.pinCode); await pinInput.fill(config.tempmail.pinCode); console.log('[LOG] ✅ PIN 码已输入'); await page.waitForTimeout(500); // 查找提交按钮 console.log('[LOG] 查找提交按钮 #verify...'); const verifyButton = page.locator('#verify'); const verifyButtonCount = await verifyButton.count(); console.log('[LOG] 找到提交按钮数量:', verifyButtonCount); if (verifyButtonCount > 0) { const buttonText = await verifyButton.textContent(); console.log('[LOG] 按钮文本:', buttonText); console.log('[LOG] 点击提交按钮...'); await verifyButton.click(); console.log('[LOG] ✅ 已点击提交按钮'); } else { console.log('[LOG] ⚠️ 未找到提交按钮,尝试按回车'); await pinInput.press('Enter'); } // 等待验证完成 await page.waitForTimeout(2000); console.log('[LOG] ✅ PIN 码验证流程完成'); } else { console.log('[LOG] ❌ 未找到 PIN 输入框!'); throw new Error('未找到 PIN 码输入框'); } // 等待弹窗关闭后获取邮箱地址 await page.waitForTimeout(2000); console.log('[LOG] 获取生成的邮箱地址...'); // 尝试多个可能的邮箱地址元素 let emailAddress = ''; // 方法 1: 查找特定的邮箱显示元素 const emailDisplay = page.locator('#email-address, #mail, .email-address, [id*="email"]').first(); if (await emailDisplay.count() > 0 && await emailDisplay.isVisible()) { const text = await emailDisplay.textContent(); const match = text.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/); if (match) { emailAddress = match[0]; } } // 方法 2: 如果没找到,尝试从页面中提取所有包含 @ 的文本 if (!emailAddress) { const pageContent = await page.content(); const emailMatches = pageContent.match(/qichen111@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/); if (emailMatches && emailMatches.length > 0) { emailAddress = emailMatches[0]; } } // 方法 3: 查找复制按钮附近的文本 if (!emailAddress) { const copyButton = page.locator('button:has-text("复制")').first(); if (await copyButton.count() > 0) { const parent = copyButton.locator('..'); const text = await parent.textContent(); const match = text.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/); if (match) { emailAddress = match[0]; } } } console.log('[LOG] 提取到的邮箱地址:', emailAddress); if (!emailAddress || !emailAddress.includes('@')) { console.log('[LOG] ⚠️ 未能正确获取邮箱地址,尝试使用默认格式'); emailAddress = `${config.tempmail.username}@mailto.plus`; } console.log('✅ 邮箱地址:', emailAddress); return { inboxReady: true, page }; } /** * 等待并获取验证码 * @param {Page} page - Playwright 页面对象 * @returns {Promise} 验证码 */ export async function waitForVerificationCode(page, sentAfterMs) { console.log('⏳ 等待接收验证码邮件...'); console.log('[LOG] 不会刷新页面,只等待新邮件出现...'); const maxWaitTime = 120000; // 最多等待 120 秒 const checkInterval = 1000; // 每 1 秒检查一次 let elapsed = 0; async function extractCodeFromOpenedMail() { // 1) 优先从特定内容区域读取 const contentLoc = page.locator('.mail-content, .email-body, #mail-body, [class*="mail-content"], .pm-text, .view, .message-body').first(); if (await contentLoc.count() > 0) { const text = await contentLoc.textContent(); const code = /\b(\d{6})\b/.exec(text || ''); if (code) return code[1]; } // 2) 遍历所有 iframe 提取文本 const frames = page.frames(); console.log(`[LOG] 当前 frame 数量: ${frames.length}`); for (const f of frames) { try { const body = await f.$('body'); if (!body) continue; const text = await f.evaluate(() => document.body.innerText || ''); const m = /\b(\d{6})\b/.exec(text); if (m) return m[1]; } catch {} } // 3) 退化为整页文本 const all = await page.textContent('body'); const m = /\b(\d{6})\b/.exec(all || ''); return m ? m[1] : null; } while (elapsed < maxWaitTime) { await page.waitForTimeout(checkInterval); elapsed += checkInterval; console.log(`[LOG] 检查邮件... (已等待 ${elapsed / 1000} 秒)`); // 邮件列表行(兼容多种结构) const emailRows = page.locator('.inbox-dataList .mail-item, .inbox-dataList > div, div.row.no-gutters'); const emailCount = await emailRows.count(); console.log(`[LOG] 当前邮件数量: ${emailCount}`); if (emailCount > 0) { console.log('📬 发现邮件,尝试读取...'); // 遍历所有邮件,优先读取发送之后的邮件(按顺序尝试) for (let i = 0; i < emailCount; i++) { const emailRow = emailRows.nth(i); // 过滤发送时间之前的旧邮件(根据 data-date) try { const timeSpan = emailRow.locator('span[data-date]').first(); if (await timeSpan.count() > 0) { const dateStr = await timeSpan.getAttribute('data-date'); if (dateStr) { const parsed = Date.parse(dateStr.replace(' ', 'T')) || new Date(dateStr).getTime(); if (sentAfterMs && parsed && parsed < sentAfterMs) { console.log(`[LOG] 跳过旧邮件 ${dateStr}`); continue; } } } } catch {} try { // 点击打开邮件(有些需要双击/再次点击切换到详情) await emailRow.click({ timeout: 5000 }); await page.waitForTimeout(500); try { await emailRow.dblclick({ timeout: 1000 }); } catch {} // 等待内容区域或 iframe 加载 await page.waitForTimeout(1500); const code = await extractCodeFromOpenedMail(); if (code) { console.log('✅ 获取到验证码:', code); return code; } console.log(`[LOG] 邮件 ${i + 1} 中未找到验证码`); } catch (error) { console.log(`[LOG] 读取邮件 ${i + 1} 失败:`, error.message); } } console.log('⚠️ 所有邮件中未找到验证码,继续等待...'); } } throw new Error('超时:未能在规定时间内收到验证码'); }