From 8dd6daccd1e9de0e4849deb4185be31f46da9fad Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Mon, 20 Apr 2026 15:42:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA2925=E9=82=AE?= =?UTF-8?q?=E7=AE=B1=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=82=AE=E4=BB=B6=E9=A2=84=E6=B8=85=E7=A9=BA=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background/verification-flow.js | 54 +++++++++++++++++++ content/mail-2925.js | 20 +++++-- tests/mail-2925-content.test.js | 7 +++ tests/verification-flow-polling.test.js | 70 ++++++++++++++++++++++++- 4 files changed, 146 insertions(+), 5 deletions(-) diff --git a/background/verification-flow.js b/background/verification-flow.js index d751359..f21b143 100644 --- a/background/verification-flow.js +++ b/background/verification-flow.js @@ -237,6 +237,58 @@ return requestedAt; } + function shouldPreclear2925Mailbox(step, mail) { + return mail?.provider === '2925' && (step === 4 || step === 8); + } + + async function clear2925MailboxBeforePolling(step, mail, options = {}) { + if (!shouldPreclear2925Mailbox(step, mail)) { + return; + } + + throwIfStopped(); + await addLog(`步骤 ${step}:开始刷新 2925 邮箱前先清空全部邮件,避免读取旧验证码邮件。`, 'warn'); + + try { + const responseTimeoutMs = await getResponseTimeoutMsForStep( + step, + options, + 15000, + '清空 2925 邮箱历史邮件' + ); + const result = await sendToMailContentScriptResilient( + mail, + { + type: 'DELETE_ALL_EMAILS', + step, + source: 'background', + payload: {}, + }, + { + timeoutMs: responseTimeoutMs, + responseTimeoutMs, + maxRecoveryAttempts: 2, + } + ); + + if (result?.error) { + throw new Error(result.error); + } + + if (result?.deleted === false) { + await addLog(`步骤 ${step}:未能确认 2925 邮箱已清空,将继续刷新等待新邮件。`, 'warn'); + return; + } + + await addLog(`步骤 ${step}:2925 邮箱已预先清空,开始刷新等待新邮件。`, 'info'); + } catch (err) { + if (isStopError(err)) { + throw err; + } + await addLog(`步骤 ${step}:预清空 2925 邮箱失败,将继续刷新等待新邮件:${err.message}`, 'warn'); + } + } + function triggerPostSuccessMailboxCleanup(step, mail) { if (mail?.provider !== '2925') { return; @@ -573,6 +625,8 @@ return nextFilterAfterTimestamp; }; + await clear2925MailboxBeforePolling(step, mail, options); + if (requestFreshCodeFirst) { if (remainingAutomaticResendCount <= 0) { await addLog(`步骤 ${step}:当前自动重新发送验证码次数为 0,将直接使用当前时间窗口轮询邮箱。`, 'info'); diff --git a/content/mail-2925.js b/content/mail-2925.js index 6b7f33f..92ccb2b 100644 --- a/content/mail-2925.js +++ b/content/mail-2925.js @@ -99,8 +99,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { } if (message.type === 'DELETE_ALL_EMAILS') { - Promise.resolve(deleteAllMailboxEmails(message.step)).catch(() => {}); - sendResponse({ ok: true }); + Promise.resolve(deleteAllMailboxEmails(message.step)).then((deleted) => { + sendResponse({ ok: true, deleted }); + }).catch((err) => { + sendResponse({ ok: false, error: err?.message || String(err || '删除邮件失败') }); + }); return true; } @@ -478,6 +481,10 @@ async function openMailAndDeleteAfterRead(item, step) { async function deleteAllMailboxEmails(step) { try { await returnToInbox(); + const initialItems = findMailItems(); + if (initialItems.length === 0) { + return true; + } const selectAllControl = findSelectAllControl(); if (!selectAllControl) { @@ -495,8 +502,15 @@ async function deleteAllMailboxEmails(step) { } simulateClick(deleteButton); + for (let attempt = 0; attempt < 20; attempt += 1) { + await sleep(250); + if (findMailItems().length === 0) { + return true; + } + } + await sleepRandom(200, 500); - return true; + return findMailItems().length === 0; } catch (err) { console.warn(MAIL2925_PREFIX, `Step ${step}: delete-all cleanup failed:`, err?.message || err); return false; diff --git a/tests/mail-2925-content.test.js b/tests/mail-2925-content.test.js index 0b2f037..bbc9900 100644 --- a/tests/mail-2925-content.test.js +++ b/tests/mail-2925-content.test.js @@ -431,12 +431,17 @@ test('deleteAllMailboxEmails selects all messages and clicks delete', async () = const calls = []; const selectAllControl = { kind: 'select-all' }; const deleteButton = { kind: 'delete' }; +let mailboxCleared = false; async function returnToInbox() { calls.push('inbox'); return true; } +function findMailItems() { + return mailboxCleared ? [] : [{ id: 'mail-1' }]; +} + function findSelectAllControl() { return selectAllControl; } @@ -456,11 +461,13 @@ function simulateClick(node) { } if (node === deleteButton) { calls.push('delete'); + mailboxCleared = true; return; } throw new Error('unexpected node'); } +async function sleep() {} async function sleepRandom() {} const console = { warn() {} }; diff --git a/tests/verification-flow-polling.test.js b/tests/verification-flow-polling.test.js index 5cb5b40..70314ed 100644 --- a/tests/verification-flow-polling.test.js +++ b/tests/verification-flow-polling.test.js @@ -111,7 +111,7 @@ test('verification flow runs beforeSubmit hook before filling the code', async ( ]); }); -test('verification flow triggers 2925 mailbox cleanup only after code submission succeeds', async () => { +test('verification flow clears 2925 mailbox before polling and after successful login code submission', async () => { const mailMessages = []; const helpers = api.createVerificationFlowHelpers({ @@ -169,7 +169,73 @@ test('verification flow triggers 2925 mailbox cleanup only after code submission await new Promise((resolve) => setTimeout(resolve, 0)); - assert.deepStrictEqual(mailMessages, ['POLL_EMAIL', 'DELETE_ALL_EMAILS']); + assert.deepStrictEqual(mailMessages, ['DELETE_ALL_EMAILS', 'POLL_EMAIL', 'DELETE_ALL_EMAILS']); +}); + +test('verification flow clears 2925 mailbox before polling and after successful signup code submission', async () => { + const mailMessages = []; + + const helpers = api.createVerificationFlowHelpers({ + addLog: async () => {}, + chrome: { + tabs: { + update: async () => {}, + }, + }, + CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email', + completeStepFromBackground: async () => {}, + confirmCustomVerificationStepBypassRequest: async () => ({ confirmed: true }), + getHotmailVerificationPollConfig: () => ({}), + getHotmailVerificationRequestTimestamp: () => 0, + getState: async () => ({}), + getTabId: async () => 1, + HOTMAIL_PROVIDER: 'hotmail-api', + isStopError: () => false, + LUCKMAIL_PROVIDER: 'luckmail-api', + MAIL_2925_VERIFICATION_INTERVAL_MS: 15000, + MAIL_2925_VERIFICATION_MAX_ATTEMPTS: 15, + pollCloudflareTempEmailVerificationCode: async () => ({}), + pollHotmailVerificationCode: async () => ({}), + pollLuckmailVerificationCode: async () => ({}), + sendToContentScript: async (_source, message) => { + if (message.type === 'FILL_CODE') { + return {}; + } + if (message.type === 'RESEND_VERIFICATION_CODE') { + return {}; + } + return {}; + }, + sendToMailContentScriptResilient: async (_mail, message) => { + mailMessages.push(message.type); + if (message.type === 'POLL_EMAIL') { + return { code: '654321', emailTimestamp: 123 }; + } + return { ok: true, deleted: true }; + }, + setState: async () => {}, + setStepStatus: async () => {}, + sleepWithStop: async () => {}, + throwIfStopped: () => {}, + VERIFICATION_POLL_MAX_ROUNDS: 5, + }); + + await helpers.resolveVerificationStep( + 4, + { + email: 'user@example.com', + mailProvider: '2925', + lastSignupCode: null, + }, + { provider: '2925', label: '2925 邮箱' }, + { + requestFreshCodeFirst: false, + } + ); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + assert.deepStrictEqual(mailMessages, ['DELETE_ALL_EMAILS', 'POLL_EMAIL', 'DELETE_ALL_EMAILS']); }); test('verification flow treats add-phone after login code submit as fatal instead of completing step 8', async () => {