diff --git a/background.js b/background.js
index b8284f5..8f5cca1 100644
--- a/background.js
+++ b/background.js
@@ -272,6 +272,7 @@ const PERSISTED_SETTING_DEFAULTS = {
customEmailPool: [],
autoDeleteUsedIcloudAlias: false,
icloudHostPreference: 'auto',
+ icloudFetchMode: 'reuse_existing',
accountRunHistoryTextEnabled: false,
accountRunHistoryHelperBaseUrl: DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL,
gmailBaseEmail: '',
@@ -674,6 +675,11 @@ function normalizeEmailGenerator(value = '') {
return 'duck';
}
+function normalizeIcloudFetchMode(value = '') {
+ const normalized = String(value || '').trim().toLowerCase();
+ return normalized === 'always_new' ? 'always_new' : 'reuse_existing';
+}
+
function normalizeCustomEmailPool(value = []) {
const source = Array.isArray(value)
? value
@@ -978,6 +984,8 @@ function normalizePersistentSettingValue(key, value) {
return Boolean(value);
case 'icloudHostPreference':
return normalizeIcloudHost(value) || 'auto';
+ case 'icloudFetchMode':
+ return normalizeIcloudFetchMode(value);
case 'accountRunHistoryHelperBaseUrl':
return normalizeAccountRunHistoryHelperBaseUrl(value);
case 'gmailBaseEmail':
@@ -3448,10 +3456,16 @@ async function validateIcloudSession(setupUrl) {
return data;
}
-async function resolveIcloudPremiumMailService() {
+async function resolveIcloudPremiumMailService(options = {}) {
const errors = [];
const state = await getState();
- const setupUrls = await getPreferredIcloudSetupUrls(state);
+ const explicitHost = normalizeIcloudHost(options?.hostPreference || options?.preferredHost || '');
+ const setupUrls = explicitHost
+ ? (() => {
+ const forcedSetupUrl = getIcloudSetupUrlForHost(explicitHost);
+ return forcedSetupUrl ? [forcedSetupUrl] : [];
+ })()
+ : await getPreferredIcloudSetupUrls(state);
for (const setupUrl of setupUrls) {
try {
@@ -3480,17 +3494,19 @@ function getIcloudAliasLabel() {
return `MultiPage ${dateStr}`;
}
-async function checkIcloudSession() {
- return withIcloudLoginHelp('检查 iCloud 会话', async () => {
- const { setupUrl } = await resolveIcloudPremiumMailService();
+async function checkIcloudSession(options = {}) {
+ const actionLabel = String(options?.actionLabel || '检查 iCloud 会话').trim() || '检查 iCloud 会话';
+ const { actionLabel: _ignoredActionLabel, ...resolveOptions } = options || {};
+ return withIcloudLoginHelp(actionLabel, async () => {
+ const { setupUrl } = await resolveIcloudPremiumMailService(resolveOptions);
await addLog(`iCloud:会话校验通过(${new URL(setupUrl).host})`, 'ok');
return { ok: true, setupUrl };
});
}
-async function listIcloudAliases() {
+async function listIcloudAliases(options = {}) {
return withIcloudLoginHelp('加载 iCloud 隐私邮箱列表', async () => {
- const { serviceUrl } = await resolveIcloudPremiumMailService();
+ const { serviceUrl } = await resolveIcloudPremiumMailService(options);
const response = await icloudRequest('GET', `${serviceUrl}/v2/hme/list`);
const state = await getState();
return normalizeIcloudAliasList(response, {
@@ -3580,9 +3596,10 @@ async function deleteUsedIcloudAliases() {
return { deleted, skipped };
}
-async function fetchIcloudHideMyEmail() {
+async function fetchIcloudHideMyEmail(options = {}) {
return withIcloudLoginHelp('获取 iCloud 隐私邮箱', async () => {
throwIfStopped();
+ const generateNew = Boolean(options?.generateNew);
await addLog('iCloud:正在校验当前浏览器登录状态...', 'info');
const { serviceUrl, setupUrl } = await resolveIcloudPremiumMailService();
@@ -3595,12 +3612,16 @@ async function fetchIcloudHideMyEmail() {
preservedEmails: getPreservedAliasMap(state),
});
- const reusableAlias = pickReusableIcloudAlias(existingAliases);
- if (reusableAlias) {
- await setEmailState(reusableAlias.email);
- await addLog(`iCloud:复用未使用别名 ${reusableAlias.email}`, 'ok');
- broadcastIcloudAliasesChanged({ reason: 'selected', email: reusableAlias.email });
- return reusableAlias.email;
+ if (!generateNew) {
+ const reusableAlias = pickReusableIcloudAlias(existingAliases);
+ if (reusableAlias) {
+ await setEmailState(reusableAlias.email);
+ await addLog(`iCloud:复用未使用别名 ${reusableAlias.email}`, 'ok');
+ broadcastIcloudAliasesChanged({ reason: 'selected', email: reusableAlias.email });
+ return reusableAlias.email;
+ }
+ } else {
+ await addLog('iCloud:已启用“始终创建新别名”,本次将跳过复用。', 'info');
}
await addLog('iCloud:没有可复用别名,开始生成新的 Hide My Email 地址...', 'warn');
@@ -5970,7 +5991,10 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
if (attempt > 1) {
await addLog(`${generatorLabel}:正在进行第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次自动获取重试...`, 'warn');
}
- const generatedEmail = await fetchGeneratedEmail(currentState, { generateNew: true, generator });
+ const generatedEmail = await fetchGeneratedEmail(currentState, {
+ generateNew: generator !== 'icloud' || normalizeIcloudFetchMode(currentState.icloudFetchMode) === 'always_new',
+ generator,
+ });
await addLog(
`=== 目标 ${targetRun}/${totalRuns} 轮:${generatorLabel}已就绪:${generatedEmail}(第 ${attemptRuns} 次尝试,第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次获取)===`,
'ok'
@@ -6116,7 +6140,10 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
if (attempt > 1) {
await addLog(`${generatorLabel}:正在进行第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次自动获取重试...`, 'warn');
}
- const generatedEmail = await fetchGeneratedEmail(currentState, { generateNew: true, generator });
+ const generatedEmail = await fetchGeneratedEmail(currentState, {
+ generateNew: generator !== 'icloud' || normalizeIcloudFetchMode(currentState.icloudFetchMode) === 'always_new',
+ generator,
+ });
await addLog(
`=== 目标 ${targetRun}/${totalRuns} 轮:${generatorLabel}已就绪:${generatedEmail}(第 ${attemptRuns} 次尝试,第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次获取)===`,
'ok'
@@ -6201,9 +6228,15 @@ async function runAutoSequenceFromStep(startStep, context = {}) {
let restartFromStep1WithCurrentEmail = false;
let step = Math.max(currentStartStep, 4);
while (step <= LAST_STEP_ID) {
+ const latestState = await getState();
+ const currentStatus = latestState.stepStatuses?.[step] || 'pending';
+ if (isStepDoneStatus(currentStatus)) {
+ await addLog(`自动运行:步骤 ${step} 当前状态为 ${currentStatus},将直接继续后续流程。`, 'info');
+ step += 1;
+ continue;
+ }
try {
await executeStepAndWait(step, AUTO_STEP_DELAYS[step]);
- const latestState = await getState();
step += 1;
} catch (err) {
if (isStopError(err)) {
@@ -6442,6 +6475,7 @@ const verificationFlowHelpers = self.MultiPageBackgroundVerificationFlow?.create
getTabId,
HOTMAIL_PROVIDER,
isMail2925LimitReachedError,
+ isRetryableContentScriptTransportError,
isStopError,
LUCKMAIL_PROVIDER,
MAIL_2925_VERIFICATION_INTERVAL_MS,
@@ -6450,6 +6484,7 @@ const verificationFlowHelpers = self.MultiPageBackgroundVerificationFlow?.create
pollHotmailVerificationCode,
pollLuckmailVerificationCode,
sendToContentScript,
+ sendToContentScriptResilient,
sendToMailContentScriptResilient,
setState,
setStepStatus,
@@ -6467,6 +6502,7 @@ const step2Executor = self.MultiPageBackgroundStep2?.createStep2Executor({
chrome,
completeStepFromBackground,
ensureContentScriptReadyOnTab,
+ ensureSignupAuthEntryPageReady,
ensureSignupEntryPageReady,
ensureSignupPostEmailPageReadyInTab,
getTabId,
@@ -6487,12 +6523,24 @@ const step3Executor = self.MultiPageBackgroundStep3?.createStep3Executor({
setState,
SIGNUP_PAGE_INJECT_FILES,
});
+
+async function ensureIcloudMailSessionForVerification(options = {}) {
+ const flowState = options?.state || await getState().catch(() => ({}));
+ const hostPreference = getConfiguredIcloudHostPreference(flowState)
+ || normalizeIcloudHost(flowState?.preferredIcloudHost);
+ return checkIcloudSession({
+ ...(hostPreference ? { hostPreference } : {}),
+ actionLabel: options?.actionLabel || '检查 iCloud 会话',
+ });
+}
+
const step4Executor = self.MultiPageBackgroundStep4?.createStep4Executor({
addLog,
chrome,
completeStepFromBackground,
confirmCustomVerificationStepBypass: verificationFlowHelpers.confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
+ ensureIcloudMailSession: ensureIcloudMailSessionForVerification,
getMailConfig,
getTabId,
HOTMAIL_PROVIDER,
@@ -6539,6 +6587,7 @@ const step8Executor = self.MultiPageBackgroundStep8?.createStep8Executor({
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
confirmCustomVerificationStepBypass: verificationFlowHelpers.confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
+ ensureIcloudMailSession: ensureIcloudMailSessionForVerification,
ensureStep8VerificationPageReady,
getOAuthFlowRemainingMs,
getOAuthFlowStepTimeoutMs,
@@ -6721,6 +6770,10 @@ async function ensureSignupEntryPageReady(step = 1) {
return signupFlowHelpers.ensureSignupEntryPageReady(step);
}
+async function ensureSignupAuthEntryPageReady(step = 1) {
+ return signupFlowHelpers.ensureSignupEntryPageReady(step);
+}
+
async function ensureSignupPasswordPageReadyInTab(tabId, step = 2, options = {}) {
return signupFlowHelpers.ensureSignupPasswordPageReadyInTab(tabId, step, options);
}
diff --git a/background/generated-email-helpers.js b/background/generated-email-helpers.js
index be121ea..022d6af 100644
--- a/background/generated-email-helpers.js
+++ b/background/generated-email-helpers.js
@@ -283,7 +283,10 @@
return fetchManagedAliasEmail(mergedState, options);
}
if (generator === 'icloud') {
- return fetchIcloudHideMyEmail();
+ const stateFetchMode = String(mergedState.icloudFetchMode || '').trim().toLowerCase();
+ return fetchIcloudHideMyEmail({
+ generateNew: Boolean(options.generateNew) || stateFetchMode === 'always_new',
+ });
}
if (generator === 'cloudflare') {
return fetchCloudflareEmail(mergedState, options);
diff --git a/background/message-router.js b/background/message-router.js
index c293b16..15c503d 100644
--- a/background/message-router.js
+++ b/background/message-router.js
@@ -151,6 +151,18 @@
if (payload.email) {
await setEmailState(payload.email);
}
+ if (payload.skipRegistrationFlow) {
+ const latestState = await getState();
+ for (const skipStep of [3, 4, 5]) {
+ const status = latestState.stepStatuses?.[skipStep];
+ if (status === 'running' || status === 'completed' || status === 'manual_completed') {
+ continue;
+ }
+ await setStepStatus(skipStep, 'skipped');
+ }
+ await addLog('步骤 2:检测到当前已登录会话,已自动跳过步骤 3/4/5,流程将直接进入步骤 6。', 'warn');
+ break;
+ }
if (payload.skippedPasswordStep) {
const latestState = await getState();
const step3Status = latestState.stepStatuses?.[3];
@@ -179,6 +191,14 @@
lastEmailTimestamp: payload.emailTimestamp || null,
signupVerificationRequestedAt: null,
});
+ if (payload.skipProfileStep) {
+ const latestState = await getState();
+ const step5Status = latestState.stepStatuses?.[5];
+ if (step5Status !== 'running' && step5Status !== 'completed' && step5Status !== 'manual_completed') {
+ await setStepStatus(5, 'skipped');
+ await addLog('步骤 4:检测到账号已直接进入已登录态,已自动跳过步骤 5。', 'warn');
+ }
+ }
break;
case 8:
await setState({
diff --git a/background/steps/fetch-login-code.js b/background/steps/fetch-login-code.js
index 7baad00..a379a77 100644
--- a/background/steps/fetch-login-code.js
+++ b/background/steps/fetch-login-code.js
@@ -10,6 +10,7 @@
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
+ ensureIcloudMailSession,
ensureStep8VerificationPageReady,
getOAuthFlowRemainingMs,
getOAuthFlowStepTimeoutMs,
@@ -140,6 +141,15 @@
return;
}
+ if (mail.source === 'icloud-mail' && typeof ensureIcloudMailSession === 'function') {
+ await addLog('步骤 8:正在确认 iCloud 邮箱登录态...', 'info');
+ await ensureIcloudMailSession({
+ state,
+ step: 8,
+ actionLabel: '步骤 8:确认 iCloud 邮箱登录态',
+ });
+ }
+
throwIfStopped();
if (
mail.provider === HOTMAIL_PROVIDER
diff --git a/background/steps/fetch-signup-code.js b/background/steps/fetch-signup-code.js
index 77dbeaa..02f9aa5 100644
--- a/background/steps/fetch-signup-code.js
+++ b/background/steps/fetch-signup-code.js
@@ -10,6 +10,7 @@
completeStepFromBackground,
confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
+ ensureIcloudMailSession,
getMailConfig,
getTabId,
HOTMAIL_PROVIDER,
@@ -111,6 +112,15 @@
return;
}
+ if (mail.source === 'icloud-mail' && typeof ensureIcloudMailSession === 'function') {
+ await addLog('步骤 4:正在确认 iCloud 邮箱登录态...', 'info');
+ await ensureIcloudMailSession({
+ state,
+ step: 4,
+ actionLabel: '步骤 4:确认 iCloud 邮箱登录态',
+ });
+ }
+
throwIfStopped();
if (
mail.provider === HOTMAIL_PROVIDER
diff --git a/background/steps/submit-signup-email.js b/background/steps/submit-signup-email.js
index 77e6b65..b8cad06 100644
--- a/background/steps/submit-signup-email.js
+++ b/background/steps/submit-signup-email.js
@@ -7,6 +7,7 @@
chrome,
completeStepFromBackground,
ensureContentScriptReadyOnTab,
+ ensureSignupAuthEntryPageReady,
ensureSignupEntryPageReady,
ensureSignupPostEmailPageReadyInTab,
getTabId,
@@ -16,6 +17,107 @@
SIGNUP_PAGE_INJECT_FILES,
} = deps;
+ function getErrorMessage(error) {
+ return String(typeof error === 'string' ? error : error?.message || '');
+ }
+
+ function isSignupEntryUnavailableErrorMessage(errorLike) {
+ const message = getErrorMessage(errorLike);
+ return /未找到可用的邮箱输入入口|当前页面没有可用的注册入口,也不在邮箱\/密码页/.test(message);
+ }
+
+ function isRetryableStep2TransportErrorMessage(errorLike) {
+ const message = getErrorMessage(errorLike);
+ return /Content script on signup-page did not respond in \d+s|Receiving end does not exist|message channel closed|A listener indicated an asynchronous response|port closed before a response was received|did not respond in \d+s/i.test(message);
+ }
+
+ function isLikelyLoggedInChatgptHomeUrl(rawUrl) {
+ const url = String(rawUrl || '').trim();
+ if (!url) {
+ return false;
+ }
+ try {
+ const parsed = new URL(url);
+ const host = String(parsed.hostname || '').toLowerCase();
+ if (!['chatgpt.com', 'www.chatgpt.com'].includes(host)) {
+ return false;
+ }
+ const path = String(parsed.pathname || '');
+ if (/^\/(?:auth\/|create-account\/|email-verification|log-in|add-phone)(?:[/?#]|$)/i.test(path)) {
+ return false;
+ }
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ async function shouldForceAuthEntryRetry(tabId) {
+ if (!Number.isInteger(tabId) || typeof chrome?.tabs?.get !== 'function') {
+ return false;
+ }
+ try {
+ const tab = await chrome.tabs.get(tabId);
+ const currentUrl = String(tab?.url || '');
+ return isLikelyLoggedInChatgptHomeUrl(currentUrl);
+ } catch {
+ return false;
+ }
+ }
+
+ async function getTabUrl(tabId) {
+ if (!Number.isInteger(tabId) || typeof chrome?.tabs?.get !== 'function') {
+ return '';
+ }
+ try {
+ const tab = await chrome.tabs.get(tabId);
+ return String(tab?.url || '');
+ } catch {
+ return '';
+ }
+ }
+
+ async function completeStep2AsLoggedInSession(tabId, resolvedEmail, reasonMessage = '') {
+ const currentUrl = await getTabUrl(tabId);
+ if (!isLikelyLoggedInChatgptHomeUrl(currentUrl)) {
+ return false;
+ }
+ const reasonText = getErrorMessage(reasonMessage);
+ const reasonSuffix = reasonText ? `(触发原因:${reasonText})` : '';
+ await addLog(`步骤 2:检测到当前会话已登录 ChatGPT,已跳过注册链路(步骤 3/4/5),将直接进入步骤 6。${reasonSuffix}`, 'warn');
+ await completeStepFromBackground(2, {
+ email: resolvedEmail,
+ nextSignupState: 'already_logged_in_home',
+ nextSignupUrl: currentUrl || 'https://chatgpt.com/',
+ skippedPasswordStep: true,
+ skipRegistrationFlow: true,
+ });
+ return true;
+ }
+
+ async function submitSignupEmail(resolvedEmail, options = {}) {
+ const {
+ timeoutMs = 35000,
+ retryDelayMs = 700,
+ logMessage = '步骤 2:官网注册入口正在切换,等待页面恢复后继续输入邮箱...',
+ } = options;
+
+ try {
+ return await sendToContentScriptResilient('signup-page', {
+ type: 'EXECUTE_STEP',
+ step: 2,
+ source: 'background',
+ payload: { email: resolvedEmail },
+ }, {
+ timeoutMs,
+ retryDelayMs,
+ logMessage,
+ });
+ } catch (error) {
+ return { error: getErrorMessage(error) };
+ }
+ }
+
async function executeStep2(state) {
const resolvedEmail = await resolveSignupEmailForFlow(state);
@@ -34,19 +136,73 @@
});
}
- const step2Result = await sendToContentScriptResilient('signup-page', {
- type: 'EXECUTE_STEP',
- step: 2,
- source: 'background',
- payload: { email: resolvedEmail },
- }, {
- timeoutMs: 20000,
+ if (await shouldForceAuthEntryRetry(signupTabId)) {
+ await addLog('步骤 2:检测到当前位于已登录 ChatGPT 首页,先切换认证入口页再提交邮箱。', 'warn');
+ try {
+ signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
+ } catch (entryError) {
+ const entryErrorMessage = getErrorMessage(entryError);
+ if (await completeStep2AsLoggedInSession(signupTabId, resolvedEmail, entryErrorMessage)) {
+ return;
+ }
+ await addLog('步骤 2:切换认证入口失败,正在重新打开官网入口并重试提交邮箱...', 'warn');
+ signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
+ }
+ }
+
+ let step2Result = await submitSignupEmail(resolvedEmail, {
+ timeoutMs: 35000,
retryDelayMs: 700,
logMessage: '步骤 2:官网注册入口正在切换,等待页面恢复后继续输入邮箱...',
});
if (step2Result?.error) {
- throw new Error(step2Result.error);
+ const errorMessage = getErrorMessage(step2Result.error);
+ if (isSignupEntryUnavailableErrorMessage(errorMessage)) {
+ await addLog('步骤 2:未找到邮箱输入入口,正在切换认证入口页后重试一次...', 'warn');
+ signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
+ step2Result = await submitSignupEmail(resolvedEmail, {
+ timeoutMs: 35000,
+ retryDelayMs: 700,
+ logMessage: '步骤 2:认证入口页已打开,正在重新提交邮箱...',
+ });
+
+ if (step2Result?.error) {
+ const retryErrorMessage = getErrorMessage(step2Result.error);
+ if (isSignupEntryUnavailableErrorMessage(retryErrorMessage)) {
+ if (await completeStep2AsLoggedInSession(signupTabId, resolvedEmail, retryErrorMessage)) {
+ return;
+ }
+ await addLog('步骤 2:认证入口仍不可用,正在重新进入官网注册入口再重试一次...', 'warn');
+ signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
+ step2Result = await submitSignupEmail(resolvedEmail, {
+ timeoutMs: 35000,
+ retryDelayMs: 700,
+ logMessage: '步骤 2:重试官网注册入口后正在重新提交邮箱...',
+ });
+ }
+ }
+ } else if (isRetryableStep2TransportErrorMessage(errorMessage)) {
+ await addLog('步骤 2:注册入口页通信超时,正在切换认证入口页并重试提交邮箱...', 'warn');
+ signupTabId = (await ensureSignupAuthEntryPageReady(2)).tabId;
+ step2Result = await submitSignupEmail(resolvedEmail, {
+ timeoutMs: 45000,
+ retryDelayMs: 700,
+ logMessage: '步骤 2:认证入口页已打开,正在重新提交邮箱...',
+ });
+ }
+ }
+
+ if (step2Result?.error) {
+ const finalErrorMessage = getErrorMessage(step2Result.error);
+ if (
+ (isSignupEntryUnavailableErrorMessage(finalErrorMessage)
+ || isRetryableStep2TransportErrorMessage(finalErrorMessage))
+ && await completeStep2AsLoggedInSession(signupTabId, resolvedEmail, finalErrorMessage)
+ ) {
+ return;
+ }
+ throw new Error(finalErrorMessage);
}
if (!step2Result?.alreadyOnPasswordPage) {
diff --git a/background/verification-flow.js b/background/verification-flow.js
index 36e9411..3b752eb 100644
--- a/background/verification-flow.js
+++ b/background/verification-flow.js
@@ -23,6 +23,7 @@
pollHotmailVerificationCode,
pollLuckmailVerificationCode,
sendToContentScript,
+ sendToContentScriptResilient,
sendToMailContentScriptResilient,
setState,
sleepWithStop,
@@ -30,6 +31,12 @@
VERIFICATION_POLL_MAX_ROUNDS,
} = deps;
+ const isRetryableVerificationTransportError = typeof deps.isRetryableContentScriptTransportError === 'function'
+ ? deps.isRetryableContentScriptTransportError
+ : ((error) => /back\/forward cache|message channel is closed|Receiving end does not exist|port closed before a response was received|A listener indicated an asynchronous response|did not respond in \d+s/i.test(
+ String(typeof error === 'string' ? error : error?.message || '')
+ ));
+
function getVerificationCodeStateKey(step) {
return step === 4 ? 'lastSignupCode' : 'lastLoginCode';
}
@@ -38,6 +45,89 @@
return step === 4 ? '注册' : '登录';
}
+ function isLikelyLoggedInChatgptHomeUrl(rawUrl) {
+ const url = String(rawUrl || '').trim();
+ if (!url) return false;
+
+ try {
+ const parsed = new URL(url);
+ const host = String(parsed.hostname || '').toLowerCase();
+ if (!['chatgpt.com', 'www.chatgpt.com'].includes(host)) {
+ return false;
+ }
+ const path = String(parsed.pathname || '');
+ if (/^\/(?:auth\/|create-account\/|email-verification|log-in|add-phone)(?:[/?#]|$)/i.test(path)) {
+ return false;
+ }
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ function isSignupProfilePageUrl(rawUrl) {
+ const url = String(rawUrl || '').trim();
+ if (!url) return false;
+
+ try {
+ const parsed = new URL(url);
+ const host = String(parsed.hostname || '').toLowerCase();
+ if (!['auth.openai.com', 'auth0.openai.com', 'accounts.openai.com'].includes(host)) {
+ return false;
+ }
+ return /\/create-account\/profile(?:[/?#]|$)/i.test(String(parsed.pathname || ''));
+ } catch {
+ return false;
+ }
+ }
+
+ async function detectStep4PostSubmitFallback(tabId, options = {}) {
+ const timeoutMs = Math.max(1000, Number(options.timeoutMs) || 8000);
+ const pollIntervalMs = Math.max(100, Number(options.pollIntervalMs) || 250);
+ const startedAt = Date.now();
+ let lastUrl = '';
+
+ while (Date.now() - startedAt < timeoutMs) {
+ throwIfStopped();
+ try {
+ const tab = await chrome.tabs.get(tabId);
+ const currentUrl = String(tab?.url || '').trim();
+ if (currentUrl) {
+ lastUrl = currentUrl;
+ }
+
+ if (isLikelyLoggedInChatgptHomeUrl(currentUrl)) {
+ return {
+ success: true,
+ reason: 'chatgpt_home',
+ skipProfileStep: true,
+ url: currentUrl,
+ };
+ }
+
+ if (isSignupProfilePageUrl(currentUrl)) {
+ return {
+ success: true,
+ reason: 'signup_profile',
+ skipProfileStep: false,
+ url: currentUrl,
+ };
+ }
+ } catch {
+ // Keep polling until timeout; tab may be mid-navigation.
+ }
+
+ await sleepWithStop(pollIntervalMs);
+ }
+
+ return {
+ success: false,
+ reason: 'unknown',
+ skipProfileStep: false,
+ url: lastUrl,
+ };
+ }
+
function getVerificationResendStateKey() {
return 'verificationResendCount';
}
@@ -448,6 +538,17 @@
`步骤 ${step}:距离下次重新发送验证码还差 ${Math.ceil(remainingBeforeResendMs / 1000)} 秒,继续刷新邮箱(第 ${round}/${maxRounds} 轮)...`,
'info'
);
+ const configuredIntervalMs = Math.max(
+ 1,
+ Number(payloadOverrides.intervalMs)
+ || Number(pollOverrides.intervalMs)
+ || 3000
+ );
+ const cooldownSleepMs = Math.min(
+ remainingBeforeResendMs,
+ Math.max(1000, Math.min(configuredIntervalMs, 3000))
+ );
+ await sleepWithStop(cooldownSleepMs);
continue;
}
@@ -597,19 +698,54 @@
}
await chrome.tabs.update(signupTabId, { active: true });
- const result = await sendToContentScript('signup-page', {
+ const baseResponseTimeoutMs = await getResponseTimeoutMsForStep(
+ step,
+ options,
+ step === 7 ? 45000 : 30000,
+ `填写${getVerificationCodeLabel(step)}验证码`
+ );
+ const message = {
type: 'FILL_CODE',
step,
source: 'background',
payload: { code },
- }, {
- responseTimeoutMs: await getResponseTimeoutMsForStep(
- step,
- options,
- step === 7 ? 45000 : 30000,
- `填写${getVerificationCodeLabel(step)}验证码`
- ),
- });
+ };
+ let result;
+ if (typeof sendToContentScriptResilient === 'function') {
+ try {
+ result = await sendToContentScriptResilient('signup-page', message, {
+ timeoutMs: Math.max(baseResponseTimeoutMs + 15000, 30000),
+ retryDelayMs: 700,
+ responseTimeoutMs: baseResponseTimeoutMs,
+ logMessage: `步骤 ${step}:认证页正在切换,等待页面重新就绪后继续确认验证码提交结果...`,
+ });
+ } catch (err) {
+ if (step === 4 && isRetryableVerificationTransportError(err)) {
+ const fallback = await detectStep4PostSubmitFallback(signupTabId, {
+ timeoutMs: 9000,
+ pollIntervalMs: 300,
+ });
+ if (fallback.success) {
+ const fallbackLabel = fallback.reason === 'chatgpt_home'
+ ? 'ChatGPT 已登录首页'
+ : '注册资料页';
+ await addLog(`步骤 4:验证码提交后页面已切换到${fallbackLabel},按提交成功继续。`, 'warn');
+ return {
+ success: true,
+ assumed: true,
+ transportRecovered: true,
+ skipProfileStep: Boolean(fallback.skipProfileStep),
+ url: fallback.url,
+ };
+ }
+ }
+ throw err;
+ }
+ } else {
+ result = await sendToContentScript('signup-page', message, {
+ responseTimeoutMs: baseResponseTimeoutMs,
+ });
+ }
if (result && result.error) {
throw new Error(result.error);
@@ -735,6 +871,7 @@
`步骤 ${step}:提交失败后距离下次重新发送验证码还差 ${Math.ceil(remainingBeforeResendMs / 1000)} 秒,先继续刷新邮箱(${attempt + 1}/${maxSubmitAttempts})...`,
'warn'
);
+ await sleepWithStop(Math.min(remainingBeforeResendMs, 2000));
continue;
}
@@ -763,6 +900,7 @@
await completeStepFromBackground(step, {
emailTimestamp: result.emailTimestamp,
code: result.code,
+ ...(step === 4 && submitResult?.skipProfileStep ? { skipProfileStep: true } : {}),
});
triggerPostSuccessMailboxCleanup(step, mail);
return;
diff --git a/content/signup-page.js b/content/signup-page.js
index 0b74b3e..91c901d 100644
--- a/content/signup-page.js
+++ b/content/signup-page.js
@@ -2122,6 +2122,20 @@ async function fillVerificationCode(step, payload) {
const { code } = payload;
if (!code) throw new Error('未提供验证码。');
+ if (step === 4 && isStep5Ready()) {
+ log(`步骤 ${step}:检测到页面已进入下一阶段,本次验证码提交按成功处理。`, 'ok');
+ return { success: true, assumed: true, alreadyAdvanced: true };
+ }
+ if (step === 8) {
+ if (isStep8Ready()) {
+ log(`步骤 ${step}:检测到页面已进入 OAuth 同意页,本次验证码提交按成功处理。`, 'ok');
+ return { success: true, assumed: true, alreadyAdvanced: true };
+ }
+ if (isAddPhonePageReady()) {
+ return { success: true, addPhonePage: true, url: location.href };
+ }
+ }
+
log(`步骤 ${step}:正在填写验证码:${code}`);
if (step === 8) {
diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html
index 7dc4b30..8436557 100644
--- a/sidepanel/sidepanel.html
+++ b/sidepanel/sidepanel.html
@@ -647,6 +647,13 @@
+
+ 获取策略
+
+
自动删除