diff --git a/background.js b/background.js index 48b997e..df04191 100644 --- a/background.js +++ b/background.js @@ -10898,6 +10898,7 @@ const phoneVerificationHelpers = self.MultiPageBackgroundPhoneVerification?.crea DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS, DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS, DEFAULT_PHONE_CODE_POLL_ROUNDS, + readAuthTabSnapshot, ensureStep8SignupPageReady, navigateAuthTabToAddPhone: async (tabId, options = {}) => { const visibleStep = Math.floor(Number(options.visibleStep || options.step) || 0) || 9; @@ -12204,6 +12205,26 @@ async function ensureStep8SignupPageReady(tabId, options = {}) { }); } +async function readAuthTabSnapshot(tabId) { + if (!Number.isInteger(tabId)) { + return null; + } + try { + const executionResults = await chrome.scripting.executeScript({ + target: { tabId }, + world: 'ISOLATED', + func: () => ({ + url: String(location.href || ''), + title: String(document.title || ''), + text: String(document.body?.innerText || document.documentElement?.innerText || '').trim(), + }), + }); + return executionResults?.[0]?.result || null; + } catch { + return null; + } +} + async function getStep8PageState(tabId, responseTimeoutMs = 1500, visibleStep = 9) { try { const result = await sendTabMessageWithTimeout(tabId, 'signup-page', { diff --git a/background/phone-verification-flow.js b/background/phone-verification-flow.js index 193adff..7cd9694 100644 --- a/background/phone-verification-flow.js +++ b/background/phone-verification-flow.js @@ -11,6 +11,7 @@ getOAuthFlowStepTimeoutMs, getState, requestStop = null, + readAuthTabSnapshot = null, sendToContentScript, sendToContentScriptResilient, navigateAuthTabToAddPhone = null, @@ -1407,6 +1408,44 @@ return new Error(`${PHONE_RESEND_SERVER_ERROR_PREFIX}${message || 'OpenAI contact-verification page returned HTTP ERROR 500 after resend.'}`); } + function getPhoneResendServerErrorFromSnapshot(snapshot = {}) { + const rawUrl = String(snapshot?.url || snapshot?.href || '').trim(); + if (!/\/contact-verification(?:[/?#]|$)/i.test(rawUrl)) { + return ''; + } + const combined = [ + snapshot?.text, + snapshot?.bodyText, + snapshot?.title, + ] + .filter(Boolean) + .join(' ') + .replace(/\s+/g, ' ') + .trim(); + if (!isPhoneResendServerError(combined)) { + return ''; + } + return combined || 'OpenAI contact-verification page returned HTTP ERROR 500 after resend.'; + } + + async function readPhoneResendServerErrorFromAuthTab(tabId) { + if (typeof readAuthTabSnapshot !== 'function') { + return ''; + } + try { + return getPhoneResendServerErrorFromSnapshot(await readAuthTabSnapshot(tabId)); + } catch (_) { + return ''; + } + } + + async function throwPhoneResendServerErrorIfAuthTabShowsIt(tabId) { + const serverErrorText = await readPhoneResendServerErrorFromAuthTab(tabId); + if (serverErrorText) { + throw buildPhoneResendServerError(serverErrorText); + } + } + function shouldTreatResendThrottledAsBanned(state = {}) { return Boolean(state?.phoneResendThrottledAsBannedEnabled); } @@ -5457,6 +5496,7 @@ if (isPhoneResendServerError(resendError)) { throw buildPhoneResendServerError(resendError); } + await throwPhoneResendServerErrorIfAuthTabShowsIt(tabId); await addLog(`步骤 4:注册手机验证码页面重发失败,将继续轮询短信。${resendError.message}`, 'warn', { step: 4, stepKey: 'fetch-signup-code', @@ -5497,6 +5537,7 @@ if (isPhoneResendServerError(resendError)) { throw buildPhoneResendServerError(resendError); } + await throwPhoneResendServerErrorIfAuthTabShowsIt(tabId); await addLog(`步骤 4:验证码被拒后点击重发失败。${resendError.message}`, 'warn', { step: 4, stepKey: 'fetch-signup-code', diff --git a/tests/phone-verification-flow.test.js b/tests/phone-verification-flow.test.js index 3533d17..705ae17 100644 --- a/tests/phone-verification-flow.test.js +++ b/tests/phone-verification-flow.test.js @@ -5888,6 +5888,77 @@ test('signup phone verification cancels activation when resend lands on contact- assert.equal(currentState.signupPhoneActivation, null); }); +test('signup phone verification cancels activation when resend lands on contact-verification 500 page but content script drops', async () => { + const requests = []; + const tabSnapshots = []; + let currentState = { + heroSmsApiKey: 'demo-key', + heroSmsCountryId: 52, + heroSmsCountryLabel: 'Thailand', + verificationResendCount: 0, + phoneCodeWaitSeconds: 60, + phoneCodeTimeoutWindows: 2, + phoneCodePollIntervalSeconds: 1, + phoneCodePollMaxRounds: 1, + signupPhoneActivation: { + activationId: '930001', + phoneNumber: '66953330002', + 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 }), + readAuthTabSnapshot: async () => { + tabSnapshots.push('read'); + return { + url: 'https://auth.openai.com/contact-verification', + title: 'auth.openai.com', + text: "This page isn't working auth.openai.com is currently unable to handle this request. HTTP ERROR 500", + }; + }, + sendToContentScriptResilient: async (_source, message) => { + if (message.type === 'RESEND_VERIFICATION_CODE') { + throw new Error('Could not establish connection. Receiving end does not exist.'); + } + 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/); + return true; + } + ); + + assert.equal(tabSnapshots.length >= 1, true); + assert.equal(currentState.signupPhoneActivation, null); + assert.equal(requests.filter((request) => request.searchParams.get('action') === 'getStatus').length, 1); +}); + test('phone verification helper skips page resend for 5sim timeouts and rotates number directly', async () => { const requests = []; const messages = [];