修复手机号登录回退与重发 500 处理
This commit is contained in:
@@ -92,6 +92,7 @@
|
||||
const PHONE_RESTART_STEP7_ERROR_PREFIX = 'PHONE_RESTART_STEP7::';
|
||||
const PHONE_RESEND_THROTTLED_ERROR_PREFIX = 'PHONE_RESEND_THROTTLED::';
|
||||
const PHONE_RESEND_BANNED_NUMBER_ERROR_PREFIX = 'PHONE_RESEND_BANNED_NUMBER::';
|
||||
const PHONE_RESEND_SERVER_ERROR_PREFIX = 'PHONE_RESEND_SERVER_ERROR::';
|
||||
const PHONE_ROUTE_405_RECOVERY_FAILED_ERROR_PREFIX = 'PHONE_ROUTE_405_RECOVERY_FAILED::';
|
||||
const PHONE_MANUAL_FREE_REUSE_ERROR_PREFIX = 'PHONE_MANUAL_FREE_REUSE::';
|
||||
const PHONE_AUTO_FREE_REUSE_PREPARE_ERROR_PREFIX = 'PHONE_AUTO_FREE_REUSE_PREPARE::';
|
||||
@@ -1331,6 +1332,25 @@
|
||||
return /无法向此电话号码发送短信|无法向此手机号发送短信|无法发送短信到此电话号码|无法发送短信到此手机号|can(?:not|'t)\s+send\s+(?:an?\s+)?(?:sms|text(?:\s+message)?)\s+to\s+(?:this|that)\s+(?:phone\s+)?number|unable\s+to\s+send\s+(?:an?\s+)?(?:sms|text(?:\s+message)?)\s+to\s+(?:this|that)\s+(?:phone\s+)?number/i.test(message);
|
||||
}
|
||||
|
||||
function isPhoneResendServerError(error) {
|
||||
const message = String(error?.message || error || '').trim();
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
if (message.startsWith(PHONE_RESEND_SERVER_ERROR_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
return /this\s+page\s+isn['’]?t\s+working|currently\s+unable\s+to\s+handle\s+this\s+request|http\s+error\s+500|500\s+internal\s+server\s+error/i.test(message);
|
||||
}
|
||||
|
||||
function buildPhoneResendServerError(error) {
|
||||
const message = String(error?.message || error || '').trim();
|
||||
if (message.startsWith(PHONE_RESEND_SERVER_ERROR_PREFIX)) {
|
||||
return new Error(message);
|
||||
}
|
||||
return new Error(`${PHONE_RESEND_SERVER_ERROR_PREFIX}${message || 'OpenAI contact-verification page returned HTTP ERROR 500 after resend.'}`);
|
||||
}
|
||||
|
||||
function shouldTreatResendThrottledAsBanned(state = {}) {
|
||||
return Boolean(state?.phoneResendThrottledAsBannedEnabled);
|
||||
}
|
||||
@@ -4220,6 +4240,13 @@
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
if (isPhoneResendServerError(error)) {
|
||||
return {
|
||||
hasError: true,
|
||||
reason: 'resend_server_error',
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
if (isPhoneMaxUsageExceededFlowError(error)) {
|
||||
return {
|
||||
hasError: true,
|
||||
@@ -4891,6 +4918,9 @@
|
||||
if (pageError?.reason === 'phone_max_usage_exceeded') {
|
||||
throw buildPhoneMaxUsageExceededError(pageError.message);
|
||||
}
|
||||
if (pageError?.reason === 'resend_server_error') {
|
||||
throw buildPhoneResendServerError(pageError.message);
|
||||
}
|
||||
if (pageError?.reason === 'resend_throttled') {
|
||||
if (shouldTreatResendThrottledAsBanned(state)) {
|
||||
throw buildHighRiskResendThrottledError(pageError.message);
|
||||
@@ -4948,6 +4978,18 @@
|
||||
reason: 'phone_max_usage_exceeded',
|
||||
};
|
||||
}
|
||||
if (isPhoneResendServerError(error)) {
|
||||
await addLog(
|
||||
`步骤 9:重发短信后进入 contact-verification 500 页面,立即更换号码。${error.message}`,
|
||||
'warn'
|
||||
);
|
||||
await clearPhoneRuntimeCountdown();
|
||||
return {
|
||||
code: '',
|
||||
replaceNumber: true,
|
||||
reason: 'resend_server_error',
|
||||
};
|
||||
}
|
||||
if (isPhoneResendThrottledError(error)) {
|
||||
if (shouldTreatResendThrottledAsBanned(state)) {
|
||||
await addLog(
|
||||
@@ -5037,6 +5079,18 @@
|
||||
: 'resend_throttled',
|
||||
};
|
||||
}
|
||||
if (isPhoneResendServerError(resendError)) {
|
||||
await addLog(
|
||||
`步骤 9:重发短信后进入 contact-verification 500 页面,立即更换号码。${resendError.message}`,
|
||||
'warn'
|
||||
);
|
||||
await clearPhoneRuntimeCountdown();
|
||||
return {
|
||||
code: '',
|
||||
replaceNumber: true,
|
||||
reason: 'resend_server_error',
|
||||
};
|
||||
}
|
||||
await addLog(`步骤 9:点击手机验证码页面重发按钮失败。${resendError.message}`, 'warn');
|
||||
}
|
||||
continue;
|
||||
@@ -5242,6 +5296,9 @@
|
||||
if (isStopRequestedError(resendError)) {
|
||||
throw resendError;
|
||||
}
|
||||
if (isPhoneResendServerError(resendError)) {
|
||||
throw buildPhoneResendServerError(resendError);
|
||||
}
|
||||
await addLog(`步骤 4:注册手机验证码页面重发失败,将继续轮询短信。${resendError.message}`, 'warn', {
|
||||
step: 4,
|
||||
stepKey: 'fetch-signup-code',
|
||||
@@ -5277,6 +5334,9 @@
|
||||
if (isStopRequestedError(resendError)) {
|
||||
throw resendError;
|
||||
}
|
||||
if (isPhoneResendServerError(resendError)) {
|
||||
throw buildPhoneResendServerError(resendError);
|
||||
}
|
||||
await addLog(`步骤 4:验证码被拒后点击重发失败。${resendError.message}`, 'warn', {
|
||||
step: 4,
|
||||
stepKey: 'fetch-signup-code',
|
||||
@@ -5479,6 +5539,9 @@
|
||||
if (isStopRequestedError(resendError)) {
|
||||
throw resendError;
|
||||
}
|
||||
if (isPhoneResendServerError(resendError)) {
|
||||
throw buildPhoneResendServerError(resendError);
|
||||
}
|
||||
await addLog(`步骤 ${visibleStep}:登录手机验证码页面重发失败,将继续轮询短信。${resendError.message}`, 'warn', {
|
||||
step: visibleStep,
|
||||
stepKey: 'fetch-login-code',
|
||||
@@ -5514,6 +5577,9 @@
|
||||
if (isStopRequestedError(resendError)) {
|
||||
throw resendError;
|
||||
}
|
||||
if (isPhoneResendServerError(resendError)) {
|
||||
throw buildPhoneResendServerError(resendError);
|
||||
}
|
||||
await addLog(`步骤 ${visibleStep}:登录手机验证码被拒后点击重发失败。${resendError.message}`, 'warn', {
|
||||
step: visibleStep,
|
||||
stepKey: 'fetch-login-code',
|
||||
|
||||
@@ -56,7 +56,30 @@
|
||||
&& !Boolean(state?.contributionMode);
|
||||
}
|
||||
|
||||
function hasStep7PhoneSignupIdentity(state = {}) {
|
||||
return Boolean(
|
||||
String(state?.signupPhoneNumber || '').trim()
|
||||
|| String(state?.signupPhoneCompletedActivation?.phoneNumber || '').trim()
|
||||
|| String(state?.signupPhoneActivation?.phoneNumber || '').trim()
|
||||
|| (
|
||||
normalizeStep7IdentifierType(state?.accountIdentifierType) === 'phone'
|
||||
&& String(state?.accountIdentifier || '').trim()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function shouldPreferStep7PhoneSignupIdentity(state = {}) {
|
||||
const frozenSignupMethod = normalizeStep7IdentifierType(state?.resolvedSignupMethod);
|
||||
return canUseConfiguredPhoneSignup(state)
|
||||
&& frozenSignupMethod !== 'email'
|
||||
&& hasStep7PhoneSignupIdentity(state);
|
||||
}
|
||||
|
||||
function resolveStep7LoginIdentifierType(state = {}, fallbackType = '') {
|
||||
if (shouldPreferStep7PhoneSignupIdentity(state)) {
|
||||
return 'phone';
|
||||
}
|
||||
|
||||
const explicitIdentifierType = normalizeStep7IdentifierType(state?.accountIdentifierType);
|
||||
if (explicitIdentifierType) {
|
||||
return explicitIdentifierType;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
} = deps;
|
||||
const PHONE_RESEND_THROTTLED_ERROR_PREFIX = 'PHONE_RESEND_THROTTLED::';
|
||||
const PHONE_RESEND_BANNED_NUMBER_ERROR_PREFIX = 'PHONE_RESEND_BANNED_NUMBER::';
|
||||
const PHONE_RESEND_SERVER_ERROR_PREFIX = 'PHONE_RESEND_SERVER_ERROR::';
|
||||
const PHONE_MAX_USAGE_EXCEEDED_PATTERN = /phone_max_usage_exceeded/i;
|
||||
const PHONE_ROUTE_405_RECOVERY_FAILED_ERROR_PREFIX = 'PHONE_ROUTE_405_RECOVERY_FAILED::';
|
||||
const PHONE_ROUTE_405_RECOVERY_COOLDOWN_MS = 6000;
|
||||
@@ -27,6 +28,7 @@
|
||||
const PHONE_RESEND_ROUTE_405_MAX_RECOVERY_TOTAL_MS = 12000;
|
||||
const PHONE_RESEND_THROTTLED_PATTERN = /tried\s+to\s+resend\s+too\s+many\s+times|please\s+try\s+again\s+later|too\s+many\s+resend|resend\s+too\s+many|发送.*过于频繁|稍后再试|重试次数过多/i;
|
||||
const PHONE_RESEND_BANNED_NUMBER_PATTERN = /无法向此电话号码发送短信|无法向此手机号发送短信|无法发送短信到此电话号码|无法发送短信到此手机号|can(?:not|'t)\s+send\s+(?:an?\s+)?(?:sms|text(?:\s+message)?)\s+to\s+(?:this|that)\s+(?:phone\s+)?number|unable\s+to\s+send\s+(?:an?\s+)?(?:sms|text(?:\s+message)?)\s+to\s+(?:this|that)\s+(?:phone\s+)?number/i;
|
||||
const PHONE_RESEND_SERVER_ERROR_PATTERN = /this\s+page\s+isn['’]?t\s+working|currently\s+unable\s+to\s+handle\s+this\s+request|http\s+error\s+500|500\s+internal\s+server\s+error/i;
|
||||
const PHONE_ROUTE_405_PATTERN = /405\s+method\s+not\s+allowed|route\s+error.*405|did\s+not\s+provide\s+an?\s+[`'"]?action|post\s+request\s+to\s+["']?\/phone-verification/i;
|
||||
const PHONE_ROUTE_405_MAX_RECOVERY_CLICKS = 3;
|
||||
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
||||
@@ -512,6 +514,20 @@
|
||||
return '';
|
||||
}
|
||||
|
||||
function getPhoneResendServerErrorText() {
|
||||
const path = String(location?.pathname || '');
|
||||
if (!/\/contact-verification(?:[/?#]|$)/i.test(path)) {
|
||||
return '';
|
||||
}
|
||||
const text = String(getPageTextSnapshot?.() || '').replace(/\s+/g, ' ').trim();
|
||||
const title = String(document?.title || '').replace(/\s+/g, ' ').trim();
|
||||
const combined = `${title} ${text}`.trim();
|
||||
if (!PHONE_RESEND_SERVER_ERROR_PATTERN.test(combined)) {
|
||||
return '';
|
||||
}
|
||||
return combined || 'OpenAI contact-verification page returned HTTP ERROR 500 after resend.';
|
||||
}
|
||||
|
||||
function checkPhoneResendError() {
|
||||
const maxUsageText = getAddPhoneErrorText();
|
||||
if (maxUsageText && PHONE_MAX_USAGE_EXCEEDED_PATTERN.test(maxUsageText)) {
|
||||
@@ -545,6 +561,17 @@
|
||||
};
|
||||
}
|
||||
|
||||
const serverErrorText = getPhoneResendServerErrorText();
|
||||
if (serverErrorText) {
|
||||
return {
|
||||
hasError: true,
|
||||
reason: 'resend_server_error',
|
||||
prefix: PHONE_RESEND_SERVER_ERROR_PREFIX,
|
||||
message: serverErrorText,
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
hasError: false,
|
||||
reason: '',
|
||||
@@ -847,6 +874,10 @@
|
||||
if (throttledText) {
|
||||
throw new Error(`${PHONE_RESEND_THROTTLED_ERROR_PREFIX}${throttledText}`);
|
||||
}
|
||||
const serverErrorText = getPhoneResendServerErrorText();
|
||||
if (serverErrorText) {
|
||||
throw new Error(`${PHONE_RESEND_SERVER_ERROR_PREFIX}${serverErrorText}`);
|
||||
}
|
||||
const resendButton = getPhoneVerificationResendButton({ allowDisabled: true });
|
||||
if (resendButton && isActionEnabled(resendButton)) {
|
||||
await humanPause(250, 700);
|
||||
@@ -864,6 +895,10 @@
|
||||
if (afterClickThrottleText) {
|
||||
throw new Error(`${PHONE_RESEND_THROTTLED_ERROR_PREFIX}${afterClickThrottleText}`);
|
||||
}
|
||||
const afterClickServerErrorText = getPhoneResendServerErrorText();
|
||||
if (afterClickServerErrorText) {
|
||||
throw new Error(`${PHONE_RESEND_SERVER_ERROR_PREFIX}${afterClickServerErrorText}`);
|
||||
}
|
||||
return {
|
||||
resent: true,
|
||||
url: location.href,
|
||||
@@ -882,6 +917,11 @@
|
||||
throw new Error(`${PHONE_RESEND_THROTTLED_ERROR_PREFIX}${timeoutThrottleText}`);
|
||||
}
|
||||
|
||||
const timeoutServerErrorText = getPhoneResendServerErrorText();
|
||||
if (timeoutServerErrorText) {
|
||||
throw new Error(`${PHONE_RESEND_SERVER_ERROR_PREFIX}${timeoutServerErrorText}`);
|
||||
}
|
||||
|
||||
throw new Error('Timed out waiting for the phone verification resend button.');
|
||||
})().finally(() => {
|
||||
activePhoneResendPromise = null;
|
||||
|
||||
@@ -147,6 +147,8 @@ const LOGIN_EXTERNAL_IDP_PATTERN = /google|microsoft|apple|sso|single\s+sign[-\s
|
||||
const LOGIN_CODE_ONLY_ACTION_PATTERN = /one[-\s]*time|passcode|use\s+(?:a\s+)?code|验证码|一次性/i;
|
||||
|
||||
const RESEND_VERIFICATION_CODE_PATTERN = /重新发送(?:验证码)?|再次发送(?:验证码)?|重发(?:验证码)?|未收到(?:验证码|邮件)|resend(?:\s+code)?|send\s+(?:a\s+)?new\s+code|send\s+(?:it\s+)?again|request\s+(?:a\s+)?new\s+code|didn'?t\s+receive/i;
|
||||
const PHONE_RESEND_SERVER_ERROR_PREFIX = 'PHONE_RESEND_SERVER_ERROR::';
|
||||
const CONTACT_VERIFICATION_SERVER_ERROR_PATTERN = /this\s+page\s+isn['’]?t\s+working|currently\s+unable\s+to\s+handle\s+this\s+request|http\s+error\s+500|500\s+internal\s+server\s+error/i;
|
||||
|
||||
function isVisibleElement(el) {
|
||||
if (!el) return false;
|
||||
@@ -250,6 +252,27 @@ function isEmailVerificationPage() {
|
||||
return /\/email-verification(?:[/?#]|$)/i.test(location.pathname || '');
|
||||
}
|
||||
|
||||
function getContactVerificationServerErrorText() {
|
||||
const path = String(location?.pathname || '');
|
||||
if (!/\/contact-verification(?:[/?#]|$)/i.test(path)) {
|
||||
return '';
|
||||
}
|
||||
const text = String(getPageTextSnapshot?.() || document?.body?.textContent || '').replace(/\s+/g, ' ').trim();
|
||||
const title = String(document?.title || '').replace(/\s+/g, ' ').trim();
|
||||
const combined = `${title} ${text}`.trim();
|
||||
if (!CONTACT_VERIFICATION_SERVER_ERROR_PATTERN.test(combined)) {
|
||||
return '';
|
||||
}
|
||||
return combined || 'OpenAI contact-verification page returned HTTP ERROR 500 after resend.';
|
||||
}
|
||||
|
||||
function throwIfContactVerificationServerError() {
|
||||
const serverErrorText = getContactVerificationServerErrorText();
|
||||
if (serverErrorText) {
|
||||
throw new Error(`${PHONE_RESEND_SERVER_ERROR_PREFIX}${serverErrorText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function resendVerificationCode(step, timeout = 45000) {
|
||||
if (step === 8) {
|
||||
await waitForLoginVerificationPageReady(10000, step);
|
||||
@@ -261,6 +284,7 @@ async function resendVerificationCode(step, timeout = 45000) {
|
||||
|
||||
while (Date.now() - start < timeout) {
|
||||
throwIfStopped();
|
||||
throwIfContactVerificationServerError();
|
||||
|
||||
// Check for 405 error page and recover by clicking "Try again"
|
||||
if (is405MethodNotAllowedPage()) {
|
||||
@@ -285,6 +309,7 @@ async function resendVerificationCode(step, timeout = 45000) {
|
||||
loggedWaiting = false;
|
||||
continue;
|
||||
}
|
||||
throwIfContactVerificationServerError();
|
||||
|
||||
return {
|
||||
resent: true,
|
||||
@@ -300,6 +325,7 @@ async function resendVerificationCode(step, timeout = 45000) {
|
||||
await sleep(250);
|
||||
}
|
||||
|
||||
throwIfContactVerificationServerError();
|
||||
throw new Error('无法点击重新发送验证码按钮。URL: ' + location.href);
|
||||
}
|
||||
|
||||
|
||||
@@ -412,6 +412,62 @@ test('step 7 keeps Plus email login even when phone sms runtime exists', async (
|
||||
assert.equal(events.payloads[0].accountIdentifier, 'plus.user@example.com');
|
||||
});
|
||||
|
||||
test('step 7 keeps phone login after step 8 stores an unbound email for phone signup', async () => {
|
||||
const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
|
||||
const globalScope = {};
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep7;`)(globalScope);
|
||||
|
||||
const events = {
|
||||
payloads: [],
|
||||
};
|
||||
|
||||
const phoneSignupState = {
|
||||
phoneVerificationEnabled: true,
|
||||
signupMethod: 'phone',
|
||||
resolvedSignupMethod: 'phone',
|
||||
email: 'bound.step8@example.com',
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: 'bound.step8@example.com',
|
||||
signupPhoneNumber: '66959916439',
|
||||
signupPhoneCompletedActivation: {
|
||||
activationId: 'signup-done',
|
||||
phoneNumber: '66959916439',
|
||||
countryId: 52,
|
||||
countryLabel: 'Thailand',
|
||||
},
|
||||
password: 'secret',
|
||||
};
|
||||
|
||||
const executor = api.createStep7Executor({
|
||||
addLog: async () => {},
|
||||
completeStepFromBackground: async () => {},
|
||||
getErrorMessage: (error) => error?.message || String(error || ''),
|
||||
getLoginAuthStateLabel: (state) => state || 'unknown',
|
||||
getState: async () => ({ ...phoneSignupState }),
|
||||
isStep6RecoverableResult: (result) => result?.step6Outcome === 'recoverable',
|
||||
isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
|
||||
refreshOAuthUrlBeforeStep6: async () => 'https://oauth.example/latest',
|
||||
reuseOrCreateTab: async () => {},
|
||||
sendToContentScriptResilient: async (_sourceName, message) => {
|
||||
events.payloads.push(message.payload);
|
||||
return {
|
||||
step6Outcome: 'success',
|
||||
state: 'phone_verification_page',
|
||||
loginVerificationRequestedAt: 123456,
|
||||
};
|
||||
},
|
||||
STEP6_MAX_ATTEMPTS: 3,
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await executor.executeStep7(phoneSignupState);
|
||||
|
||||
assert.equal(events.payloads[0].loginIdentifierType, 'phone');
|
||||
assert.equal(events.payloads[0].phoneNumber, '66959916439');
|
||||
assert.equal(events.payloads[0].email, '');
|
||||
assert.equal(events.payloads[0].accountIdentifier, '66959916439');
|
||||
});
|
||||
|
||||
test('step 7 can infer phone login from an available phone signup configuration before step 2 finishes', async () => {
|
||||
const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
|
||||
const globalScope = {};
|
||||
|
||||
@@ -4677,6 +4677,69 @@ test('phone verification helper replaces number immediately when phone-verificat
|
||||
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), true);
|
||||
});
|
||||
|
||||
test('signup phone verification cancels activation when resend lands on contact-verification HTTP 500 page', async () => {
|
||||
const requests = [];
|
||||
let currentState = {
|
||||
heroSmsApiKey: 'demo-key',
|
||||
heroSmsCountryId: 52,
|
||||
heroSmsCountryLabel: 'Thailand',
|
||||
verificationResendCount: 0,
|
||||
phoneCodeWaitSeconds: 60,
|
||||
phoneCodeTimeoutWindows: 2,
|
||||
phoneCodePollIntervalSeconds: 1,
|
||||
phoneCodePollMaxRounds: 1,
|
||||
signupPhoneActivation: {
|
||||
activationId: '920001',
|
||||
phoneNumber: '66953330001',
|
||||
provider: 'hero-sms',
|
||||
countryId: 52,
|
||||
countryLabel: 'Thailand',
|
||||
},
|
||||
};
|
||||
|
||||
const helpers = api.createPhoneVerificationHelpers({
|
||||
addLog: async () => {},
|
||||
fetchImpl: async (url) => {
|
||||
const parsedUrl = new URL(url);
|
||||
requests.push(parsedUrl);
|
||||
const action = parsedUrl.searchParams.get('action');
|
||||
const id = parsedUrl.searchParams.get('id');
|
||||
if (action === 'getStatus') {
|
||||
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
|
||||
}
|
||||
if (action === 'setStatus') {
|
||||
return { ok: true, text: async () => `STATUS_UPDATED:${id}` };
|
||||
}
|
||||
throw new Error(`Unexpected HeroSMS action: ${action}`);
|
||||
},
|
||||
getState: async () => ({ ...currentState }),
|
||||
sendToContentScriptResilient: async (_source, message) => {
|
||||
if (message.type === 'RESEND_VERIFICATION_CODE') {
|
||||
throw new Error(
|
||||
'PHONE_RESEND_SERVER_ERROR::This page isn\'t working auth.openai.com is currently unable to handle this request. HTTP ERROR 500'
|
||||
);
|
||||
}
|
||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
() => helpers.completeSignupPhoneVerificationFlow(1, { state: currentState }),
|
||||
(error) => {
|
||||
assert.match(error.message, /^PHONE_RESEND_SERVER_ERROR::This page isn't working/);
|
||||
assert.equal(error.message.includes('PHONE_RESEND_SERVER_ERROR::PHONE_RESEND_SERVER_ERROR::'), false);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(currentState.signupPhoneActivation, null);
|
||||
});
|
||||
|
||||
test('phone verification helper skips page resend for 5sim timeouts and rotates number directly', async () => {
|
||||
const requests = [];
|
||||
const messages = [];
|
||||
|
||||
@@ -166,6 +166,59 @@ return {
|
||||
assert.deepStrictEqual(snapshot.clicks, ['Continue']);
|
||||
});
|
||||
|
||||
test('resendVerificationCode reports contact-verification HTTP 500 after resend click', async () => {
|
||||
const api = new Function(`
|
||||
const PHONE_RESEND_SERVER_ERROR_PREFIX = 'PHONE_RESEND_SERVER_ERROR::';
|
||||
const CONTACT_VERIFICATION_SERVER_ERROR_PATTERN = /this\\s+page\\s+isn['’]?t\\s+working|currently\\s+unable\\s+to\\s+handle\\s+this\\s+request|http\\s+error\\s+500|500\\s+internal\\s+server\\s+error/i;
|
||||
const logs = [];
|
||||
const location = {
|
||||
href: 'https://auth.openai.com/email-verification',
|
||||
pathname: '/email-verification',
|
||||
};
|
||||
const document = {
|
||||
title: 'Verify your email',
|
||||
body: { textContent: 'Enter the verification code.' },
|
||||
};
|
||||
function throwIfStopped() {}
|
||||
function log(message, level = 'info') { logs.push({ message, level }); }
|
||||
function findResendVerificationCodeTrigger() { return { textContent: 'Resend code' }; }
|
||||
function isActionEnabled() { return true; }
|
||||
async function humanPause() {}
|
||||
function simulateClick() {
|
||||
location.href = 'https://auth.openai.com/contact-verification';
|
||||
location.pathname = '/contact-verification';
|
||||
document.title = "This page isn't working";
|
||||
document.body.textContent = 'auth.openai.com is currently unable to handle this request. HTTP ERROR 500';
|
||||
}
|
||||
async function sleep() {}
|
||||
function is405MethodNotAllowedPage() { return false; }
|
||||
async function handle405ResendError() {}
|
||||
function getActionText(element) { return element?.textContent || ''; }
|
||||
function getPageTextSnapshot() { return document.body.textContent; }
|
||||
|
||||
${extractFunction('getContactVerificationServerErrorText')}
|
||||
${extractFunction('throwIfContactVerificationServerError')}
|
||||
${extractFunction('resendVerificationCode')}
|
||||
|
||||
return {
|
||||
async run() {
|
||||
try {
|
||||
await resendVerificationCode(4, 1000);
|
||||
return { ok: true };
|
||||
} catch (error) {
|
||||
return { ok: false, message: String(error?.message || error), logs };
|
||||
}
|
||||
},
|
||||
};
|
||||
`)();
|
||||
|
||||
const result = await api.run();
|
||||
|
||||
assert.equal(result.ok, false);
|
||||
assert.match(result.message, /^PHONE_RESEND_SERVER_ERROR::This page isn't working/);
|
||||
assert.equal(result.message.includes('PHONE_RESEND_SERVER_ERROR::PHONE_RESEND_SERVER_ERROR::'), false);
|
||||
});
|
||||
|
||||
test('fillVerificationCode does not short-circuit on mixed email-verification profile page before verification exits', async () => {
|
||||
const api = new Function(`
|
||||
const logs = [];
|
||||
|
||||
@@ -488,6 +488,7 @@ IP 代理模块在同步、切换、Change、出口探测和自动运行成功
|
||||
- 对 `custom` provider 而言,Step 8 仍使用手动验证码确认弹窗;弹窗当前额外提供“出现手机号验证”按钮,点击后会直接抛出与真实 `add-phone` 页面一致的 fatal 错误,供 Auto 按既有 add-phone 分支继续下一邮箱。
|
||||
- 当登录验证码提交后进入 `phone-verification` 页时,内容脚本会显式把该页面识别为“手机验证码页”,避免与邮箱验证码页混淆。
|
||||
- 手机号注册身份本身不会让 Step 8 自动走短信;只有真实页面状态是 `phone_verification_page` 时才调用短信 helper。手机号注册登录后出现 `add-email` 时,Step 8 走“按需生成邮箱 -> 绑定邮箱 -> 邮箱验证码”链路。
|
||||
- 如果手机号注册账号在 Step 8 的 `add-email` / 邮箱验证码阶段失败并回退到 Step 7,Step 7 会优先使用当前轮保留的注册手机号身份重新登录,避免误用尚未绑定成功的邮箱登录。
|
||||
- `email_in_use` 会在 Step 8 当前步骤内清理当前邮箱并重新打开 `add-email` 获取新邮箱;`max_check_attempts` 不继续点击认证页 `重试`,只恢复当前 Step 8。
|
||||
|
||||
### Step 9
|
||||
@@ -514,6 +515,7 @@ IP 代理模块在同步、切换、Change、出口探测和自动运行成功
|
||||
- 号码当前最多复用 3 次成功注册;超过上限后会清空可复用激活记录,下次重新申请新号码。
|
||||
- 5sim 的国家/地区使用字符串 slug(当前开放 `indonesia / thailand / vietnam`),HeroSMS 仍使用数字国家 ID(当前开放印度尼西亚/泰国/越南)。
|
||||
- 如果同一个号码在重发短信后仍收不到验证码,后台会按当前平台取消或 ban 激活并换号,而不是把当前号码无限重试下去。
|
||||
- 如果注册手机号验证码页在点击 `resend` 后跳到 `contact-verification` 的 `This page isn’t working / HTTP ERROR 500`,后台会把当前号码视为不可继续使用并结束当前激活,避免继续停留在坏页面上空轮询。
|
||||
|
||||
### Step 10
|
||||
|
||||
|
||||
Reference in New Issue
Block a user