From b71bccaf8ad4c7477a616afffdc846df218ff27d Mon Sep 17 00:00:00 2001 From: Isulew <224964+netcookies@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:57:51 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=202925=20?= =?UTF-8?q?=E6=94=B6=E4=BB=B6=E6=A8=A1=E5=BC=8F=E8=BD=AE=E8=AF=A2=E4=B8=8E?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - receive 模式下跳过 2925 邮箱页顶部账号校验 - 解析 bounce 转发地址与中文日期,避免误过滤验证码邮件 - 为内容脚本增加重复注入保护,避免重复声明导致轮询失效 --- background/steps/fetch-login-code.js | 4 ++ background/steps/fetch-signup-code.js | 4 ++ content/mail-2925.js | 60 ++++++++++++++++++++++++++- content/signup-page.js | 2 + content/utils.js | 10 ++--- tests/mail-2925-content.test.js | 1 + 6 files changed, 74 insertions(+), 7 deletions(-) diff --git a/background/steps/fetch-login-code.js b/background/steps/fetch-login-code.js index c25b2f4..41b0e3b 100644 --- a/background/steps/fetch-login-code.js +++ b/background/steps/fetch-login-code.js @@ -69,6 +69,10 @@ } function getExpectedMail2925MailboxEmail(state = {}) { + if (String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive') { + return ''; + } + if (Boolean(state?.mail2925UseAccountPool)) { const currentAccountId = String(state?.currentMail2925AccountId || '').trim(); const accounts = Array.isArray(state?.mail2925Accounts) ? state.mail2925Accounts : []; diff --git a/background/steps/fetch-signup-code.js b/background/steps/fetch-signup-code.js index 88778a3..791d0c2 100644 --- a/background/steps/fetch-signup-code.js +++ b/background/steps/fetch-signup-code.js @@ -26,6 +26,10 @@ } = deps; function getExpectedMail2925MailboxEmail(state = {}) { + if (String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive') { + return ''; + } + if (Boolean(state?.mail2925UseAccountPool)) { const currentAccountId = String(state?.currentMail2925AccountId || '').trim(); const accounts = Array.isArray(state?.mail2925Accounts) ? state.mail2925Accounts : []; diff --git a/content/mail-2925.js b/content/mail-2925.js index 1e698c4..4b723e2 100644 --- a/content/mail-2925.js +++ b/content/mail-2925.js @@ -1,6 +1,13 @@ // content/mail-2925.js - Content script for 2925 Mail (steps 4, 8) // Injected dynamically on: 2925.com +(function initMail2925ContentScript() { +if (window.__MULTIPAGE_MAIL2925_SCRIPT_LOADED__) { + console.log('[MultiPage:mail-2925] Duplicate injection skipped on', location.href); + return; +} +window.__MULTIPAGE_MAIL2925_SCRIPT_LOADED__ = true; + const MAIL2925_PREFIX = '[MultiPage:mail-2925]'; const isTopFrame = window === window.top; @@ -699,10 +706,29 @@ function extractEmails(text = '') { return [...new Set(matches.map((item) => item.toLowerCase()))]; } +function extractForwardedTargetEmails(text = '') { + const normalizedText = String(text || '').toLowerCase(); + const matches = normalizedText.match(/bounce\+[a-z0-9._%+-]*-([a-z0-9._%+-]+)=([a-z0-9.-]+\.[a-z]{2,})@(?:tm\d*\.openai\.com|em\d+\.tm\.openai\.com)/gi) || []; + const decoded = matches + .map((candidate) => { + const match = String(candidate || '').match(/bounce\+[a-z0-9._%+-]*-([a-z0-9._%+-]+)=([a-z0-9.-]+\.[a-z]{2,})@/i); + if (!match) { + return ''; + } + return `${match[1].toLowerCase()}@${match[2].toLowerCase()}`; + }) + .filter(Boolean); + return [...new Set(decoded)]; +} + function emailMatchesTarget(candidate, targetEmail) { const normalizedCandidate = String(candidate || '').trim().toLowerCase(); const normalizedTarget = String(targetEmail || '').trim().toLowerCase(); - return Boolean(normalizedCandidate && normalizedTarget && normalizedCandidate === normalizedTarget); + if (!normalizedCandidate || !normalizedTarget) { + return false; + } + + return normalizedCandidate === normalizedTarget; } function getTargetEmailMatchState(text, targetEmail) { @@ -717,12 +743,30 @@ function getTargetEmailMatchState(text, targetEmail) { } const extractedEmails = extractEmails(normalizedText); + const forwardedTargetEmails = extractForwardedTargetEmails(normalizedText); if (!extractedEmails.length) { + return forwardedTargetEmails.length + ? { + matches: forwardedTargetEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)), + hasExplicitEmail: true, + } + : { matches: true, hasExplicitEmail: false }; + } + + const targetDomain = normalizedTarget.includes('@') + ? normalizedTarget.split('@').pop() + : ''; + const comparableEmails = [...new Set( + (targetDomain + ? [...extractedEmails, ...forwardedTargetEmails].filter((candidate) => String(candidate || '').trim().toLowerCase().endsWith(`@${targetDomain}`)) + : [...extractedEmails, ...forwardedTargetEmails]) + )]; + if (!comparableEmails.length) { return { matches: true, hasExplicitEmail: false }; } return { - matches: extractedEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)), + matches: comparableEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)), hasExplicitEmail: true, }; } @@ -782,6 +826,17 @@ function parseMailItemTimestamp(item) { return date.getTime(); } + match = timeText.match(/(\d{1,2})月(\d{1,2})日(?:\s*(\d{1,2}):(\d{2}))?/); + if (match) { + date.setMonth(Number(match[1]) - 1, Number(match[2])); + if (match[3] && match[4]) { + date.setHours(Number(match[3]), Number(match[4]), 0, 0); + } else { + date.setHours(0, 0, 0, 0); + } + return date.getTime(); + } + match = timeText.match(/(\d{4})-(\d{1,2})-(\d{1,2})\s*(\d{1,2}):(\d{2})/); if (match) { return new Date( @@ -1168,3 +1223,4 @@ async function handlePollEmail(step, payload) { } } +})(); diff --git a/content/signup-page.js b/content/signup-page.js index f36207b..c742b51 100644 --- a/content/signup-page.js +++ b/content/signup-page.js @@ -2,6 +2,7 @@ // Injected on: auth0.openai.com, auth.openai.com, accounts.openai.com // Dynamically injected on: chatgpt.com +(function initSignupPageContentScript() { console.log('[MultiPage:signup-page] Content script loaded on', location.href); const SIGNUP_PAGE_LISTENER_SENTINEL = 'data-multipage-signup-page-listener'; @@ -3560,3 +3561,4 @@ async function step5_fillNameBirthday(payload) { log('步骤 5:已点击“完成帐户创建”,当前步骤直接完成,不再等待页面结果。'); return completionPayload; } +})(); diff --git a/content/utils.js b/content/utils.js index f208aca..71e711e 100644 --- a/content/utils.js +++ b/content/utils.js @@ -1,6 +1,6 @@ // content/utils.js — Shared utilities for all content scripts -const getActivationStrategy = self.MultiPageActivationUtils?.getActivationStrategy; +var getActivationStrategy = self.MultiPageActivationUtils?.getActivationStrategy; function detectScriptSource({ injectedSource, @@ -26,7 +26,7 @@ function detectScriptSource({ return 'vps-panel'; } -const SCRIPT_SOURCE = (() => { +var SCRIPT_SOURCE = (() => { return detectScriptSource({ injectedSource: window.__MULTIPAGE_SOURCE, url: location.href, @@ -38,9 +38,9 @@ function getRuntimeScriptSource() { return window.__MULTIPAGE_SOURCE || SCRIPT_SOURCE; } -const LOG_PREFIX = `[MultiPage:${SCRIPT_SOURCE}]`; -const STOP_ERROR_MESSAGE = '流程已被用户停止。'; -let flowStopped = false; +var LOG_PREFIX = `[MultiPage:${SCRIPT_SOURCE}]`; +var STOP_ERROR_MESSAGE = '流程已被用户停止。'; +var flowStopped = false; if (!window.__MULTIPAGE_UTILS_LISTENER_READY__) { window.__MULTIPAGE_UTILS_LISTENER_READY__ = true; diff --git a/tests/mail-2925-content.test.js b/tests/mail-2925-content.test.js index 29f5306..c824ee0 100644 --- a/tests/mail-2925-content.test.js +++ b/tests/mail-2925-content.test.js @@ -319,6 +319,7 @@ return { test('handlePollEmail skips explicit mismatched target emails when receive-mode matching is enabled', async () => { const bundle = [ extractFunction('extractEmails'), + extractFunction('extractForwardedTargetEmails'), extractFunction('emailMatchesTarget'), extractFunction('getTargetEmailMatchState'), extractFunction('normalizeMinuteTimestamp'), From 941102200fe2e96ec05102e465294bd3b2457e0a Mon Sep 17 00:00:00 2001 From: Isulew <224964+netcookies@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:15:06 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E6=94=BE=E5=BC=80=202925=20?= =?UTF-8?q?=E5=85=A8=E6=A8=A1=E5=BC=8F=E7=9B=AE=E6=A0=87=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 让 2925 提供邮箱与接收邮箱模式都启用 targetEmail 弱匹配 - 同步更新轮询测试,验证 provide 与 receive 模式行为一致 --- background/verification-flow.js | 3 +-- tests/verification-flow-polling.test.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/background/verification-flow.js b/background/verification-flow.js index 229bed7..84bdfb5 100644 --- a/background/verification-flow.js +++ b/background/verification-flow.js @@ -214,8 +214,7 @@ function getVerificationPollPayload(step, state, overrides = {}) { const is2925Provider = state?.mailProvider === '2925'; - const mail2925MatchTargetEmail = is2925Provider - && String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive'; + const mail2925MatchTargetEmail = is2925Provider; if (step === 4) { return { filterAfterTimestamp: is2925Provider ? 0 : getHotmailVerificationRequestTimestamp(4, state), diff --git a/tests/verification-flow-polling.test.js b/tests/verification-flow-polling.test.js index 426b828..d241d93 100644 --- a/tests/verification-flow-polling.test.js +++ b/tests/verification-flow-polling.test.js @@ -43,7 +43,7 @@ test('verification flow keeps 2925 polling cadence in the default payload', () = assert.equal(step8Payload.intervalMs, 15000); }); -test('verification flow only enables 2925 target email matching in receive mode', () => { +test('verification flow enables 2925 target email matching in both provide and receive modes', () => { const helpers = api.createVerificationFlowHelpers({ addLog: async () => {}, chrome: { tabs: { update: async () => {} } }, @@ -82,7 +82,7 @@ test('verification flow only enables 2925 target email matching in receive mode' mail2925Mode: 'receive', }); - assert.equal(providePayload.mail2925MatchTargetEmail, false); + assert.equal(providePayload.mail2925MatchTargetEmail, true); assert.equal(receivePayload.mail2925MatchTargetEmail, true); }); From cf2a720531732c01fe4a38bbc15db4f75a73a90e Mon Sep 17 00:00:00 2001 From: Isulew <224964+netcookies@users.noreply.github.com> Date: Tue, 28 Apr 2026 00:50:29 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E6=94=B6=E6=95=9B=202925=20?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E4=BF=AE=E5=A4=8D=E8=8C=83=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 回退非必要的 2925 行为改动,仅保留已验证有效的修复 - 保留 bounce 转发地址解析、中文日期解析与邮箱页复用时的 ready 恢复 - 同步最小化测试断言,保持当前代码与测试一致 --- background/mail-2925-session.js | 20 +++++++++---------- background/steps/fetch-login-code.js | 4 ---- background/steps/fetch-signup-code.js | 4 ---- background/verification-flow.js | 3 ++- content/mail-2925.js | 8 -------- content/signup-page.js | 2 -- content/utils.js | 10 +++++----- ...background-mail2925-entry-behavior.test.js | 2 +- tests/verification-flow-polling.test.js | 4 ++-- 9 files changed, 20 insertions(+), 37 deletions(-) diff --git a/background/mail-2925-session.js b/background/mail-2925-session.js index 502362a..e80dc4c 100644 --- a/background/mail-2925-session.js +++ b/background/mail-2925-session.js @@ -537,6 +537,16 @@ } } + if (typeof ensureContentScriptReadyOnTab === 'function') { + await ensureContentScriptReadyOnTab(MAIL2925_SOURCE, tabId, { + inject: MAIL2925_INJECT, + injectSource: MAIL2925_INJECT_SOURCE, + timeoutMs: 20000, + retryDelayMs: 800, + logMessage: '步骤 0:2925 登录页内容脚本未就绪,正在等待页面稳定后继续登录...', + }); + } + if (!forceRelogin && !isMail2925LoginUrl(openedUrl) && !normalizedExpectedMailboxEmail) { await addLog('2925:当前邮箱页未跳转到登录页,将直接复用已登录会话。', 'info'); return buildSuccessPayload(); @@ -553,16 +563,6 @@ }); } - if (typeof ensureContentScriptReadyOnTab === 'function') { - await ensureContentScriptReadyOnTab(MAIL2925_SOURCE, tabId, { - inject: MAIL2925_INJECT, - injectSource: MAIL2925_INJECT_SOURCE, - timeoutMs: 20000, - retryDelayMs: 800, - logMessage: '步骤 0:2925 登录页内容脚本未就绪,正在等待页面稳定后继续登录...', - }); - } - if (forceRelogin && typeof sleepWithStop === 'function') { await addLog('2925:登录页已打开,等待 3 秒后开始检查输入框并执行登录...', 'info'); await sleepWithStop(3000); diff --git a/background/steps/fetch-login-code.js b/background/steps/fetch-login-code.js index 41b0e3b..c25b2f4 100644 --- a/background/steps/fetch-login-code.js +++ b/background/steps/fetch-login-code.js @@ -69,10 +69,6 @@ } function getExpectedMail2925MailboxEmail(state = {}) { - if (String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive') { - return ''; - } - if (Boolean(state?.mail2925UseAccountPool)) { const currentAccountId = String(state?.currentMail2925AccountId || '').trim(); const accounts = Array.isArray(state?.mail2925Accounts) ? state.mail2925Accounts : []; diff --git a/background/steps/fetch-signup-code.js b/background/steps/fetch-signup-code.js index 791d0c2..88778a3 100644 --- a/background/steps/fetch-signup-code.js +++ b/background/steps/fetch-signup-code.js @@ -26,10 +26,6 @@ } = deps; function getExpectedMail2925MailboxEmail(state = {}) { - if (String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive') { - return ''; - } - if (Boolean(state?.mail2925UseAccountPool)) { const currentAccountId = String(state?.currentMail2925AccountId || '').trim(); const accounts = Array.isArray(state?.mail2925Accounts) ? state.mail2925Accounts : []; diff --git a/background/verification-flow.js b/background/verification-flow.js index 84bdfb5..229bed7 100644 --- a/background/verification-flow.js +++ b/background/verification-flow.js @@ -214,7 +214,8 @@ function getVerificationPollPayload(step, state, overrides = {}) { const is2925Provider = state?.mailProvider === '2925'; - const mail2925MatchTargetEmail = is2925Provider; + const mail2925MatchTargetEmail = is2925Provider + && String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive'; if (step === 4) { return { filterAfterTimestamp: is2925Provider ? 0 : getHotmailVerificationRequestTimestamp(4, state), diff --git a/content/mail-2925.js b/content/mail-2925.js index 4b723e2..d2ba01a 100644 --- a/content/mail-2925.js +++ b/content/mail-2925.js @@ -1,13 +1,6 @@ // content/mail-2925.js - Content script for 2925 Mail (steps 4, 8) // Injected dynamically on: 2925.com -(function initMail2925ContentScript() { -if (window.__MULTIPAGE_MAIL2925_SCRIPT_LOADED__) { - console.log('[MultiPage:mail-2925] Duplicate injection skipped on', location.href); - return; -} -window.__MULTIPAGE_MAIL2925_SCRIPT_LOADED__ = true; - const MAIL2925_PREFIX = '[MultiPage:mail-2925]'; const isTopFrame = window === window.top; @@ -1223,4 +1216,3 @@ async function handlePollEmail(step, payload) { } } -})(); diff --git a/content/signup-page.js b/content/signup-page.js index c742b51..f36207b 100644 --- a/content/signup-page.js +++ b/content/signup-page.js @@ -2,7 +2,6 @@ // Injected on: auth0.openai.com, auth.openai.com, accounts.openai.com // Dynamically injected on: chatgpt.com -(function initSignupPageContentScript() { console.log('[MultiPage:signup-page] Content script loaded on', location.href); const SIGNUP_PAGE_LISTENER_SENTINEL = 'data-multipage-signup-page-listener'; @@ -3561,4 +3560,3 @@ async function step5_fillNameBirthday(payload) { log('步骤 5:已点击“完成帐户创建”,当前步骤直接完成,不再等待页面结果。'); return completionPayload; } -})(); diff --git a/content/utils.js b/content/utils.js index 71e711e..f208aca 100644 --- a/content/utils.js +++ b/content/utils.js @@ -1,6 +1,6 @@ // content/utils.js — Shared utilities for all content scripts -var getActivationStrategy = self.MultiPageActivationUtils?.getActivationStrategy; +const getActivationStrategy = self.MultiPageActivationUtils?.getActivationStrategy; function detectScriptSource({ injectedSource, @@ -26,7 +26,7 @@ function detectScriptSource({ return 'vps-panel'; } -var SCRIPT_SOURCE = (() => { +const SCRIPT_SOURCE = (() => { return detectScriptSource({ injectedSource: window.__MULTIPAGE_SOURCE, url: location.href, @@ -38,9 +38,9 @@ function getRuntimeScriptSource() { return window.__MULTIPAGE_SOURCE || SCRIPT_SOURCE; } -var LOG_PREFIX = `[MultiPage:${SCRIPT_SOURCE}]`; -var STOP_ERROR_MESSAGE = '流程已被用户停止。'; -var flowStopped = false; +const LOG_PREFIX = `[MultiPage:${SCRIPT_SOURCE}]`; +const STOP_ERROR_MESSAGE = '流程已被用户停止。'; +let flowStopped = false; if (!window.__MULTIPAGE_UTILS_LISTENER_READY__) { window.__MULTIPAGE_UTILS_LISTENER_READY__ = true; diff --git a/tests/background-mail2925-entry-behavior.test.js b/tests/background-mail2925-entry-behavior.test.js index fe26b5c..8aeed04 100644 --- a/tests/background-mail2925-entry-behavior.test.js +++ b/tests/background-mail2925-entry-behavior.test.js @@ -68,7 +68,7 @@ test('ensureMail2925MailboxSession reuses current mailbox page without sending l }); assert.equal(sendCalls, 0); - assert.equal(readyCalls, 0); + assert.equal(readyCalls, 1); assert.equal(result.result.usedExistingSession, true); }); diff --git a/tests/verification-flow-polling.test.js b/tests/verification-flow-polling.test.js index d241d93..426b828 100644 --- a/tests/verification-flow-polling.test.js +++ b/tests/verification-flow-polling.test.js @@ -43,7 +43,7 @@ test('verification flow keeps 2925 polling cadence in the default payload', () = assert.equal(step8Payload.intervalMs, 15000); }); -test('verification flow enables 2925 target email matching in both provide and receive modes', () => { +test('verification flow only enables 2925 target email matching in receive mode', () => { const helpers = api.createVerificationFlowHelpers({ addLog: async () => {}, chrome: { tabs: { update: async () => {} } }, @@ -82,7 +82,7 @@ test('verification flow enables 2925 target email matching in both provide and r mail2925Mode: 'receive', }); - assert.equal(providePayload.mail2925MatchTargetEmail, true); + assert.equal(providePayload.mail2925MatchTargetEmail, false); assert.equal(receivePayload.mail2925MatchTargetEmail, true); });