From 8cc800715009dcd56383c26ef1b30fb3b23f37eb Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Thu, 7 May 2026 02:25:23 +0800 Subject: [PATCH] fix: clear signup phone mismatch runtime state --- background.js | 161 +++++++++++++------------ tests/auto-run-step4-restart.test.js | 14 +-- tests/signup-entry-diagnostics.test.js | 5 + 项目完整链路说明.md | 1 + 项目文件结构说明.md | 4 +- 5 files changed, 99 insertions(+), 86 deletions(-) diff --git a/background.js b/background.js index e96018e..4e3c3df 100644 --- a/background.js +++ b/background.js @@ -7307,6 +7307,72 @@ function isSignupPhonePasswordMismatchFailure(error) { return /SIGNUP_PHONE_PASSWORD_MISMATCH::/i.test(message); } +function getSignupPhonePasswordMismatchRestartPayload(preservedState = {}) { + const preservedEmail = String(preservedState.email || '').trim(); + const preservedPassword = String(preservedState.password || '').trim(); + const accountIdentifierType = String(preservedState.accountIdentifierType || '').trim().toLowerCase(); + const activeSignupPhoneNumber = String( + preservedState.signupPhoneNumber + || preservedState.signupPhoneActivation?.phoneNumber + || preservedState.signupPhoneCompletedActivation?.phoneNumber + || (accountIdentifierType === 'phone' ? preservedState.accountIdentifier : '') + || '' + ).trim(); + const shouldClearSignupPhoneRuntime = Boolean( + activeSignupPhoneNumber + || preservedState.signupPhoneActivation + || preservedState.signupPhoneCompletedActivation + || preservedState.signupPhoneVerificationRequestedAt + || preservedState.signupPhoneVerificationPurpose + || accountIdentifierType === 'phone' + ); + const restorePayload = {}; + if (preservedEmail) restorePayload.email = preservedEmail; + if (preservedPassword) restorePayload.password = preservedPassword; + if (shouldClearSignupPhoneRuntime) { + restorePayload.signupPhoneNumber = ''; + restorePayload.signupPhoneActivation = null; + restorePayload.signupPhoneCompletedActivation = null; + restorePayload.signupPhoneVerificationRequestedAt = null; + restorePayload.signupPhoneVerificationPurpose = ''; + if (accountIdentifierType === 'phone') { + restorePayload.accountIdentifierType = null; + restorePayload.accountIdentifier = ''; + } + } + return { + activeSignupPhoneNumber, + preservedEmail, + restorePayload, + shouldClearSignupPhoneRuntime, + }; +} + +async function restartSignupPhonePasswordMismatchAttemptFromStep(step, restartCount, error) { + const preservedState = await getState(); + const { + activeSignupPhoneNumber, + preservedEmail, + restorePayload, + shouldClearSignupPhoneRuntime, + } = getSignupPhonePasswordMismatchRestartPayload(preservedState); + const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : ''; + const phoneSuffix = activeSignupPhoneNumber ? `当前手机号:${activeSignupPhoneNumber};` : ''; + await addLog( + `步骤 ${step}:检测到手机号/密码不匹配,准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${restartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${getErrorMessage(error)}`, + 'warn' + ); + await invalidateDownstreamAfterStepRestart(1, { + logLabel: `步骤 ${step} 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 ${restartCount} 次重开)`, + }); + if (shouldClearSignupPhoneRuntime) { + await addLog(`步骤 ${step}:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。`, 'warn'); + } + if (Object.keys(restorePayload).length) { + await setState(restorePayload); + } +} + function isSignupUserAlreadyExistsFailure(error) { if (typeof loggingStatus !== 'undefined' && loggingStatus?.isSignupUserAlreadyExistsFailure) { return loggingStatus.isSignupUserAlreadyExistsFailure(error); @@ -9984,42 +10050,7 @@ async function runAutoSequenceFromStep(startStep, context = {}) { } if (isSignupPhonePasswordMismatchFailure(err)) { step4RestartCount += 1; - const preservedState = await getState(); - const preservedEmail = String(preservedState.email || '').trim(); - const preservedPassword = String(preservedState.password || '').trim(); - const activeSignupPhoneNumber = String( - preservedState.signupPhoneNumber - || preservedState.signupPhoneActivation?.phoneNumber - || preservedState.signupPhoneCompletedActivation?.phoneNumber - || '' - ).trim(); - const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : ''; - const phoneSuffix = activeSignupPhoneNumber ? `当前手机号:${activeSignupPhoneNumber};` : ''; - await addLog( - `步骤 3:检测到手机号/密码不匹配,准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${step4RestartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${getErrorMessage(err)}`, - 'warn' - ); - await invalidateDownstreamAfterStepRestart(1, { - logLabel: `步骤 3 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 ${step4RestartCount} 次重开)`, - }); - const restorePayload = {}; - if (preservedEmail) restorePayload.email = preservedEmail; - if (preservedPassword) restorePayload.password = preservedPassword; - if (preservedState.signupPhoneActivation) { - restorePayload.signupPhoneNumber = ''; - restorePayload.signupPhoneActivation = null; - restorePayload.signupPhoneCompletedActivation = null; - restorePayload.signupPhoneVerificationRequestedAt = null; - restorePayload.signupPhoneVerificationPurpose = ''; - if (String(preservedState.accountIdentifierType || '').trim().toLowerCase() === 'phone') { - restorePayload.accountIdentifierType = null; - restorePayload.accountIdentifier = ''; - } - await addLog('步骤 3:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。', 'warn'); - } - if (Object.keys(restorePayload).length) { - await setState(restorePayload); - } + await restartSignupPhonePasswordMismatchAttemptFromStep(3, step4RestartCount, err); currentStartStep = 1; continueCurrentAttempt = true; restartFromStep1WithCurrentEmail = true; @@ -10090,46 +10121,26 @@ async function runAutoSequenceFromStep(startStep, context = {}) { throw err; } step4RestartCount += 1; - const preservedState = await getState(); - const preservedEmail = String(preservedState.email || '').trim(); - const preservedPassword = String(preservedState.password || '').trim(); - const isSignupPhonePasswordMismatch = /SIGNUP_PHONE_PASSWORD_MISMATCH::/i.test(getErrorMessage(err)); - const activeSignupPhoneNumber = String( - preservedState.signupPhoneNumber - || preservedState.signupPhoneActivation?.phoneNumber - || preservedState.signupPhoneCompletedActivation?.phoneNumber - || '' - ).trim(); - const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : ''; - const phoneSuffix = activeSignupPhoneNumber ? `当前手机号:${activeSignupPhoneNumber};` : ''; - await addLog( - isSignupPhonePasswordMismatch - ? `步骤 4:检测到手机号/密码不匹配,准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${step4RestartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${getErrorMessage(err)}` - : `步骤 4:执行失败,准备沿用当前邮箱回到步骤 1 重新开始(第 ${step4RestartCount} 次重开)。${emailSuffix}原因:${getErrorMessage(err)}`, - 'warn' - ); - await invalidateDownstreamAfterStepRestart(1, { - logLabel: isSignupPhonePasswordMismatch - ? `步骤 4 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 ${step4RestartCount} 次重开)` - : `步骤 4 报错后准备回到步骤 1 沿用当前邮箱重试(第 ${step4RestartCount} 次重开)`, - }); - const restorePayload = {}; - if (preservedEmail) restorePayload.email = preservedEmail; - if (preservedPassword) restorePayload.password = preservedPassword; - if (isSignupPhonePasswordMismatch && preservedState.signupPhoneActivation) { - restorePayload.signupPhoneNumber = ''; - restorePayload.signupPhoneActivation = null; - restorePayload.signupPhoneCompletedActivation = null; - restorePayload.signupPhoneVerificationRequestedAt = null; - restorePayload.signupPhoneVerificationPurpose = ''; - if (String(preservedState.accountIdentifierType || '').trim().toLowerCase() === 'phone') { - restorePayload.accountIdentifierType = null; - restorePayload.accountIdentifier = ''; + if (isSignupPhonePasswordMismatchFailure(err)) { + await restartSignupPhonePasswordMismatchAttemptFromStep(4, step4RestartCount, err); + } else { + const preservedState = await getState(); + const preservedEmail = String(preservedState.email || '').trim(); + const preservedPassword = String(preservedState.password || '').trim(); + const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : ''; + await addLog( + `步骤 4:执行失败,准备沿用当前邮箱回到步骤 1 重新开始(第 ${step4RestartCount} 次重开)。${emailSuffix}原因:${getErrorMessage(err)}`, + 'warn' + ); + await invalidateDownstreamAfterStepRestart(1, { + logLabel: `步骤 4 报错后准备回到步骤 1 沿用当前邮箱重试(第 ${step4RestartCount} 次重开)`, + }); + const restorePayload = {}; + if (preservedEmail) restorePayload.email = preservedEmail; + if (preservedPassword) restorePayload.password = preservedPassword; + if (Object.keys(restorePayload).length) { + await setState(restorePayload); } - await addLog('步骤 4:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。', 'warn'); - } - if (Object.keys(restorePayload).length) { - await setState(restorePayload); } currentStartStep = 1; continueCurrentAttempt = true; diff --git a/tests/auto-run-step4-restart.test.js b/tests/auto-run-step4-restart.test.js index 6e884af..6ee2104 100644 --- a/tests/auto-run-step4-restart.test.js +++ b/tests/auto-run-step4-restart.test.js @@ -57,6 +57,8 @@ const bundle = [ extractFunction('isAddPhoneAuthState'), extractFunction('isMail2925ThreadTerminatedError'), extractFunction('isSignupPhonePasswordMismatchFailure'), + extractFunction('getSignupPhonePasswordMismatchRestartPayload'), + extractFunction('restartSignupPhonePasswordMismatchAttemptFromStep'), extractFunction('isSignupUserAlreadyExistsFailure'), extractFunction('getPostStep6AutoRestartDecision'), extractFunction('runAutoSequenceFromStep'), @@ -621,7 +623,7 @@ return { assert.equal(events.logs.some(({ message }) => /已清空本轮注册手机号与接码订单/.test(message)), true); }); -test('auto-run restarts from step 1 immediately when step 3 detects phone/password mismatch', async () => { +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 }; const LAST_STEP_ID = 10; @@ -644,14 +646,8 @@ let currentState = { accountIdentifierType: 'phone', accountIdentifier: '+56988841722', signupPhoneNumber: '+56988841722', - signupPhoneActivation: { - activationId: 'act-1', - phoneNumber: '+56988841722', - }, - signupPhoneCompletedActivation: { - activationId: 'act-1', - phoneNumber: '+56988841722', - }, + signupPhoneActivation: null, + signupPhoneCompletedActivation: null, stepStatuses: { 1: 'pending', 2: 'pending', diff --git a/tests/signup-entry-diagnostics.test.js b/tests/signup-entry-diagnostics.test.js index d32df30..a0ff73b 100644 --- a/tests/signup-entry-diagnostics.test.js +++ b/tests/signup-entry-diagnostics.test.js @@ -450,6 +450,10 @@ function getSignupPasswordDisplayedEmail() { return 'user@example.com'; } +function getSignupPasswordFieldErrorText() { + return ''; +} + function findOneTimeCodeLoginTrigger() { return oneTimeCodeButton; } @@ -474,6 +478,7 @@ return { assert.equal(result.url, 'https://auth.openai.com/create-account/password'); assert.equal(result.displayedEmail, 'user@example.com'); + assert.equal(result.passwordErrorText, ''); assert.equal(result.hasVisiblePasswordInput, true); assert.equal(result.passwordInputCount, 1); assert.equal(result.visiblePasswordInputCount, 1); diff --git a/项目完整链路说明.md b/项目完整链路说明.md index 34844fd..643ddcb 100644 --- a/项目完整链路说明.md +++ b/项目完整链路说明.md @@ -352,6 +352,7 @@ IP 代理模块在同步、切换、Change、出口探测和自动运行成功 7. 延迟提交真正触发前会再次检查 Stop 状态,避免用户已停止时页面仍继续自动提交 8. 后台在真正确认 Step 3 完成前,会额外检查提交后是否切换页面;如果出现认证页 `Try again / 重试` 页面,或 `/email-verification` 上的 `405 / Route Error` 重试页,会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复,再继续后续链路 9. Step 3 收尾阶段如果页面切换导致旧内容脚本失联,后台会把单次消息等待收口到当前收尾预算内,优先尽快重试重连;若最终仍未恢复,则输出中文的步骤级错误,而不是直接暴露底层英文通信超时 +10. 手机号注册时,如果 Step 3 收尾或 Step 4 准备验证码阶段在密码页检测到 `Incorrect phone number or password`,后台会把它视为当前注册手机号/密码不匹配:清空本轮 `signupPhoneNumber / signupPhoneActivation / signupPhoneCompletedActivation` 和手机号账号身份,回到 Step 1 重新获取号码并重开当前轮,避免继续在密码页重复点击 补充: diff --git a/项目文件结构说明.md b/项目文件结构说明.md index 4164348..e16a5e2 100644 --- a/项目文件结构说明.md +++ b/项目文件结构说明.md @@ -93,7 +93,7 @@ - `content/phone-auth.js`:认证页手机号验证脚本,负责识别 `add-phone / phone-verification` 页面、选择国家、填写手机号、提交短信验证码、触发重发短信,以及把“回到 add-phone / 进入 OAuth 同意页”的结果反馈给后台。 - `content/plus-checkout.js`:ChatGPT Plus checkout 页面脚本,负责读取 `/api/auth/session` 创建 Plus checkout、选择 PayPal、填写账单姓名、触发 Google 地址推荐、校验结构化地址字段并点击订阅。 - `content/qq-mail.js`:QQ 邮箱轮询脚本,负责网页邮箱验证码读取。 -- `content/signup-page.js`:注册、登录、授权主内容脚本,负责 OpenAI / ChatGPT 页面上的步骤执行;其中 Step 2 / 3 的延迟点击与延迟提交在真正触发前会先检查 Stop 状态,避免停止后页面继续自动点击;当前 Step 2 同时支持邮箱注册与手机号注册入口切换、手机号国家下拉框同步校验、手机号填写与提交;Step 7 / 8 登录链路会额外识别手机号输入页、手机号登录入口、`phone-verification` 与真实 `add-email` 页面,避免把手机验证码页或添加邮箱页误判成普通邮箱登录入口;Step 9 后置手机号验证完成后若偶发进入资料页,会复用 Step 5 的姓名生日填写逻辑再继续等待 OAuth 同意页。 +- `content/signup-page.js`:注册、登录、授权主内容脚本,负责 OpenAI / ChatGPT 页面上的步骤执行;其中 Step 2 / 3 的延迟点击与延迟提交在真正触发前会先检查 Stop 状态,避免停止后页面继续自动点击;当前 Step 2 同时支持邮箱注册与手机号注册入口切换、手机号国家下拉框同步校验、手机号填写与提交;Step 3 收尾会识别手机号注册密码页的 `Incorrect phone number or password` 并上报当前轮重开信号;Step 7 / 8 登录链路会额外识别手机号输入页、手机号登录入口、`phone-verification` 与真实 `add-email` 页面,避免把手机验证码页或添加邮箱页误判成普通邮箱登录入口;Step 9 后置手机号验证完成后若偶发进入资料页,会复用 Step 5 的姓名生日填写逻辑再继续等待 OAuth 同意页。 - `content/sub2api-panel.js`:SUB2API 后台内容脚本,负责获取 OAuth 地址和提交 localhost 回调;平台验证请求会读取 `visibleStep`,普通模式承接步骤 10,Plus 模式承接步骤 13。 - `content/utils.js`:内容脚本公共工具层,负责结构化日志、READY / COMPLETE / ERROR 上报、元素等待、输入与点击;内容脚本日志通过 `{ step, stepKey }` 上报,正文不再承担步骤号解析职责。 - `content/vps-panel.js`:CPA 面板内容脚本,负责获取 OAuth 地址、提交回调 URL,并基于精确成功徽标与错误态做平台验证判定;当前支持普通步骤 10 与 Plus 可见步骤 13。 @@ -243,7 +243,7 @@ - `tests/sidepanel-mail2925-manager.test.js`:测试侧边栏 2925 管理器模块接线、共享号池表单脚本加载顺序、显隐交互与空态渲染。 - `tests/sidepanel-mail2925-mode.test.js`:测试侧边栏保留 `2925 provide / receive` 模式行与独立的号池配置行,并验证 sidepanel 只在 provide 模式下把 2925 视为别名邮箱 provider。 - `tests/sidepanel-paypal-manager.test.js`:测试侧边栏公共表单弹窗脚本与 PayPal manager 的加载顺序,以及 PayPal 账号保存后会立即选中当前账号。 -- `tests/signup-entry-diagnostics.test.js`:测试注册入口诊断快照输出。 +- `tests/signup-entry-diagnostics.test.js`:测试注册入口与密码页诊断快照输出,包括密码页错误文案字段。 - `tests/signup-step2-email-switch.test.js`:测试 Step 2 在手机号输入模式下切回邮箱输入模式、本地化邮箱输入框识别,以及手机号注册时国家下拉框可视区号同步。 - `tests/signup-page-tab-cleanup.test.js`:测试注册页来源标签的冲突清理逻辑。 - `tests/step-definitions-module.test.js`:测试共享步骤定义模块及侧边栏脚本加载顺序。