From 9b38b0bffe1ca82a880fe572f384588914989e01 Mon Sep 17 00:00:00 2001 From: zhangkun <1184253234@qq.com> Date: Mon, 27 Apr 2026 22:33:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20LuckMail=20=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E8=BD=AE=E8=AF=A2=E4=B8=8E=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 87 ++++++++++++++++++++++++--- background/steps/fetch-login-code.js | 8 ++- background/steps/fetch-signup-code.js | 9 ++- background/verification-flow.js | 8 ++- 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/background.js b/background.js index 97b85b1..e62b730 100644 --- a/background.js +++ b/background.js @@ -3509,21 +3509,16 @@ async function resolveLuckmailVerificationMail(client, token, filters = {}, toke return match || null; } -async function pollLuckmailVerificationCode(step, state, pollPayload = {}) { +async function legacyPollLuckmailVerificationCode(step, state, pollPayload = {}) { const purchase = getCurrentLuckmailPurchase(state); if (!purchase?.token) { throw new Error('LuckMail 当前没有可用 token,请先执行步骤 3 购买邮箱。'); } const client = createLuckmailClient(state); - const maxAttempts = Math.max(1, Number(pollPayload.maxAttempts) || 5); - const intervalMs = Math.max(1000, Number(pollPayload.intervalMs) || 3000); - const filters = { - afterTimestamp: pollPayload.filterAfterTimestamp || 0, - senderFilters: pollPayload.senderFilters || [], - subjectFilters: pollPayload.subjectFilters || [], - excludeCodes: pollPayload.excludeCodes || [], - }; + const maxAttempts = Math.max(1, Number(pollPayload.maxAttempts) || 3); + const intervalMs = Math.max(15000, Number(pollPayload.intervalMs) || 15000); + const excludedCodes = new Set((pollPayload.excludeCodes || []).filter(Boolean)); const initialCursor = normalizeLuckmailMailCursor((await getState()).currentLuckmailMailCursor); if (!initialCursor.messageId && !initialCursor.receivedAt) { @@ -3587,6 +3582,80 @@ async function pollLuckmailVerificationCode(step, state, pollPayload = {}) { throw lastError || new Error(`步骤 ${step}:未在 LuckMail 邮箱中找到新的匹配验证码。`); } +async function pollLuckmailVerificationCode(step, state, pollPayload = {}) { + const purchase = getCurrentLuckmailPurchase(state); + if (!purchase?.token) { + throw new Error('LuckMail 当前没有可用 token,请先执行步骤 3 购买邮箱。'); + } + + const client = createLuckmailClient(state); + const maxAttempts = Math.max(1, Number(pollPayload.maxAttempts) || 3); + const intervalMs = Math.max(15000, Number(pollPayload.intervalMs) || 15000); + const excludedCodes = new Set((pollPayload.excludeCodes || []).filter(Boolean)); + + const initialCursor = normalizeLuckmailMailCursor((await getState()).currentLuckmailMailCursor); + if (!initialCursor.messageId && !initialCursor.receivedAt) { + const mailList = await client.user.getTokenMails(purchase.token); + const baselineCursor = buildLuckmailBaselineCursor(mailList?.mails || []); + await setLuckmailMailCursorState(baselineCursor); + if (baselineCursor?.messageId || baselineCursor?.receivedAt) { + await addLog(`步骤 ${step}:LuckMail 已保存当前邮箱旧邮件快照,后续仅使用新收到的验证码。`, 'info'); + } + } + + let lastError = null; + for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { + throwIfStopped(); + await addLog(`步骤 ${step}:正在通过 LuckMail /code 接口轮询验证码(${attempt}/${maxAttempts})...`, 'info'); + + try { + const tokenCode = await client.user.getTokenCode(purchase.token); + const remoteEmail = String(tokenCode?.email_address || '').trim().toLowerCase(); + const expectedEmail = String(purchase.email_address || state?.email || '').trim().toLowerCase(); + if (remoteEmail && expectedEmail && remoteEmail !== expectedEmail) { + throw new Error(`步骤 ${step}:LuckMail token 对应邮箱与当前邮箱不一致。当前邮箱:${expectedEmail};token 邮箱:${remoteEmail}`); + } + + const tokenMail = tokenCode.verification_code && tokenCode.mail && !tokenCode.mail.verification_code + ? { + ...tokenCode.mail, + verification_code: tokenCode.verification_code, + } + : tokenCode.mail; + const code = String(tokenCode?.verification_code || tokenMail?.verification_code || '').trim(); + const cursor = normalizeLuckmailMailCursor((await getState()).currentLuckmailMailCursor); + + if (!code || !tokenMail) { + lastError = new Error(`步骤 ${step}:LuckMail /code 接口暂未返回新的验证码。`); + } else if (excludedCodes.has(code)) { + lastError = new Error(`步骤 ${step}:LuckMail 返回的验证码 ${code} 已试过,等待 15 秒后再次轮询。`); + } else if (!isLuckmailMailNewerThanCursor(tokenMail, cursor)) { + lastError = new Error(`步骤 ${step}:LuckMail /code 返回的最新邮件仍是旧验证码。`); + } else { + await setLuckmailMailCursorState(buildLuckmailMailCursor(tokenMail)); + return { + ok: true, + code, + emailTimestamp: normalizeLuckmailTimestamp(tokenMail.received_at) || Date.now(), + mailId: tokenMail.message_id, + }; + } + } catch (err) { + if (isStopError(err)) { + throw err; + } + lastError = err; + await addLog(`步骤 ${step}:LuckMail /code 轮询失败:${err.message}`, 'warn'); + } + + if (attempt < maxAttempts) { + await sleepWithStop(intervalMs); + } + } + + throw lastError || new Error(`步骤 ${step}:未在 LuckMail /code 接口中获取到新的验证码。`); +} + function summarizeCloudflareTempEmailMessagesForLog(messages) { return (messages || []) .slice() diff --git a/background/steps/fetch-login-code.js b/background/steps/fetch-login-code.js index 0c1535f..0af5cd4 100644 --- a/background/steps/fetch-login-code.js +++ b/background/steps/fetch-login-code.js @@ -268,9 +268,11 @@ getRemainingTimeMs: getStep8RemainingTimeResolver(state?.oauthUrl || '', visibleStep), requestFreshCodeFirst: false, targetEmail: fixedTargetEmail, - resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925') - ? 0 - : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS, + resendIntervalMs: mail.provider === LUCKMAIL_PROVIDER + ? 15000 + : ((mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925') + ? 0 + : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS), }); } diff --git a/background/steps/fetch-signup-code.js b/background/steps/fetch-signup-code.js index 88778a3..fc0a646 100644 --- a/background/steps/fetch-signup-code.js +++ b/background/steps/fetch-signup-code.js @@ -149,6 +149,7 @@ const shouldRequestFreshCodeFirst = ![ HOTMAIL_PROVIDER, + LUCKMAIL_PROVIDER, CLOUDFLARE_TEMP_EMAIL_PROVIDER, ].includes(mail.provider); @@ -157,9 +158,11 @@ sessionKey: verificationSessionKey, disableTimeBudgetCap: mail.provider === '2925', requestFreshCodeFirst: shouldRequestFreshCodeFirst, - resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925') - ? 0 - : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS, + resendIntervalMs: mail.provider === LUCKMAIL_PROVIDER + ? 15000 + : ((mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925') + ? 0 + : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS), }); } diff --git a/background/verification-flow.js b/background/verification-flow.js index 7e8ea92..dcade7a 100644 --- a/background/verification-flow.js +++ b/background/verification-flow.js @@ -917,7 +917,7 @@ getLegacyVerificationResendCountDefault(step, { requestFreshCodeFirst }) ) : getConfiguredVerificationResendCount(step, state, { requestFreshCodeFirst }); - const maxSubmitAttempts = 15; + const maxSubmitAttempts = mail.provider === LUCKMAIL_PROVIDER ? 3 : 15; const resendIntervalMs = Math.max(0, Number(options.resendIntervalMs) || 0); let lastResendAt = Number(options.lastResendAt) || 0; @@ -1002,6 +1002,12 @@ throw new Error(`步骤 ${step}:验证码连续失败,已达到 ${maxSubmitAttempts} 次重试上限。`); } + if (mail.provider === LUCKMAIL_PROVIDER) { + await addLog(`步骤 ${step}:LuckMail 验证码提交失败,等待 15 秒后重新轮询 /code 接口(${attempt + 1}/${maxSubmitAttempts})...`, 'warn'); + await sleepWithStop(15000); + continue; + } + const remainingBeforeResendMs = resendIntervalMs > 0 && lastResendAt > 0 ? Math.max(0, resendIntervalMs - (Date.now() - lastResendAt)) : 0;