diff --git a/README.md b/README.md index 0b17840..ee0429f 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,16 @@ - `2925` 旧的“只填前缀”使用方式已经不再推荐,应该改为填写完整基邮箱 - 如果你手动填写了与当前 `Gmail / 2925 provide` 基邮箱不匹配的完整邮箱,侧边栏会在保存或执行 Step 3 时拦截 +## 2026-04-23 更新补充:自定义邮箱池 + +本次版本新增 `自定义邮箱池` 生成方式,用于把一批已经准备好的邮箱按顺序分配给自动流程: + +- 在 `邮箱生成` 中选择 `自定义邮箱池` +- 在新出现的 `邮箱池` 文本框里按“每行一个邮箱”填写 +- `Auto` 运行次数会自动跟随邮箱池数量,无需再手动对齐轮数 +- 同一目标轮次的失败重试会继续复用当前轮邮箱,不会提前跳到下一个 +- 实际收码仍然走当前 `Mail` 对应的邮箱服务,因此应保证邮箱池里的地址与当前收码链路匹配 + ## 快速开始 如果你只是想先跑通一套最稳的组合,建议直接按下面三种方案之一配置。 @@ -118,9 +128,9 @@ 1. `CPA` 填你的管理面板 OAuth 页面地址 2. `Mail` 选择 `QQ Mail`、`163 Mail` 或 `163 VIP Mail` -3. `邮箱生成` 选择 `DuckDuckGo` 或 `Cloudflare` +3. `邮箱生成` 选择 `DuckDuckGo`、`Cloudflare` 或 `自定义邮箱池` 4. 若你选择 `Cloudflare`,先按下文把 Cloudflare Email Routing 配好 -5. 点击 `获取` 生成邮箱,或手动粘贴一个你能收信的邮箱 +5. 若你选择 `自定义邮箱池`,就在 `邮箱池` 中按行填入邮箱;否则点击 `获取` 生成邮箱,或手动粘贴一个你能收信的邮箱 6. 先单步验证 `Step 1 ~ Step 4` 7. 验证没问题后再点右上角 `Auto` @@ -349,17 +359,18 @@ Step 3 使用的注册邮箱。 来源有两种: - 手动粘贴 -- 点击 `获取` 自动生成邮箱(DuckDuckGo 或 Cloudflare) +- 按当前生成方式自动生成或分配邮箱(DuckDuckGo / Cloudflare / 自定义邮箱池) 注意: - 若 `邮箱生成 = Cloudflare`,插件里只需要维护 `CF 域名` +- 若 `邮箱生成 = 自定义邮箱池`,需要在 `邮箱池` 文本框中按行维护邮箱列表 - `CF 域名` 支持保存多个,并通过下拉框切换当前要生成的域名 - Cloudflare 侧的转发规则、Catch-all、路由目标邮箱等,都需要你自己提前在 Cloudflare 后台配置好 - 当 `Mail = Hotmail` 时,这个输入框由账号池自动同步当前账号邮箱 - 当 `Mail = Hotmail` 时,Step 3 会直接使用 Hotmail 账号池里的邮箱;`Duck / Cloudflare` 不参与自动邮箱生成 - 若你准备走 `Cloudflare`,更推荐把 `Mail` 设为 `QQ / 163 / 163 VIP`;`Inbucket` 仅在它能真实接收外部邮件并完成 Cloudflare 验证时再使用 -- 当前 `Auto` 按钮只负责 DuckDuckGo 地址获取 +- `Auto` 会按当前“邮箱生成”配置自动获取或分配邮箱;若当前是 `自定义邮箱池`,则会按邮箱池顺序取用 - 如果你使用 Inbucket,它只是验证码收件箱,不会自动生成 Inbucket 地址 ### `邮箱生成 = Cloudflare` 时的配置 @@ -509,7 +520,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。 1. Step 1 打开 `https://chatgpt.com/` 2. 根据 `Mail` 选择邮箱来源 3. 如果 `Mail = Hotmail`,会从账号池自动分配一个可用账号 -4. 如果不是 Hotmail,则按当前“邮箱生成”配置尝试自动获取邮箱(Duck / Cloudflare / iCloud 等) +4. 如果不是 Hotmail,则按当前“邮箱生成”配置尝试自动获取或分配邮箱(Duck / Cloudflare / iCloud / 自定义邮箱池等) 5. Step 2 点击注册、填写邮箱,并按真实落地页进入密码页或直接进入邮箱验证码页 6. 如果自动获取失败,暂停并等待你在侧边栏填写邮箱后点击 `Continue` 7. 继续执行 Step 3 ~ Step 10 diff --git a/background.js b/background.js index a2e2e93..aaf6ed8 100644 --- a/background.js +++ b/background.js @@ -139,10 +139,12 @@ const ICLOUD_LOGIN_URLS = [ ]; const ICLOUD_PROVIDER = 'icloud'; const GMAIL_PROVIDER = 'gmail'; +const GMAIL_ALIAS_GENERATOR = 'gmail-alias'; const HOTMAIL_PROVIDER = 'hotmail-api'; const LUCKMAIL_PROVIDER = 'luckmail-api'; const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email'; +const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool'; const HOTMAIL_MAILBOXES = ['INBOX', 'Junk']; const STOP_ERROR_MESSAGE = '流程已被用户停止。'; const CLOUDFLARE_SECURITY_BLOCK_ERROR_PREFIX = 'CF_SECURITY_BLOCKED::'; @@ -266,6 +268,7 @@ const PERSISTED_SETTING_DEFAULTS = { mail2925Mode: DEFAULT_MAIL_2925_MODE, mail2925UseAccountPool: false, emailGenerator: 'duck', + customEmailPool: [], autoDeleteUsedIcloudAlias: false, icloudHostPreference: 'auto', accountRunHistoryTextEnabled: false, @@ -647,9 +650,21 @@ function getAutoRunTimerStatusPayload(plan) { function normalizeEmailGenerator(value = '') { const normalized = String(value || '').trim().toLowerCase(); + const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' + ? CUSTOM_EMAIL_POOL_GENERATOR + : 'custom-pool'; + const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string' + ? GMAIL_ALIAS_GENERATOR + : 'gmail-alias'; if (normalized === 'custom' || normalized === 'manual') { return 'custom'; } + if (normalized === gmailAliasGenerator) { + return gmailAliasGenerator; + } + if (normalized === customEmailPoolGenerator) { + return customEmailPoolGenerator; + } if (normalized === 'icloud') { return 'icloud'; } @@ -658,6 +673,36 @@ function normalizeEmailGenerator(value = '') { return 'duck'; } +function normalizeCustomEmailPool(value = []) { + const source = Array.isArray(value) + ? value + : String(value || '').split(/[\r\n,,;;]+/); + + return source + .map((item) => String(item || '').trim().toLowerCase()) + .filter((item) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(item)); +} + +function isCustomEmailPoolGenerator(stateOrValue = {}) { + const generator = typeof stateOrValue === 'string' + ? stateOrValue + : stateOrValue?.emailGenerator; + const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' + ? CUSTOM_EMAIL_POOL_GENERATOR + : 'custom-pool'; + return normalizeEmailGenerator(generator) === customEmailPoolGenerator; +} + +function getCustomEmailPool(state = {}) { + return normalizeCustomEmailPool(state?.customEmailPool); +} + +function getCustomEmailPoolEmailForRun(state = {}, targetRun = 1) { + const entries = getCustomEmailPool(state); + const numericRun = Math.max(1, Math.floor(Number(targetRun) || 1)); + return entries[numericRun - 1] || ''; +} + function normalizePanelMode(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api') { @@ -913,6 +958,8 @@ function normalizePersistentSettingValue(key, value) { return Boolean(value); case 'emailGenerator': return normalizeEmailGenerator(value); + case 'customEmailPool': + return normalizeCustomEmailPool(value); case 'autoDeleteUsedIcloudAlias': case 'accountRunHistoryTextEnabled': case 'cloudflareTempEmailUseRandomSubdomain': @@ -2151,6 +2198,18 @@ function parseGmailBaseEmail(rawValue) { } function isGeneratedAliasProvider(stateOrProvider, mail2925Mode = undefined) { + if ( + stateOrProvider + && typeof stateOrProvider === 'object' + && !Array.isArray(stateOrProvider) + && normalizeEmailGenerator(stateOrProvider.emailGenerator) === ( + typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' + ? CUSTOM_EMAIL_POOL_GENERATOR + : 'custom-pool' + ) + ) { + return false; + } const provider = typeof stateOrProvider === 'string' ? stateOrProvider : stateOrProvider?.mailProvider; @@ -2308,6 +2367,18 @@ function getManagedAliasBaseEmail(state = {}, provider = state?.mailProvider) { } function isGeneratedAliasProvider(stateOrProvider, mail2925Mode = undefined) { + if ( + stateOrProvider + && typeof stateOrProvider === 'object' + && !Array.isArray(stateOrProvider) + && normalizeEmailGenerator(stateOrProvider.emailGenerator) === ( + typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' + ? CUSTOM_EMAIL_POOL_GENERATOR + : 'custom-pool' + ) + ) { + return false; + } const provider = typeof stateOrProvider === 'string' ? stateOrProvider : stateOrProvider?.mailProvider; @@ -5051,7 +5122,12 @@ async function handleStepData(step, payload) { }); } await finalizeIcloudAliasAfterSuccessfulFlow(latestState); - if (shouldUseCustomRegistrationEmail(latestState) && latestState.email) { + const shouldClearCustomPoolEmail = String(latestState?.emailGenerator || '').trim().toLowerCase() === ( + typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' + ? CUSTOM_EMAIL_POOL_GENERATOR + : 'custom-pool' + ); + if ((shouldUseCustomRegistrationEmail(latestState) || shouldClearCustomPoolEmail) && latestState.email) { await setEmailStateSilently(null); } break; @@ -5465,9 +5541,21 @@ async function executeStepAndWait(step, delayAfter = 2000) { } function getEmailGeneratorLabel(generator) { + const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' + ? CUSTOM_EMAIL_POOL_GENERATOR + : 'custom-pool'; + const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string' + ? GMAIL_ALIAS_GENERATOR + : 'gmail-alias'; if (generator === 'custom') { return '自定义邮箱'; } + if (generator === gmailAliasGenerator) { + return 'Gmail +tag 邮箱'; + } + if (generator === customEmailPoolGenerator) { + return '自定义邮箱池'; + } if (generator === 'icloud') { return 'iCloud 隐私邮箱'; } @@ -5568,11 +5656,13 @@ const generatedEmailHelpers = self.MultiPageGeneratedEmailHelpers?.createGenerat buildGeneratedAliasEmail, buildCloudflareTempEmailHeaders, CLOUDFLARE_TEMP_EMAIL_GENERATOR, + CUSTOM_EMAIL_POOL_GENERATOR, DUCK_AUTOFILL_URL, fetch, fetchIcloudHideMyEmail, getCloudflareTempEmailAddressFromResponse, getCloudflareTempEmailConfig, + getCustomEmailPoolEmail: getCustomEmailPoolEmailForRun, getState, ensureMail2925AccountForFlow, joinCloudflareTempEmailUrl, @@ -5815,6 +5905,21 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) { return currentState.email; } + if (isCustomEmailPoolGenerator(currentState)) { + const queuedEmail = getCustomEmailPoolEmailForRun(currentState, targetRun); + if (!queuedEmail) { + const poolSize = getCustomEmailPool(currentState).length; + throw new Error( + poolSize > 0 + ? `自定义邮箱池第 ${targetRun} 个邮箱不存在,请检查邮箱池数量是否与自动轮数一致。` + : '自定义邮箱池为空,请先至少填写 1 个邮箱。' + ); + } + await setEmailState(queuedEmail); + await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:自定义邮箱池已就绪:${queuedEmail}(第 ${attemptRuns} 次尝试)===`, 'ok'); + return queuedEmail; + } + if (shouldUseCustomRegistrationEmail(currentState)) { await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮已暂停:请先填写自定义注册邮箱,然后继续 ===`, 'warn'); await broadcastAutoRunStatus('waiting_email', { @@ -5933,6 +6038,21 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) { return currentState.email; } + if (isCustomEmailPoolGenerator(currentState)) { + const queuedEmail = getCustomEmailPoolEmailForRun(currentState, targetRun); + if (!queuedEmail) { + const poolSize = getCustomEmailPool(currentState).length; + throw new Error( + poolSize > 0 + ? `自定义邮箱池第 ${targetRun} 个邮箱不存在,请检查邮箱池数量是否与自动轮数一致。` + : '自定义邮箱池为空,请先至少填写 1 个邮箱。' + ); + } + await setEmailState(queuedEmail); + await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:自定义邮箱池已就绪:${queuedEmail}(第 ${attemptRuns} 次尝试)===`, 'ok'); + return queuedEmail; + } + if (shouldUseCustomRegistrationEmail(currentState)) { await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮已暂停:请先填写自定义注册邮箱,然后继续 ===`, 'warn'); await broadcastAutoRunStatus('waiting_email', { diff --git a/background/generated-email-helpers.js b/background/generated-email-helpers.js index 7db5c98..be121ea 100644 --- a/background/generated-email-helpers.js +++ b/background/generated-email-helpers.js @@ -7,11 +7,13 @@ buildGeneratedAliasEmail, buildCloudflareTempEmailHeaders, CLOUDFLARE_TEMP_EMAIL_GENERATOR, + CUSTOM_EMAIL_POOL_GENERATOR, DUCK_AUTOFILL_URL, fetch, fetchIcloudHideMyEmail, getCloudflareTempEmailAddressFromResponse, getCloudflareTempEmailConfig, + getCustomEmailPoolEmail, getState, ensureMail2925AccountForFlow, joinCloudflareTempEmailUrl, @@ -189,6 +191,24 @@ return result.email; } + async function fetchCustomEmailPoolEmail(state, options = {}) { + throwIfStopped(); + const latestState = state || await getState(); + const requestedIndex = Math.max(0, Math.floor(Number(options.poolIndex) || 0)); + const email = String(getCustomEmailPoolEmail?.(latestState, requestedIndex + 1) || '').trim().toLowerCase(); + if (!email) { + throw new Error( + requestedIndex > 0 + ? `自定义邮箱池第 ${requestedIndex + 1} 个邮箱不存在,请检查邮箱池配置。` + : '自定义邮箱池为空,请先至少填写 1 个邮箱。' + ); + } + + await setEmailState(email); + await addLog(`自定义邮箱池:已取用 ${email}`, 'ok'); + return email; + } + async function fetchManagedAliasEmail(state, options = {}) { throwIfStopped(); const provider = String(options.mailProvider || state?.mailProvider || '').trim().toLowerCase(); @@ -234,21 +254,42 @@ const mail2925Mode = options.mail2925Mode !== undefined ? options.mail2925Mode : currentState.mail2925Mode; - if (isGeneratedAliasProvider?.(provider, mail2925Mode)) { - return fetchManagedAliasEmail(currentState, options); - } const generator = normalizeEmailGenerator(options.generator ?? currentState.emailGenerator); + const mergedState = { + ...currentState, + mailProvider: provider || currentState.mailProvider, + mail2925Mode, + emailGenerator: generator, + }; + if (options.gmailBaseEmail !== undefined) { + mergedState.gmailBaseEmail = String(options.gmailBaseEmail || '').trim(); + } + if (options.mail2925BaseEmail !== undefined) { + mergedState.mail2925BaseEmail = String(options.mail2925BaseEmail || '').trim(); + } + if (options.customEmailPool !== undefined) { + mergedState.customEmailPool = options.customEmailPool; + } if (generator === 'custom') { throw new Error('当前邮箱生成方式为自定义邮箱,请直接填写注册邮箱。'); } + if (generator === CUSTOM_EMAIL_POOL_GENERATOR) { + return fetchCustomEmailPoolEmail(mergedState, options); + } + const shouldUseManagedAlias = typeof isGeneratedAliasProvider === 'function' + ? isGeneratedAliasProvider(mergedState, mail2925Mode) + : false; + if (shouldUseManagedAlias) { + return fetchManagedAliasEmail(mergedState, options); + } if (generator === 'icloud') { return fetchIcloudHideMyEmail(); } if (generator === 'cloudflare') { - return fetchCloudflareEmail(currentState, options); + return fetchCloudflareEmail(mergedState, options); } if (generator === CLOUDFLARE_TEMP_EMAIL_GENERATOR) { - return fetchCloudflareTempEmailAddress(currentState, options); + return fetchCloudflareTempEmailAddress(mergedState, options); } return fetchDuckEmail(options); } @@ -256,6 +297,7 @@ return { ensureCloudflareTempEmailConfig, fetchCloudflareEmail, + fetchCustomEmailPoolEmail, fetchCloudflareTempEmailAddress, fetchDuckEmail, fetchGeneratedEmail, diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 966a58c..63f7ab0 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -240,12 +240,19 @@
邮箱生成
+