fix: rotate unusable phone verification numbers

This commit is contained in:
root
2026-05-09 05:28:01 -04:00
parent a8e77396e1
commit 018b84264b
5 changed files with 645 additions and 13 deletions
+97 -10
View File
@@ -406,6 +406,31 @@
return /phone\s+number\s+is\s+not\s+valid|invalid\s+phone\s+number|invalid\s+phone|not\s+a\s+valid\s+phone|号码.*无效|手机号.*无效|电话号码.*无效/i.test(text);
}
function isPhoneNumberDeliveryRefusedError(value) {
const text = String(value || '').trim();
if (!text) {
return false;
}
return /无法向此电话号码发送验证码|无法向.*(?:电话号码|手机号|号码).*发送(?:验证码|短信)|(?:不能|无法).*发送.*(?:验证码|短信).*(?:电话号码|手机号|号码)|(?:cannot|can't|could\s*not|couldn't|unable\s+to)\s+(?:send|deliver).{0,80}(?:verification\s+code|code|sms|text(?:\s+message)?).{0,80}(?:phone|number)|(?:verification\s+code|sms|text(?:\s+message)?).{0,80}(?:cannot|can't|could\s*not|couldn't|unable\s+to).{0,80}(?:send|deliver)/i.test(text);
}
function isWhatsAppPhoneResendResult(value) {
if (!value) {
return false;
}
const text = typeof value === 'string'
? value
: [
value.channel,
value.channelText,
value.text,
value.buttonText,
value.label,
value.message,
].filter(Boolean).join(' ');
return /whats\s*app/i.test(String(text || ''));
}
function isRecoverableAddPhoneSubmitError(value) {
const text = String(value || '').trim();
if (!text) {
@@ -4080,7 +4105,7 @@
return result || {};
}
async function resendPhoneVerificationCode(tabId) {
async function resendPhoneVerificationCode(tabId, options = {}) {
const visibleStep = normalizeLogStep(activePhoneVerificationLogStep) || 9;
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(65000, { step: visibleStep, actionLabel: 'resend phone verification code' })
@@ -4088,7 +4113,7 @@
const result = await sendToContentScriptResilient('signup-page', {
type: 'RESEND_PHONE_VERIFICATION_CODE',
source: 'background',
payload: {},
payload: options || {},
}, {
timeoutMs,
responseTimeoutMs: timeoutMs,
@@ -5001,7 +5026,6 @@
);
continue;
}
await requestAdditionalPhoneSms(state, normalizedActivation);
if (resendTriggeredForCurrentNumber) {
await addLog(
`步骤 9:号码 ${normalizedActivation.phoneNumber} 已触发过一次页面重发;为避免限流,将继续轮询不再点击重发。`,
@@ -5010,7 +5034,35 @@
continue;
}
try {
await resendPhoneVerificationCode(tabId);
const resendProbeResult = await resendPhoneVerificationCode(tabId, { probeOnly: true });
if (isWhatsAppPhoneResendResult(resendProbeResult)) {
await addLog(
`步骤 9:页面重发入口显示 WhatsApp 通道(${resendProbeResult.channelText || resendProbeResult.text || 'WhatsApp'}),当前接码平台无法读取 WhatsApp 消息,立即更换号码。`,
'warn'
);
await clearPhoneRuntimeCountdown();
return {
code: '',
replaceNumber: true,
reason: 'whatsapp_resend_channel',
};
}
await requestAdditionalPhoneSms(state, normalizedActivation);
if (resendProbeResult?.probed) {
const resendResult = await resendPhoneVerificationCode(tabId);
if (isWhatsAppPhoneResendResult(resendResult)) {
await addLog(
`步骤 9:页面重发入口切换为 WhatsApp 通道(${resendResult.channelText || resendResult.text || 'WhatsApp'}),当前接码平台无法读取 WhatsApp 消息,立即更换号码。`,
'warn'
);
await clearPhoneRuntimeCountdown();
return {
code: '',
replaceNumber: true,
reason: 'whatsapp_resend_channel',
};
}
}
resendTriggeredForCurrentNumber = true;
await addLog('步骤 9:已点击手机验证码页面的“重新发送短信”。', 'info');
} catch (resendError) {
@@ -5982,10 +6034,10 @@
submitResult = await submitPhoneNumber(tabId, activation.phoneNumber, activation);
} catch (submitError) {
const submitErrorText = String(submitError?.message || submitError || 'unknown error');
if (isRecoverableAddPhoneSubmitError(submitErrorText)) {
if (isPhoneNumberDeliveryRefusedError(submitErrorText) || isRecoverableAddPhoneSubmitError(submitErrorText)) {
await rotateActivationAfterAddPhoneFailure(
submitErrorText,
'add_phone_submit_failed',
isPhoneNumberDeliveryRefusedError(submitErrorText) ? 'phone_delivery_refused' : 'add_phone_submit_failed',
{ url: pageState?.url || '' }
);
continue;
@@ -6032,6 +6084,14 @@
};
continue;
}
if (isPhoneNumberDeliveryRefusedError(addPhoneRejectText)) {
await rotateActivationAfterAddPhoneFailure(
addPhoneRejectText,
'phone_delivery_refused',
submitResult || {}
);
continue;
}
await addLog(
`步骤 9:添加手机号页面拒绝当前号码,但未明确提示已使用(${addPhoneRejectText}),将用同一号码再试一次。`,
@@ -6050,10 +6110,16 @@
|| submitResult?.url
|| 'unknown error'
);
if (isPhoneNumberUsedError(retryRejectText) || isRecoverableAddPhoneSubmitError(retryRejectText)) {
if (
isPhoneNumberUsedError(retryRejectText)
|| isPhoneNumberDeliveryRefusedError(retryRejectText)
|| isRecoverableAddPhoneSubmitError(retryRejectText)
) {
await rotateActivationAfterAddPhoneFailure(
`add-phone keeps rejecting ${activation.phoneNumber} (${retryRejectText})`,
isPhoneNumberUsedError(retryRejectText) ? 'phone_number_used' : 'add_phone_rejected',
isPhoneNumberUsedError(retryRejectText)
? 'phone_number_used'
: (isPhoneNumberDeliveryRefusedError(retryRejectText) ? 'phone_delivery_refused' : 'add_phone_rejected'),
submitResult || {}
);
continue;
@@ -6170,9 +6236,30 @@
if (remainingResendRequests > 0) {
remainingResendRequests -= 1;
await requestAdditionalPhoneSms(state, activation);
try {
await resendPhoneVerificationCode(tabId);
const resendProbeResult = await resendPhoneVerificationCode(tabId, { probeOnly: true });
if (isWhatsAppPhoneResendResult(resendProbeResult)) {
shouldReplaceNumber = true;
replaceReason = 'whatsapp_resend_channel';
await addLog(
`步骤 9:验证码被拒后的重发入口显示 WhatsApp 通道(${resendProbeResult.channelText || resendProbeResult.text || 'WhatsApp'}),当前接码平台无法读取 WhatsApp 消息,将更换号码。`,
'warn'
);
break;
}
await requestAdditionalPhoneSms(state, activation);
if (resendProbeResult?.probed) {
const resendResult = await resendPhoneVerificationCode(tabId);
if (isWhatsAppPhoneResendResult(resendResult)) {
shouldReplaceNumber = true;
replaceReason = 'whatsapp_resend_channel';
await addLog(
`步骤 9:验证码被拒后的重发入口切换为 WhatsApp 通道(${resendResult.channelText || resendResult.text || 'WhatsApp'}),当前接码平台无法读取 WhatsApp 消息,将更换号码。`,
'warn'
);
break;
}
}
await addLog('步骤 9:手机验证码被拒后已点击“重新发送短信”。', 'info');
} catch (resendError) {
await addLog(`步骤 9:验证码被拒后点击重发失败。${resendError.message}`, 'warn');
+52 -2
View File
@@ -403,6 +403,33 @@
}) || buttons.find((button) => isVisibleElement(button));
}
function getPhoneVerificationResendActionText(button) {
if (!button) return '';
return [
button.getAttribute?.('value'),
button.getAttribute?.('aria-label'),
button.getAttribute?.('title'),
getActionText(button),
button.textContent,
].filter(Boolean).join(' ').replace(/\s+/g, ' ').trim();
}
function isWhatsAppResendText(value) {
return /whats\s*app/i.test(String(value || ''));
}
function getPhoneVerificationResendActionInfo(button) {
const text = getPhoneVerificationResendActionText(button);
const channel = isWhatsAppResendText(text)
? 'whatsapp'
: (/(?:sms|text\s+message|短信)/i.test(text) ? 'sms' : '');
return {
channel,
channelText: text,
text,
};
}
function getPhoneVerificationResendButton(options = {}) {
const { allowDisabled = false } = options;
const form = getPhoneVerificationForm();
@@ -413,7 +440,7 @@
if (!allowDisabled && !isActionEnabled(button)) return false;
const intent = String(button.getAttribute('value') || '').trim().toLowerCase();
if (intent === 'resend') return true;
return /resend/i.test(getActionText(button));
return /resend|重新发送|再次发送|whats\s*app/i.test(getPhoneVerificationResendActionText(button));
}) || null;
}
@@ -829,7 +856,7 @@
return waitForPhoneVerificationOutcome();
}
async function resendPhoneVerificationCode(timeout = 45000) {
async function resendPhoneVerificationCode(timeout = 45000, options = {}) {
if (activePhoneResendPromise) {
return activePhoneResendPromise;
}
@@ -872,6 +899,26 @@
}
const resendButton = getPhoneVerificationResendButton({ allowDisabled: true });
if (resendButton && isActionEnabled(resendButton)) {
const resendInfo = getPhoneVerificationResendActionInfo(resendButton);
if (resendInfo.channel === 'whatsapp') {
return {
resent: false,
channel: 'whatsapp',
channelText: resendInfo.channelText,
text: resendInfo.text,
url: location.href,
};
}
if (options?.probeOnly) {
return {
resent: false,
probed: true,
channel: resendInfo.channel || 'unknown',
channelText: resendInfo.channelText,
text: resendInfo.text,
url: location.href,
};
}
await humanPause(250, 700);
await performOperationWithDelay({ stepKey: 'phone-auth', kind: 'click', label: 'phone-verification-resend' }, async () => {
simulateClick(resendButton);
@@ -891,6 +938,9 @@
}
return {
resent: true,
channel: resendInfo.channel || 'sms',
channelText: resendInfo.channelText,
text: resendInfo.text,
url: location.href,
};
}
+1 -1
View File
@@ -105,7 +105,7 @@ async function handleCommand(message) {
}
return await submitPhoneVerificationCodeWithProfileFallback(message.payload);
case 'RESEND_PHONE_VERIFICATION_CODE':
return await phoneAuthHelpers.resendPhoneVerificationCode();
return await phoneAuthHelpers.resendPhoneVerificationCode(undefined, message.payload || {});
case 'CHECK_PHONE_RESEND_ERROR':
return phoneAuthHelpers.checkPhoneResendError();
case 'RETURN_TO_ADD_PHONE':
+74
View File
@@ -449,6 +449,80 @@ test('phone auth resend stops with PHONE_ROUTE_405_RECOVERY_FAILED instead of en
}
});
test('phone auth probes WhatsApp resend channel without clicking', async () => {
const originalDocument = global.document;
const originalLocation = global.location;
const originalWindow = global.window;
let clickCount = 0;
const fakeWhatsAppButton = {
disabled: false,
textContent: '重新发送 WhatsApp 消息',
getAttribute() {
return '';
},
click() {
clickCount += 1;
},
};
const fakePhoneForm = {
querySelectorAll(selector) {
if (selector === 'button, input[type="submit"], input[type="button"]') {
return [fakeWhatsAppButton];
}
return [];
},
};
global.document = {
querySelector(selector) {
if (selector === 'form[action*="/phone-verification" i]') {
return fakePhoneForm;
}
return null;
},
querySelectorAll() {
return [];
},
};
global.location = {
href: 'https://auth.openai.com/phone-verification',
pathname: '/phone-verification',
};
global.window = global;
const helpers = api.createPhoneAuthHelpers({
fillInput: () => {},
getActionText: (element) => String(element?.textContent || ''),
getPageTextSnapshot: () => '重新发送 WhatsApp 消息',
getVerificationErrorText: () => '',
humanPause: async () => {},
isActionEnabled: () => true,
isAddPhonePageReady: () => false,
isConsentReady: () => false,
isPhoneVerificationPageReady: () => true,
isVisibleElement: () => true,
simulateClick: (element) => {
element?.click?.();
},
sleep: async () => {},
throwIfStopped: () => {},
waitForElement: async () => null,
});
try {
const result = await helpers.resendPhoneVerificationCode(1000, { probeOnly: true });
assert.equal(result.resent, false);
assert.equal(result.channel, 'whatsapp');
assert.match(result.channelText, /WhatsApp/i);
assert.equal(clickCount, 0);
} finally {
global.document = originalDocument;
global.location = originalLocation;
global.window = originalWindow;
}
});
test('phone auth exposes resend page error checks for banned numbers', () => {
const originalLocation = global.location;
const originalDocument = global.document;
+421
View File
@@ -2938,6 +2938,284 @@ test('phone verification helper immediately replaces number when page says the p
);
});
test('phone verification helper rotates number when verification code cannot be sent from add-phone rejection', async () => {
const requests = [];
const messages = [];
const submittedNumbers = [];
const refusedText = '无法向此电话号码发送验证码。请稍后重试或使用其他号码。';
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '331111', phoneNumber: '66950003111' },
{ activationId: '332222', phoneNumber: '66950003222' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: 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 === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => (id === '332222' ? 'STATUS_OK:654321' : 'STATUS_WAIT_CODE'),
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
const phoneNumber = message.payload.phoneNumber;
submittedNumbers.push(phoneNumber);
if (phoneNumber === '66950003111') {
return {
addPhoneRejected: true,
addPhonePage: true,
errorText: refusedText,
url: 'https://auth.openai.com/add-phone',
};
}
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'STEP8_GET_STATE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('delivery-refused add-phone rejection should rotate before resend.');
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const result = await helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
});
assert.deepStrictEqual(result, {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
});
assert.deepStrictEqual(submittedNumbers, ['66950003111', '66950003222']);
assert.equal(numberIndex, 2);
assert.equal(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), false);
assert.equal(
requests.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getNumber').length,
2
);
});
test('phone verification helper keeps used-number cleanup when add-phone rejection asks to use a different phone number', async () => {
const messages = [];
const submittedNumbers = [];
const usedText = 'This phone number is already linked to the maximum number of accounts. Please use a different phone number.';
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
currentPhoneActivation: null,
reusablePhoneActivation: null,
phoneReusableActivationPool: [
{
activationId: 'pool-used-different-match',
phoneNumber: '+66 95 000 4111',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 1,
maxUses: 3,
},
],
freeReusablePhoneActivation: {
activationId: 'free-used-different-match',
phoneNumber: '+66 95 000 4111',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
countryLabel: 'Thailand',
successfulUses: 0,
maxUses: 3,
source: 'free-manual-reuse',
},
};
const numbers = [
{ activationId: '341111', phoneNumber: '66950004111' },
{ activationId: '342222', phoneNumber: '66950004222' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => (id === '342222' ? 'STATUS_OK:654321' : 'STATUS_WAIT_CODE'),
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
const phoneNumber = message.payload.phoneNumber;
submittedNumbers.push(phoneNumber);
if (phoneNumber === '66950004111') {
return {
addPhoneRejected: true,
addPhonePage: true,
errorText: usedText,
url: 'https://auth.openai.com/add-phone',
};
}
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'STEP8_GET_STATE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('used add-phone rejection should rotate before resend.');
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const result = await helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
});
assert.deepStrictEqual(result, {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
});
assert.deepStrictEqual(submittedNumbers, ['66950004111', '66950004222']);
assert.equal(currentState.freeReusablePhoneActivation, null);
assert.deepStrictEqual(
currentState.phoneReusableActivationPool.filter((activation) => activation.phoneNumber === '+66 95 000 4111'),
[]
);
assert.equal(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), false);
});
test('phone verification helper treats phone_max_usage_exceeded as used-number and rotates immediately', async () => {
const requests = [];
const messages = [];
@@ -5103,6 +5381,149 @@ test('phone verification helper skips page resend for 5sim timeouts and rotates
);
});
test('phone verification helper rotates number when resend action is WhatsApp in SMS provider mode', async () => {
const requests = [];
const messages = [];
const submittedNumbers = [];
const statusCallsById = {};
const heroSmsResendStatusCalls = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 1,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 2,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '520001', phoneNumber: '66952220001' },
{ activationId: '520002', phoneNumber: '66952220002' },
];
let numberIndex = 0;
const realDateNow = Date.now;
let fakeNow = 0;
Date.now = () => fakeNow;
try {
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
const status = parsedUrl.searchParams.get('status');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
statusCallsById[id] = (statusCallsById[id] || 0) + 1;
return {
ok: true,
text: async () => (id === '520001' ? 'STATUS_WAIT_CODE' : 'STATUS_OK:123456'),
};
}
if (action === 'setStatus') {
if (id === '520001' && status === '3') {
heroSmsResendStatusCalls.push(parsedUrl);
}
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
submittedNumbers.push(message.payload.phoneNumber);
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
return {
resent: false,
channel: 'whatsapp',
channelText: '重新发送 WhatsApp 消息',
text: '重新发送 WhatsApp 消息',
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'STEP8_GET_STATE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {
fakeNow += 61000;
},
throwIfStopped: () => {},
});
const result = await helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
});
assert.deepStrictEqual(result, {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
});
assert.deepStrictEqual(submittedNumbers, ['66952220001', '66952220002']);
assert.equal(heroSmsResendStatusCalls.length, 0, 'WhatsApp resend must not call HeroSMS setStatus(3)');
assert.equal(statusCallsById['520001'], 1, 'WhatsApp resend should replace after the first SMS wait window');
assert.equal(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), true);
} finally {
Date.now = realDateNow;
}
});
test('phone verification helper rotates number immediately when 5sim activation is missing (order not found)', async () => {
const requests = [];
let currentState = {