fix: restart phone signup when SMS send is rejected
- 合并 PR #217 的核心改动:将 Step 4 无法向当前手机号发送短信归为换号重开流程。 - 本地补充修复:导出并通过 phoneVerificationHelpers 调用短信拒发分类器,同时修正重开日志原因。 - 影响范围:background auto-run Step 4、phone verification helpers、Step 4 restart tests。
This commit is contained in:
+14
-3
@@ -7676,12 +7676,20 @@ async function restartSignupPhonePasswordMismatchAttemptFromStep(step, restartCo
|
|||||||
} = getSignupPhonePasswordMismatchRestartPayload(preservedState);
|
} = getSignupPhonePasswordMismatchRestartPayload(preservedState);
|
||||||
const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : '';
|
const emailSuffix = preservedEmail ? `当前邮箱:${preservedEmail};` : '';
|
||||||
const phoneSuffix = activeSignupPhoneNumber ? `当前手机号:${activeSignupPhoneNumber};` : '';
|
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(
|
await addLog(
|
||||||
`步骤 ${step}:检测到手机号/密码不匹配,准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${restartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${getErrorMessage(error)}`,
|
`步骤 ${step}:检测到${reasonLabel},准备丢弃当前注册手机号并回到步骤 1 重新开始(第 ${restartCount} 次重开)。${phoneSuffix}${emailSuffix}原因:${errorMessage}`,
|
||||||
'warn'
|
'warn'
|
||||||
);
|
);
|
||||||
await invalidateDownstreamAfterStepRestart(1, {
|
await invalidateDownstreamAfterStepRestart(1, {
|
||||||
logLabel: `步骤 ${step} 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 ${restartCount} 次重开)`,
|
logLabel: `步骤 ${step} 检测到${reasonLabel}后准备回到步骤 1 重新获取手机号重试(第 ${restartCount} 次重开)`,
|
||||||
});
|
});
|
||||||
if (shouldClearSignupPhoneRuntime) {
|
if (shouldClearSignupPhoneRuntime) {
|
||||||
await addLog(`步骤 ${step}:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。`, 'warn');
|
await addLog(`步骤 ${step}:已清空本轮注册手机号与接码订单,下一次重开将重新获取号码。`, 'warn');
|
||||||
@@ -10504,7 +10512,10 @@ async function runAutoSequenceFromStep(startStep, context = {}) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
step4RestartCount += 1;
|
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);
|
await restartSignupPhonePasswordMismatchAttemptFromStep(4, step4RestartCount, err);
|
||||||
} else {
|
} else {
|
||||||
const preservedState = await getState();
|
const preservedState = await getState();
|
||||||
|
|||||||
@@ -6288,6 +6288,7 @@
|
|||||||
completeSignupPhoneVerificationFlow,
|
completeSignupPhoneVerificationFlow,
|
||||||
finalizeLoginPhoneActivationAfterSuccess,
|
finalizeLoginPhoneActivationAfterSuccess,
|
||||||
finalizeSignupPhoneActivationAfterSuccess,
|
finalizeSignupPhoneActivationAfterSuccess,
|
||||||
|
isPhoneResendBannedNumberError,
|
||||||
normalizeActivation,
|
normalizeActivation,
|
||||||
pollPhoneActivationCode,
|
pollPhoneActivationCode,
|
||||||
prepareLoginPhoneActivation,
|
prepareLoginPhoneActivation,
|
||||||
|
|||||||
@@ -623,6 +623,170 @@ return {
|
|||||||
assert.equal(events.logs.some(({ message }) => /已清空本轮注册手机号与接码订单/.test(message)), true);
|
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 () => {
|
test('auto-run clears manual signup phone state when step 3 detects phone/password mismatch', async () => {
|
||||||
const api = new Function(`
|
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 AUTO_STEP_DELAYS = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 };
|
||||||
|
|||||||
Reference in New Issue
Block a user