fix: clear signup phone mismatch runtime state

This commit is contained in:
QLHazyCoder
2026-05-07 02:25:23 +08:00
parent 12f35f1d3b
commit 8cc8007150
5 changed files with 99 additions and 86 deletions
+73 -62
View File
@@ -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,47 +10121,27 @@ async function runAutoSequenceFromStep(startStep, context = {}) {
throw err;
}
step4RestartCount += 1;
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 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)}`,
`步骤 4:执行失败,准备沿用当前邮箱回到步骤 1 重新开始(第 ${step4RestartCount} 次重开)。${emailSuffix}原因:${getErrorMessage(err)}`,
'warn'
);
await invalidateDownstreamAfterStepRestart(1, {
logLabel: isSignupPhonePasswordMismatch
? `步骤 4 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 ${step4RestartCount} 次重开)`
: `步骤 4 报错后准备回到步骤 1 沿用当前邮箱重试(第 ${step4RestartCount} 次重开)`,
logLabel: `步骤 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 = '';
}
await addLog('步骤 4:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。', 'warn');
}
if (Object.keys(restorePayload).length) {
await setState(restorePayload);
}
}
currentStartStep = 1;
continueCurrentAttempt = true;
restartFromStep1WithCurrentEmail = true;
+5 -9
View File
@@ -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',
+5
View File
@@ -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);
+1
View File
@@ -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 重新获取号码并重开当前轮,避免继续在密码页重复点击
补充:
+2 -2
View File
@@ -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`:测试共享步骤定义模块及侧边栏脚本加载顺序。