From cf42dc73fd22d60534a99311c709604f43bc5a40 Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Tue, 21 Apr 2026 23:21:19 +0800 Subject: [PATCH] =?UTF-8?q?2925=E9=82=AE=E7=AE=B1=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E6=8E=A5=E6=94=B6=E5=92=8C=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 +++--- background.js | 24 +++- background/generated-email-helpers.js | 8 +- background/verification-flow.js | 4 + content/mail-2925.js | 47 ++++++++ managed-alias-utils.js | 29 +++++ sidepanel/sidepanel.html | 19 +-- sidepanel/sidepanel.js | 44 +++++-- .../background-generated-email-module.test.js | 61 ++++++++++ tests/mail-2925-content.test.js | 93 ++++++++++++++- tests/managed-alias-utils.test.js | 9 ++ tests/sidepanel-mail2925-mode.test.js | 109 ++++++++++++++++++ tests/verification-flow-polling.test.js | 43 +++++++ 项目完整链路说明.md | 34 +++--- 项目文件结构说明.md | 15 +-- 15 files changed, 515 insertions(+), 58 deletions(-) create mode 100644 tests/sidepanel-mail2925-mode.test.js diff --git a/README.md b/README.md index 967bb11..6da734c 100644 --- a/README.md +++ b/README.md @@ -81,33 +81,34 @@ ## 安装 1. 打开 `chrome://extensions/` +2. 开启“开发者模式” +3. 点击“加载已解压的扩展程序” +4. 选择本项目目录 +5. 打开扩展侧边栏 ## 2026-04-17 更新补充:Gmail / 2925 别名邮箱 -本次版本对 `Gmail` 与 `2925` 的注册邮箱逻辑做了统一整理: +本次版本对 `Gmail` 与 `2925 provide` 的注册邮箱逻辑做了统一整理: -- `Gmail` 与 `2925` 现在都走同一套“别名邮箱”逻辑。 +- `Gmail` 与 `2925 provide` 现在都走同一套“别名邮箱”逻辑。 - 两者都不再使用“只填前缀再特殊拼接”的界面交互。 - 两者都要求先填写“基邮箱”: - `Gmail`:例如 `name@gmail.com` - - `2925`:例如 `name@2925.com` + - `2925`(仅 provide 模式):例如 `name@2925.com` - 侧边栏里的“注册邮箱”输入框对这两种模式都已开放,可直接手动填写完整邮箱。 - 侧边栏里的 `获取 / 生成` 按钮对这两种模式也可用,行为与 Duck / Cloudflare 一样,都是“可自动生成,也可手动覆盖”。 +- 当 `Mail = 2925` 且模式切到 `接收邮箱` 时,不再走别名基邮箱链路,而是回退到普通“邮箱生成 / 手动填写注册邮箱”路线,2925 只负责后续收信。 具体行为: - `Gmail` 会基于完整基邮箱生成 `name+tag@gmail.com` -- `2925` 会基于完整基邮箱生成 `name123456@2925.com` +- `2925` 仅在 provide 模式下会基于完整基邮箱生成 `name123456@2925.com` - 如果当前“注册邮箱”里已经是与当前基邮箱兼容的完整邮箱,流程会优先复用,不会强行重新生成 注意: - `2925` 旧的“只填前缀”使用方式已经不再推荐,应该改为填写完整基邮箱 -- 如果你手动填写了与当前 `Gmail / 2925` 基邮箱不匹配的完整邮箱,侧边栏会在保存或执行 Step 3 时拦截 -2. 开启“开发者模式” -3. 点击“加载已解压的扩展程序” -4. 选择本项目目录 -5. 打开扩展侧边栏 +- 如果你手动填写了与当前 `Gmail / 2925 provide` 基邮箱不匹配的完整邮箱,侧边栏会在保存或执行 Step 3 时拦截 ## 快速开始 @@ -143,12 +144,15 @@ 1. `Mail` 选择 `2925` 2. 在 `2925 账号池` 中添加 `邮箱 / 密码` -3. 在“2925 基邮箱”右侧打开 `号池` 开关后,基邮箱输入框会切成下拉框,只能从 2925 账号池中选择邮箱;关闭开关则回退到原来的手填基邮箱路线 -4. 可先点 `使用此账号` 让当前别名基邮箱切到这条 2925 账号,再点 `登录` 手动验证网页邮箱登录态 -5. 只有在 `号池` 开关开启时,自动流程执行到 Step 4 / Step 8 前才会自动检查 2925 登录态;如果未登录,会先清理登录 cookie、等待 `3 秒`,再打开登录页,并在页面打开后再等待 `3 秒`,然后使用当前账号自动登录;点击登录后若 `20 秒`内仍未进入收件箱,则会判定当前登录失败 -6. 当 Step 4 / Step 8 轮询邮箱时遇到“子邮箱已达上限邮箱”,扩展会记录当前时间;如果还有下一个可用账号,就禁用当前账号 24 小时并自动切换登录;如果没有下一个可用账号,或当前未启用号池模式,则会直接复用现有“手动暂停 / 停止”逻辑终止自动流程 -7. 如果你同时开启了 `Auto` 的自动重试,当前尝试结束后会按现有逻辑自动进入下一次尝试,不需要再手动介入 -8. `Mail = 2925` 仍然走 Gmail / 2925 共用的别名邮箱链路;实际注册邮箱会基于当前 2925 账号邮箱生成,例如 `name@2925.com -> name123456@2925.com` +3. 先根据你的用途选择 `2925 模式` + - `提供邮箱`:注册邮箱本身就是 2925 别名,会显示“别名基邮箱”输入 + - `接收邮箱`:注册邮箱回退到普通“邮箱生成 / 手动填写”路线,2925 只负责收信 +4. `2925 号池` 现在是独立配置行;开启 `号池` 后可从下拉框中选择当前 2925 账号。若当前处于 `提供邮箱` 模式,这个账号也会同步作为别名基邮箱 +5. 可先点 `使用此账号` 让当前 2925 账号切到这条记录,再点 `登录` 手动验证网页邮箱登录态 +6. 只有在 `号池` 开关开启时,自动流程执行到 Step 4 / Step 8 前才会自动检查 2925 登录态;如果未登录,会先清理登录 cookie、等待 `3 秒`,再打开登录页,并在页面打开后再等待 `3 秒`,然后使用当前账号自动登录;点击登录后若 `20 秒`内仍未进入收件箱,则会判定当前登录失败 +7. 当 Step 4 / Step 8 轮询邮箱时遇到“子邮箱已达上限邮箱”,扩展会记录当前时间;如果还有下一个可用账号,就禁用当前账号 24 小时并自动切换登录;如果没有下一个可用账号,或当前未启用号池模式,则会直接复用现有“手动暂停 / 停止”逻辑终止自动流程 +8. 如果你同时开启了 `Auto` 的自动重试,当前尝试结束后会按现有逻辑自动进入下一次尝试,不需要再手动介入 +9. 只有 `Mail = 2925` 且模式为 `提供邮箱` 时,才会继续走 Gmail / 2925 共用的别名邮箱链路;例如 `name@2925.com -> name123456@2925.com` ## 侧边栏配置说明 diff --git a/background.js b/background.js index 83b9e09..eb23563 100644 --- a/background.js +++ b/background.js @@ -2132,11 +2132,21 @@ function isGeneratedAliasProvider(stateOrProvider, mail2925Mode = undefined) { const provider = typeof stateOrProvider === 'string' ? stateOrProvider : stateOrProvider?.mailProvider; + const resolvedMail2925Mode = mail2925Mode !== undefined + ? normalizeMail2925Mode(mail2925Mode) + : getMail2925Mode(stateOrProvider); const utils = (typeof self !== 'undefined' ? self : globalThis).MultiPageManagedAliasUtils || null; + if (utils?.usesManagedAliasGeneration) { + return utils.usesManagedAliasGeneration(provider, { mail2925Mode: resolvedMail2925Mode }); + } if (utils?.isManagedAliasProvider) { + if (String(provider || '').trim().toLowerCase() === '2925') { + return utils.isManagedAliasProvider(provider) && resolvedMail2925Mode === MAIL_2925_MODE_PROVIDE; + } return utils.isManagedAliasProvider(provider); } - return provider === GMAIL_PROVIDER || provider === '2925'; + return provider === GMAIL_PROVIDER + || (provider === '2925' && resolvedMail2925Mode === MAIL_2925_MODE_PROVIDE); } function shouldUseCustomRegistrationEmail(state = {}) { @@ -2279,11 +2289,21 @@ function isGeneratedAliasProvider(stateOrProvider, mail2925Mode = undefined) { const provider = typeof stateOrProvider === 'string' ? stateOrProvider : stateOrProvider?.mailProvider; + const resolvedMail2925Mode = mail2925Mode !== undefined + ? normalizeMail2925Mode(mail2925Mode) + : getMail2925Mode(stateOrProvider); const utils = getManagedAliasUtils(); + if (utils?.usesManagedAliasGeneration) { + return utils.usesManagedAliasGeneration(provider, { mail2925Mode: resolvedMail2925Mode }); + } if (utils?.isManagedAliasProvider) { + if (String(provider || '').trim().toLowerCase() === '2925') { + return utils.isManagedAliasProvider(provider) && resolvedMail2925Mode === MAIL_2925_MODE_PROVIDE; + } return utils.isManagedAliasProvider(provider); } - return provider === GMAIL_PROVIDER || provider === '2925'; + return provider === GMAIL_PROVIDER + || (provider === '2925' && resolvedMail2925Mode === MAIL_2925_MODE_PROVIDE); } function shouldUseCustomRegistrationEmail(state = {}) { diff --git a/background/generated-email-helpers.js b/background/generated-email-helpers.js index be98af5..3d924a7 100644 --- a/background/generated-email-helpers.js +++ b/background/generated-email-helpers.js @@ -195,6 +195,9 @@ ...(state || {}), mailProvider: provider, }; + if (options.mail2925Mode !== undefined) { + mergedState.mail2925Mode = String(options.mail2925Mode || '').trim(); + } if (options.gmailBaseEmail !== undefined) { mergedState.gmailBaseEmail = String(options.gmailBaseEmail || '').trim(); } @@ -227,7 +230,10 @@ async function fetchGeneratedEmail(state, options = {}) { const currentState = state || await getState(); const provider = String(options.mailProvider || currentState.mailProvider || '').trim().toLowerCase(); - if (isGeneratedAliasProvider?.(provider)) { + const mail2925Mode = options.mail2925Mode !== undefined + ? options.mail2925Mode + : currentState.mail2925Mode; + if (isGeneratedAliasProvider?.(provider, mail2925Mode)) { return fetchManagedAliasEmail(currentState, options); } const generator = normalizeEmailGenerator(options.generator ?? currentState.emailGenerator); diff --git a/background/verification-flow.js b/background/verification-flow.js index fc4f208..eba621d 100644 --- a/background/verification-flow.js +++ b/background/verification-flow.js @@ -113,12 +113,15 @@ function getVerificationPollPayload(step, state, overrides = {}) { const is2925Provider = state?.mailProvider === '2925'; + const mail2925MatchTargetEmail = is2925Provider + && String(state?.mail2925Mode || '').trim().toLowerCase() === 'receive'; if (step === 4) { return { filterAfterTimestamp: is2925Provider ? 0 : getHotmailVerificationRequestTimestamp(4, state), senderFilters: ['openai', 'noreply', 'verify', 'auth', 'duckduckgo', 'forward'], subjectFilters: ['verify', 'verification', 'code', '验证码', 'confirm'], targetEmail: state.email, + mail2925MatchTargetEmail, maxAttempts: is2925Provider ? MAIL_2925_VERIFICATION_MAX_ATTEMPTS : 5, intervalMs: is2925Provider ? MAIL_2925_VERIFICATION_INTERVAL_MS : 3000, ...overrides, @@ -130,6 +133,7 @@ senderFilters: ['openai', 'noreply', 'verify', 'auth', 'chatgpt', 'duckduckgo', 'forward'], subjectFilters: ['verify', 'verification', 'code', '验证码', 'confirm', 'login'], targetEmail: String(state?.step8VerificationTargetEmail || '').trim() || state.email, + mail2925MatchTargetEmail, maxAttempts: is2925Provider ? MAIL_2925_VERIFICATION_MAX_ATTEMPTS : 5, intervalMs: is2925Provider ? MAIL_2925_VERIFICATION_INTERVAL_MS : 3000, ...overrides, diff --git a/content/mail-2925.js b/content/mail-2925.js index 54970d2..1c3a6b0 100644 --- a/content/mail-2925.js +++ b/content/mail-2925.js @@ -629,6 +629,39 @@ function extractVerificationCode(text, strictChatGPTCodeOnly = false) { return null; } +function extractEmails(text = '') { + const matches = String(text || '').match(/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/gi) || []; + return [...new Set(matches.map((item) => item.toLowerCase()))]; +} + +function emailMatchesTarget(candidate, targetEmail) { + const normalizedCandidate = String(candidate || '').trim().toLowerCase(); + const normalizedTarget = String(targetEmail || '').trim().toLowerCase(); + return Boolean(normalizedCandidate && normalizedTarget && normalizedCandidate === normalizedTarget); +} + +function getTargetEmailMatchState(text, targetEmail) { + const normalizedTarget = String(targetEmail || '').trim().toLowerCase(); + if (!normalizedTarget) { + return { matches: true, hasExplicitEmail: false }; + } + + const normalizedText = String(text || '').toLowerCase(); + if (normalizedText.includes(normalizedTarget)) { + return { matches: true, hasExplicitEmail: true }; + } + + const extractedEmails = extractEmails(normalizedText); + if (!extractedEmails.length) { + return { matches: true, hasExplicitEmail: false }; + } + + return { + matches: extractedEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)), + hasExplicitEmail: true, + }; +} + function normalizeMinuteTimestamp(timestamp) { if (!Number.isFinite(timestamp) || timestamp <= 0) return 0; const date = new Date(timestamp); @@ -930,6 +963,8 @@ async function handlePollEmail(step, payload) { filterAfterTimestamp = 0, excludeCodes = [], strictChatGPTCodeOnly = false, + targetEmail = '', + mail2925MatchTargetEmail = false, } = payload || {}; const excludedCodeSet = new Set(excludeCodes.filter(Boolean)); const filterAfterMinute = normalizeMinuteTimestamp(Number(filterAfterTimestamp) || 0); @@ -994,9 +1029,21 @@ async function handlePollEmail(step, payload) { if (!matchesMailFilters(previewText, senderFilters, subjectFilters)) { continue; } + const previewTargetState = mail2925MatchTargetEmail + ? getTargetEmailMatchState(previewText, targetEmail) + : { matches: true, hasExplicitEmail: false }; + if (mail2925MatchTargetEmail && previewTargetState.hasExplicitEmail && !previewTargetState.matches) { + continue; + } const previewCode = extractVerificationCode(previewText, strictChatGPTCodeOnly); const openedText = await openMailAndDeleteAfterRead(item, step); + const openedTargetState = mail2925MatchTargetEmail + ? getTargetEmailMatchState(openedText, targetEmail) + : { matches: true, hasExplicitEmail: false }; + if (mail2925MatchTargetEmail && openedTargetState.hasExplicitEmail && !openedTargetState.matches) { + continue; + } const bodyCode = extractVerificationCode(openedText, strictChatGPTCodeOnly); const candidateCode = bodyCode || previewCode; diff --git a/managed-alias-utils.js b/managed-alias-utils.js index 5213097..51e8c69 100644 --- a/managed-alias-utils.js +++ b/managed-alias-utils.js @@ -3,6 +3,9 @@ })(typeof self !== 'undefined' ? self : globalThis, function createManagedAliasUtilsModule() { const GMAIL_PROVIDER = 'gmail'; const MAIL_2925_PROVIDER = '2925'; + const MAIL_2925_MODE_PROVIDE = 'provide'; + const MAIL_2925_MODE_RECEIVE = 'receive'; + const DEFAULT_MAIL_2925_MODE = MAIL_2925_MODE_PROVIDE; const PROVIDER_CONFIGS = { [GMAIL_PROVIDER]: { @@ -64,10 +67,31 @@ return PROVIDER_CONFIGS[String(provider || '').trim().toLowerCase()] || null; } + function normalizeMail2925Mode(value = '') { + return String(value || '').trim().toLowerCase() === MAIL_2925_MODE_RECEIVE + ? MAIL_2925_MODE_RECEIVE + : DEFAULT_MAIL_2925_MODE; + } + function isManagedAliasProvider(provider = '') { return Boolean(getManagedAliasProviderConfig(provider)); } + function usesManagedAliasGeneration(provider = '', options = {}) { + const normalizedProvider = String(provider || '').trim().toLowerCase(); + if (!isManagedAliasProvider(normalizedProvider)) { + return false; + } + if (normalizedProvider !== MAIL_2925_PROVIDER) { + return true; + } + + const mail2925Mode = typeof options === 'string' + ? options + : options?.mail2925Mode; + return normalizeMail2925Mode(mail2925Mode) === MAIL_2925_MODE_PROVIDE; + } + function parseEmailParts(rawValue = '') { const value = String(rawValue || '').trim().toLowerCase(); const match = value.match(/^([^@\s]+)@([^@\s]+\.[^@\s]+)$/); @@ -136,11 +160,16 @@ return { buildManagedAliasEmail, + DEFAULT_MAIL_2925_MODE, getManagedAliasProviderConfig, getManagedAliasProviderUiCopy, isManagedAliasEmail, isManagedAliasProvider, + MAIL_2925_MODE_PROVIDE, + MAIL_2925_MODE_RECEIVE, + normalizeMail2925Mode, parseEmailParts, parseManagedAliasBaseEmail, + usesManagedAliasGeneration, }; }); diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index ffd6bc0..c8f4b05 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -269,20 +269,25 @@ -