diff --git a/background.js b/background.js index 631f9ea..05e6e94 100644 --- a/background.js +++ b/background.js @@ -7676,12 +7676,20 @@ async function restartSignupPhonePasswordMismatchAttemptFromStep(step, restartCo } = getSignupPhonePasswordMismatchRestartPayload(preservedState); const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : ''; const phoneSuffix = activeSignupPhoneNumber ? `当前手机号:${activeSignupPhoneNumber};` : ''; + const errorMessage = getErrorMessage(error); + const reasonLabel = /PHONE_RESEND_BANNED_NUMBER::|无法向此(?:电话|手机)号码发送短信|无法发送短信到此(?:电话|手机)号码|unable\s+to\s+send\s+(?:an?\s+)?(?:sms|text(?:\s+message)?)\s+to\s+(?:this|that)\s+(?:phone\s+)?number/i + .test(errorMessage) + ? '当前注册手机号无法接收短信' + : (/与此(?:电话|手机)号码相关联的帐户已存在|account\s+associated\s+with\s+this\s+phone\s+number\s+already\s+exists/i + .test(errorMessage) + ? '注册手机号异常' + : '手机号/密码不匹配'); await addLog( - `步骤 ${step}:检测到手机号/密码不匹配,准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${restartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${getErrorMessage(error)}`, + `步骤 ${step}:检测到${reasonLabel},准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${restartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${errorMessage}`, 'warn' ); await invalidateDownstreamAfterStepRestart(1, { - logLabel: `步骤 ${step} 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 ${restartCount} 次重开)`, + logLabel: `步骤 ${step} 检测到${reasonLabel}后准备回到步骤 1 重新获取手机号重试(第 ${restartCount} 次重开)`, }); if (shouldClearSignupPhoneRuntime) { await addLog(`步骤 ${step}:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。`, 'warn'); @@ -10504,7 +10512,10 @@ async function runAutoSequenceFromStep(startStep, context = {}) { throw err; } step4RestartCount += 1; - if (isSignupPhonePasswordMismatchFailure(err)) { + const isPhoneResendBanned = typeof phoneVerificationHelpers !== 'undefined' + && typeof phoneVerificationHelpers?.isPhoneResendBannedNumberError === 'function' + && phoneVerificationHelpers.isPhoneResendBannedNumberError(err); + if (isSignupPhonePasswordMismatchFailure(err) || isPhoneResendBanned) { await restartSignupPhonePasswordMismatchAttemptFromStep(4, step4RestartCount, err); } else { const preservedState = await getState(); diff --git a/background/phone-verification-flow.js b/background/phone-verification-flow.js index e597c1d..62331d4 100644 --- a/background/phone-verification-flow.js +++ b/background/phone-verification-flow.js @@ -6288,6 +6288,7 @@ completeSignupPhoneVerificationFlow, finalizeLoginPhoneActivationAfterSuccess, finalizeSignupPhoneActivationAfterSuccess, + isPhoneResendBannedNumberError, normalizeActivation, pollPhoneActivationCode, prepareLoginPhoneActivation, diff --git a/tests/auto-run-step4-restart.test.js b/tests/auto-run-step4-restart.test.js index 6ee2104..823e00c 100644 --- a/tests/auto-run-step4-restart.test.js +++ b/tests/auto-run-step4-restart.test.js @@ -623,6 +623,170 @@ return { assert.equal(events.logs.some(({ message }) => /已清空本轮注册手机号与接码订单/.test(message)), true); }); +test('auto-run clears fetched signup phone state before restarting when step 4 cannot send text to this phone number', async () => { + const api = new Function(` +const AUTO_STEP_DELAYS = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 }; +const LAST_STEP_ID = 10; +const FINAL_OAUTH_CHAIN_START_STEP = 7; +const SIGNUP_METHOD_PHONE = 'phone'; +const chrome = { + tabs: { + update: async () => {}, + }, + runtime: { + sendMessage: async () => {}, + }, +}; + +let remainingFailures = 1; +let currentState = { + email: '', + password: 'Secret123!', + signupMethod: 'phone', + accountIdentifierType: 'phone', + accountIdentifier: '+56988841722', + signupPhoneNumber: '+56988841722', + signupPhoneActivation: { activationId: 'signup-activation', phoneNumber: '+56988841722' }, + signupPhoneCompletedActivation: { activationId: 'signup-completed', phoneNumber: '+56988841722' }, + signupPhoneVerificationRequestedAt: 123456, + signupPhoneVerificationPurpose: 'signup', + stepStatuses: { + 1: 'pending', + 2: 'pending', + 3: 'pending', + 4: 'pending', + 5: 'pending', + 6: 'pending', + 7: 'pending', + 8: 'pending', + 9: 'pending', + 10: 'pending', + }, +}; +const events = { + steps: [], + invalidations: [], + logs: [], + setStateCalls: [], +}; + +async function addLog(message, level = 'info') { + events.logs.push({ message, level }); +} + +async function ensureAutoEmailReady() { + return ''; +} + +async function broadcastAutoRunStatus() {} +async function ensureResolvedSignupMethodForRun() { return 'phone'; } + +async function getState() { + return currentState; +} + +async function setState(updates) { + currentState = { + ...currentState, + ...updates, + stepStatuses: updates.stepStatuses ? { ...updates.stepStatuses } : currentState.stepStatuses, + }; + events.setStateCalls.push(updates); +} + +function isStopError(error) { + return (error?.message || String(error || '')) === '流程已被用户停止。'; +} + +function isStepDoneStatus(status) { + return status === 'completed' || status === 'manual_completed' || status === 'skipped'; +} + +async function executeStepAndWait(step) { + events.steps.push(step); + if (step === 4 && remainingFailures > 0) { + remainingFailures -= 1; + throw new Error('PHONE_RESEND_BANNED_NUMBER::Unable to send a text message to this phone number'); + } +} + +async function getTabId() { + return 1; +} + +async function invalidateDownstreamAfterStepRestart(step, options = {}) { + events.invalidations.push({ step, options }); + currentState = { + ...currentState, + password: null, + stepStatuses: { + 1: currentState.stepStatuses[1] || 'completed', + 2: 'pending', + 3: 'pending', + 4: 'pending', + 5: 'pending', + 6: 'pending', + 7: 'pending', + 8: 'pending', + 9: 'pending', + 10: 'pending', + }, + }; +} + +function getLoginAuthStateLabel(state) { + return state || 'unknown'; +} + +function getErrorMessage(error) { + return error?.message || String(error || ''); +} + +const phoneVerificationHelpers = { + isPhoneResendBannedNumberError(error) { + return String(error?.message || error || '').startsWith('PHONE_RESEND_BANNED_NUMBER::'); + }, +}; + +async function getLoginAuthStateFromContent() { + return { state: 'password_page', url: 'https://auth.openai.com/log-in' }; +} + +${bundle} + +return { + async run() { + await runAutoSequenceFromStep(1, { + targetRun: 1, + totalRuns: 1, + attemptRuns: 1, + continued: false, + }); + return { events, currentState }; + }, +}; +`)(); + + const { events, currentState } = await api.run(); + + assert.deepStrictEqual(events.invalidations, [ + { + step: 1, + options: { + logLabel: '步骤 4 检测到当前注册手机号无法接收短信后准备回到步骤 1 重新获取手机号重试(第 1 次重开)', + }, + }, + ]); + assert.equal(currentState.signupPhoneNumber, ''); + assert.equal(currentState.signupPhoneActivation, null); + assert.equal(currentState.signupPhoneCompletedActivation, null); + assert.equal(currentState.accountIdentifierType, null); + assert.equal(currentState.accountIdentifier, ''); + assert.equal(currentState.password, 'Secret123!'); + assert.equal(events.logs.some(({ message }) => /丢弃当前注册手机号并回到步骤 1 重新开始/.test(message)), true); + assert.equal(events.logs.some(({ message }) => /已清空本轮注册手机号与接码订单/.test(message)), true); +}); + test('auto-run clears manual signup phone state when step 3 detects phone/password mismatch', async () => { const api = new Function(` const AUTO_STEP_DELAYS = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 };