diff --git a/background.js b/background.js index 289757c..1a6ccb2 100644 --- a/background.js +++ b/background.js @@ -603,6 +603,11 @@ function isRetryableContentScriptTransportError(error) { return /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/i.test(message); } +function isRestartCurrentAttemptError(error) { + const message = String(typeof error === 'string' ? error : error?.message || ''); + return /当前邮箱已存在,需要重新开始新一轮/.test(message); +} + function isStepDoneStatus(status) { return status === 'completed' || status === 'manual_completed' || status === 'skipped'; } @@ -1297,7 +1302,8 @@ async function autoRunLoop(totalRuns, options = {}) { const resumeCurrentRun = Number.isInteger(options.resumeCurrentRun) ? options.resumeCurrentRun : 0; const resumeSuccessfulRuns = Number.isInteger(options.resumeSuccessfulRuns) ? options.resumeSuccessfulRuns : 0; const resumeAttemptRunsProcessed = Number.isInteger(options.resumeAttemptRunsProcessed) ? options.resumeAttemptRunsProcessed : 0; - const maxAttempts = autoRunSkipFailures ? Math.max(totalRuns * 10, totalRuns + 20) : totalRuns; + let maxAttempts = autoRunSkipFailures ? Math.max(totalRuns * 10, totalRuns + 20) : totalRuns; + const forcedRetryCap = Math.max(totalRuns * 10, totalRuns + 20); let successfulRuns = Math.max(0, resumeSuccessfulRuns); let attemptRuns = Math.max(0, resumeAttemptRunsProcessed); let forceFreshTabsNextRun = false; @@ -1392,6 +1398,20 @@ async function autoRunLoop(totalRuns, options = {}) { break; } + if (isRestartCurrentAttemptError(err)) { + await addLog(`目标 ${targetRun}/${totalRuns} 轮检测到当前邮箱已存在,当前线程已放弃,将重新开始新一轮。`, 'warn'); + cancelPendingCommands('当前线程因邮箱已存在而放弃。'); + await broadcastStopToContentScripts(); + await broadcastAutoRunStatus('retrying', { + currentRun: targetRun, + totalRuns, + attemptRun: attemptRuns, + }); + forceFreshTabsNextRun = true; + maxAttempts = Math.max(maxAttempts, Math.min(forcedRetryCap, attemptRuns + 1)); + continue; + } + if (!autoRunSkipFailures) { await addLog(`目标 ${targetRun}/${totalRuns} 轮失败:${err.message}`, 'error'); await broadcastAutoRunStatus('stopped', { diff --git a/content/signup-page.js b/content/signup-page.js index 0992357..338fe0e 100644 --- a/content/signup-page.js +++ b/content/signup-page.js @@ -359,6 +359,7 @@ const ADD_PHONE_PAGE_PATTERN = /add[\s-]*phone|添加手机号|手机号码|手 const STEP5_SUBMIT_ERROR_PATTERN = /无法根据该信息创建帐户|请重试|unable\s+to\s+create\s+(?:your\s+)?account|couldn'?t\s+create\s+(?:your\s+)?account|something\s+went\s+wrong|invalid\s+(?:birthday|birth|date)|生日|出生日期/i; const SIGNUP_PASSWORD_ERROR_TITLE_PATTERN = /糟糕,出错了|something\s+went\s+wrong|oops/i; const SIGNUP_PASSWORD_ERROR_DETAIL_PATTERN = /operation\s+timed\s+out|timed\s+out|请求超时|操作超时/i; +const SIGNUP_EMAIL_EXISTS_PATTERN = /与此电子邮件地址相关联的帐户已存在|account\s+associated\s+with\s+this\s+email\s+address\s+already\s+exists|email\s+address.*already\s+exists/i; function getVerificationErrorText() { const messages = []; @@ -609,6 +610,10 @@ function isSignupPasswordErrorPage() { ); } +function isSignupEmailAlreadyExistsPage() { + return isSignupPasswordPage() && SIGNUP_EMAIL_EXISTS_PATTERN.test(getPageTextSnapshot()); +} + function inspectSignupVerificationState() { if (isStep5Ready()) { return { state: 'step5' }; @@ -625,6 +630,10 @@ function inspectSignupVerificationState() { }; } + if (isSignupEmailAlreadyExistsPage()) { + return { state: 'email_exists' }; + } + const passwordInput = getSignupPasswordInput(); if (passwordInput) { return { @@ -644,7 +653,7 @@ async function waitForSignupVerificationTransition(timeout = 5000) { throwIfStopped(); const snapshot = inspectSignupVerificationState(); - if (snapshot.state === 'step5' || snapshot.state === 'verification' || snapshot.state === 'error') { + if (snapshot.state === 'step5' || snapshot.state === 'verification' || snapshot.state === 'error' || snapshot.state === 'email_exists') { return snapshot; } @@ -677,6 +686,10 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) { return { ready: true, retried: recoveryRound }; } + if (snapshot.state === 'email_exists') { + throw new Error('当前邮箱已存在,需要重新开始新一轮。'); + } + recoveryRound += 1; if (snapshot.state === 'error') {