fix: preserve oauth step metadata and resolve dev merge conflicts
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,110 @@
|
||||
// background/ip-proxy-provider-711proxy.js — 711Proxy 参数与账号规则
|
||||
(function register711ProxyProvider(root) {
|
||||
function normalizeCountryCode(value = '') {
|
||||
const raw = String(value || '').trim().toUpperCase().replace(/[^A-Z]/g, '');
|
||||
return /^[A-Z]{2}$/.test(raw) ? raw : '';
|
||||
}
|
||||
|
||||
function normalize711SessionId(value = '') {
|
||||
return String(value || '').trim().replace(/[^A-Za-z0-9_-]/g, '').slice(0, 64);
|
||||
}
|
||||
|
||||
function normalize711SessionMinutes(value = '') {
|
||||
const raw = String(value ?? '').trim();
|
||||
if (!raw) return '';
|
||||
const numeric = Number.parseInt(raw, 10);
|
||||
if (!Number.isInteger(numeric)) return '';
|
||||
return String(Math.max(1, Math.min(180, numeric)));
|
||||
}
|
||||
|
||||
function apply711SessionToUsername(username = '', options = {}) {
|
||||
const text = String(username || '').trim();
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const sessionId = normalize711SessionId(options?.sessionId || '');
|
||||
const sessTime = normalize711SessionMinutes(options?.sessTime || '');
|
||||
let next = text;
|
||||
|
||||
if (sessionId) {
|
||||
if (/(?:^|[-_])session[-_:][A-Za-z0-9_-]+?(?=(?:[-_](?:sessTime|sessAuto|region|life|zone|ptype|country|area)\b)|$)/i.test(next)) {
|
||||
next = next.replace(
|
||||
/((?:^|[-_])session[-_:])([A-Za-z0-9_-]+?)(?=(?:[-_](?:sessTime|sessAuto|region|life|zone|ptype|country|area)\b)|$)/i,
|
||||
`$1${sessionId}`
|
||||
);
|
||||
} else {
|
||||
next = `${next}-session-${sessionId}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (sessTime) {
|
||||
if (/(?:^|[-_])sessTime[-_:]?\d+\b/i.test(next)) {
|
||||
next = next.replace(/((?:^|[-_])sessTime[-_:]?)(\d+)\b/i, `$1${sessTime}`);
|
||||
} else {
|
||||
next = `${next}-sessTime-${sessTime}`;
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
function apply711RegionToUsername(username = '', regionCode = '') {
|
||||
const text = String(username || '').trim();
|
||||
const normalizedRegion = normalizeCountryCode(regionCode);
|
||||
if (!text || !normalizedRegion) {
|
||||
return text;
|
||||
}
|
||||
if (/(?:^|[-_])region[-_:]?[A-Za-z]{2}\b/i.test(text)) {
|
||||
return text.replace(/((?:^|[-_])region[-_:]?)([A-Za-z]{2})\b/i, `$1${normalizedRegion}`);
|
||||
}
|
||||
return `${text}-region-${normalizedRegion}`;
|
||||
}
|
||||
|
||||
function transform711ProxyAccountEntry(entry = {}, context = {}) {
|
||||
const state = context?.state || {};
|
||||
const hasAccountList = Boolean(context?.hasAccountList);
|
||||
const nextEntry = { ...entry };
|
||||
const username = String(nextEntry.username || '').trim();
|
||||
if (!username) {
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
const configuredRegion = normalizeCountryCode(state?.ipProxyRegion || '');
|
||||
if (!hasAccountList && configuredRegion) {
|
||||
nextEntry.username = apply711RegionToUsername(username, configuredRegion);
|
||||
if (!String(nextEntry.region || '').trim()) {
|
||||
nextEntry.region = configuredRegion;
|
||||
}
|
||||
}
|
||||
|
||||
// 账号列表模式按每行原样生效,不叠加固定账号区的 session/sessTime。
|
||||
if (hasAccountList) {
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
const sessionId = normalize711SessionId(state?.ipProxyAccountSessionPrefix || '');
|
||||
const sessTime = normalize711SessionMinutes(state?.ipProxyAccountLifeMinutes || '');
|
||||
if (!sessionId && !sessTime) {
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
nextEntry.username = apply711SessionToUsername(nextEntry.username, {
|
||||
sessionId,
|
||||
sessTime,
|
||||
});
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
root.transformIpProxyAccountEntryByProvider = function transformIpProxyAccountEntryByProvider(
|
||||
provider = '',
|
||||
entry = {},
|
||||
context = {}
|
||||
) {
|
||||
const normalizedProvider = String(provider || '').trim().toLowerCase();
|
||||
if (normalizedProvider === '711proxy') {
|
||||
return transform711ProxyAccountEntry(entry, context);
|
||||
}
|
||||
return entry;
|
||||
};
|
||||
})(typeof self !== 'undefined' ? self : globalThis);
|
||||
@@ -10,6 +10,7 @@
|
||||
buildLuckmailSessionSettingsPayload,
|
||||
buildPersistentSettingsPayload,
|
||||
broadcastDataUpdate,
|
||||
applyIpProxySettingsFromState,
|
||||
cancelScheduledAutoRun,
|
||||
checkIcloudSession,
|
||||
clearAccountRunHistory,
|
||||
@@ -58,6 +59,7 @@
|
||||
launchAutoRunTimerPlan,
|
||||
listIcloudAliases,
|
||||
listLuckmailPurchasesForManagement,
|
||||
refreshIpProxyPool,
|
||||
normalizeHotmailAccounts,
|
||||
normalizeMail2925Accounts,
|
||||
normalizeRunCount,
|
||||
@@ -69,11 +71,14 @@
|
||||
pollContributionStatus,
|
||||
registerTab,
|
||||
requestStop,
|
||||
probeIpProxyExit,
|
||||
handleCloudflareSecurityBlocked,
|
||||
resetState,
|
||||
resumeAutoRun,
|
||||
scheduleAutoRun,
|
||||
selectLuckmailPurchase,
|
||||
switchIpProxy,
|
||||
changeIpProxyExit,
|
||||
setCurrentMail2925Account,
|
||||
setCurrentHotmailAccount,
|
||||
setContributionMode,
|
||||
@@ -609,14 +614,138 @@
|
||||
}
|
||||
|
||||
case 'SAVE_SETTING': {
|
||||
const currentState = await getState();
|
||||
const updates = buildPersistentSettingsPayload(message.payload || {});
|
||||
const sessionUpdates = buildLuckmailSessionSettingsPayload(message.payload || {});
|
||||
const modeChanged = Object.prototype.hasOwnProperty.call(updates, 'plusModeEnabled')
|
||||
&& Boolean(currentState?.plusModeEnabled) !== Boolean(updates.plusModeEnabled);
|
||||
await setPersistentSettings(updates);
|
||||
await setState({
|
||||
const stateUpdates = {
|
||||
...updates,
|
||||
...sessionUpdates,
|
||||
};
|
||||
if (modeChanged && typeof getStepIdsForState === 'function') {
|
||||
const nextStateForSteps = { ...currentState, ...stateUpdates };
|
||||
stateUpdates.stepStatuses = Object.fromEntries(
|
||||
getStepIdsForState(nextStateForSteps).map((stepId) => [stepId, 'pending'])
|
||||
);
|
||||
stateUpdates.currentStep = 0;
|
||||
}
|
||||
await setState(stateUpdates);
|
||||
const mergedState = await getState();
|
||||
const hasIpProxyUpdates = Object.keys(updates).some((key) => key.startsWith('ipProxy'));
|
||||
const hasIpProxyEnabledUpdate = Object.prototype.hasOwnProperty.call(updates, 'ipProxyEnabled');
|
||||
const previousIpProxyEnabled = Boolean(currentState?.ipProxyEnabled);
|
||||
const nextIpProxyEnabled = hasIpProxyEnabledUpdate
|
||||
? Boolean(updates.ipProxyEnabled)
|
||||
: previousIpProxyEnabled;
|
||||
// 仅在“手动开关代理”时自动应用。
|
||||
// 其他字段改动(host/账号/地区/session 等)需由“同步/下一条/检测出口/Change”显式触发。
|
||||
const shouldApplyIpProxyOnSave = hasIpProxyUpdates
|
||||
&& hasIpProxyEnabledUpdate
|
||||
&& previousIpProxyEnabled !== nextIpProxyEnabled;
|
||||
let proxyRouting = null;
|
||||
if (shouldApplyIpProxyOnSave && typeof applyIpProxySettingsFromState === 'function') {
|
||||
const isEnablingProxy = !previousIpProxyEnabled && nextIpProxyEnabled;
|
||||
proxyRouting = await applyIpProxySettingsFromState(mergedState, {
|
||||
// 手动开启时自动应用一次代理,不做出口探测;
|
||||
// 出口探测由“同步/检测出口”按钮显式触发,避免开启即误判为失败。
|
||||
skipExitProbe: true,
|
||||
resetNetworkState: false,
|
||||
forceAuthRebind: false,
|
||||
suppressAuthRebind: !isEnablingProxy,
|
||||
}).catch((error) => ({
|
||||
applied: false,
|
||||
reason: 'apply_failed',
|
||||
error: error?.message || String(error || '代理应用失败'),
|
||||
}));
|
||||
}
|
||||
if (Boolean(currentState?.contributionMode) && typeof setContributionMode === 'function') {
|
||||
await setContributionMode(true);
|
||||
}
|
||||
if (modeChanged) {
|
||||
await addLog(
|
||||
Boolean(updates.plusModeEnabled)
|
||||
? 'Plus 模式已开启,已切换为 Plus Checkout + PayPal 步骤。'
|
||||
: 'Plus 模式已关闭,已恢复普通注册授权步骤。',
|
||||
'info'
|
||||
);
|
||||
}
|
||||
return { ok: true, state: await getState(), proxyRouting };
|
||||
}
|
||||
|
||||
case 'REFRESH_IP_PROXY_POOL': {
|
||||
if (typeof refreshIpProxyPool !== 'function') {
|
||||
throw new Error('IP 代理池能力尚未接入。');
|
||||
}
|
||||
const result = await refreshIpProxyPool({
|
||||
maxItems: message.payload?.maxItems,
|
||||
mode: message.payload?.mode,
|
||||
});
|
||||
return { ok: true, state: await getState() };
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
|
||||
case 'SWITCH_IP_PROXY': {
|
||||
if (typeof switchIpProxy !== 'function') {
|
||||
throw new Error('IP 代理切换能力尚未接入。');
|
||||
}
|
||||
const result = await switchIpProxy(message.payload?.direction || 'next', {
|
||||
maxItems: message.payload?.maxItems,
|
||||
mode: message.payload?.mode,
|
||||
forceRefresh: message.payload?.forceRefresh,
|
||||
});
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
|
||||
case 'CHANGE_IP_PROXY_EXIT': {
|
||||
if (typeof changeIpProxyExit !== 'function') {
|
||||
throw new Error('IP 代理 Change 能力尚未接入。');
|
||||
}
|
||||
const result = await changeIpProxyExit({
|
||||
mode: message.payload?.mode,
|
||||
});
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
|
||||
case 'PROBE_IP_PROXY_EXIT': {
|
||||
if (typeof probeIpProxyExit !== 'function') {
|
||||
throw new Error('IP 代理出口检测能力尚未接入。');
|
||||
}
|
||||
const probeState = await getState();
|
||||
const mode = typeof normalizeIpProxyMode === 'function'
|
||||
? normalizeIpProxyMode(probeState?.ipProxyMode)
|
||||
: String(probeState?.ipProxyMode || 'account').trim().toLowerCase();
|
||||
const provider = typeof normalizeIpProxyProviderValue === 'function'
|
||||
? normalizeIpProxyProviderValue(probeState?.ipProxyService)
|
||||
: String(probeState?.ipProxyService || '').trim().toLowerCase();
|
||||
const is711AccountMode = mode === 'account' && provider === '711proxy';
|
||||
const previousReason = String(probeState?.ipProxyAppliedReason || '').trim().toLowerCase();
|
||||
const previousExitError = String(probeState?.ipProxyAppliedExitError || '').trim();
|
||||
const hadMissingAuthChallenge = /challenge=0|provided=0|未触发代理鉴权挑战|未收到 407/i.test(previousExitError);
|
||||
const shouldPreRebindBeforeProbe = Boolean(
|
||||
probeState?.ipProxyEnabled
|
||||
&& is711AccountMode
|
||||
&& (hadMissingAuthChallenge || previousReason === 'connectivity_failed')
|
||||
);
|
||||
const timeoutMs = Number(message.payload?.timeoutMs) > 0
|
||||
? Number(message.payload.timeoutMs)
|
||||
: (is711AccountMode ? (shouldPreRebindBeforeProbe ? 8000 : 6000) : undefined);
|
||||
|
||||
// 手动“检测出口”前先轻量应用当前配置,避免读取到旧代理链路状态。
|
||||
if (probeState?.ipProxyEnabled && typeof applyIpProxySettingsFromState === 'function') {
|
||||
await applyIpProxySettingsFromState(probeState, {
|
||||
skipExitProbe: true,
|
||||
resetNetworkState: shouldPreRebindBeforeProbe,
|
||||
forceAuthRebind: shouldPreRebindBeforeProbe,
|
||||
suppressAuthRebind: !shouldPreRebindBeforeProbe,
|
||||
}).catch(() => null);
|
||||
}
|
||||
|
||||
const result = await probeIpProxyExit({
|
||||
timeoutMs,
|
||||
authRebindMaxAttempts: is711AccountMode ? 1 : undefined,
|
||||
});
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
|
||||
case 'EXPORT_SETTINGS': {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
addLog,
|
||||
chrome,
|
||||
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
|
||||
completeStepFromBackground,
|
||||
confirmCustomVerificationStepBypass,
|
||||
ensureMail2925MailboxSession,
|
||||
ensureIcloudMailSession,
|
||||
@@ -68,6 +69,68 @@
|
||||
return String(value || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
async function completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, options = {}) {
|
||||
await setState({
|
||||
step8VerificationTargetEmail: '',
|
||||
loginVerificationRequestedAt: null,
|
||||
});
|
||||
const fromRecovery = Boolean(options.fromRecovery);
|
||||
await addLog(
|
||||
`步骤 ${visibleStep}:当前认证页已进入 OAuth 授权页${fromRecovery ? '(轮询失败后复核)' : ''},跳过登录验证码拉取并继续后续流程。`,
|
||||
'warn'
|
||||
);
|
||||
if (typeof completeStepFromBackground === 'function') {
|
||||
await completeStepFromBackground(visibleStep, {
|
||||
loginVerificationRequestedAt: null,
|
||||
skipLoginVerificationStep: true,
|
||||
directOAuthConsentPage: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isStep8AddPhoneStateError(error) {
|
||||
const message = String(error?.message || error || '');
|
||||
return /add-phone|手机号页面|手机号验证页|phone[\s-_]verification|phone\s+number/i.test(message);
|
||||
}
|
||||
|
||||
async function recoverStep8PollingFailure(currentState, visibleStep) {
|
||||
const authLoginStep = getAuthLoginStepForVisibleStep(visibleStep);
|
||||
try {
|
||||
const pageState = await ensureStep8VerificationPageReady({
|
||||
visibleStep,
|
||||
authLoginStep,
|
||||
timeoutMs: await getStep8ReadyTimeoutMs(
|
||||
'登录验证码轮询异常后复核认证页状态',
|
||||
currentState?.oauthUrl || '',
|
||||
visibleStep
|
||||
),
|
||||
});
|
||||
if (pageState?.state === 'oauth_consent_page') {
|
||||
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, { fromRecovery: true });
|
||||
return { outcome: 'completed' };
|
||||
}
|
||||
if (pageState?.state === 'verification_page') {
|
||||
await addLog(
|
||||
`步骤 ${visibleStep}:检测到邮箱轮询/页面通信异常,但认证页仍在验证码页,先在当前链路重试,不回到步骤 ${authLoginStep}。`,
|
||||
'warn'
|
||||
);
|
||||
return { outcome: 'retry_without_step7' };
|
||||
}
|
||||
} catch (inspectError) {
|
||||
if (isStep8RestartStep7Error(inspectError)) {
|
||||
return { outcome: 'restart_step7', error: inspectError };
|
||||
}
|
||||
if (isStep8AddPhoneStateError(inspectError)) {
|
||||
throw inspectError;
|
||||
}
|
||||
await addLog(
|
||||
`步骤 ${visibleStep}:轮询失败后复核认证页状态异常:${inspectError?.message || inspectError},将回到步骤 ${authLoginStep} 重试。`,
|
||||
'warn'
|
||||
);
|
||||
}
|
||||
return { outcome: 'restart_step7' };
|
||||
}
|
||||
|
||||
function getExpectedMail2925MailboxEmail(state = {}) {
|
||||
if (Boolean(state?.mail2925UseAccountPool)) {
|
||||
const currentAccountId = String(state?.currentMail2925AccountId || '').trim();
|
||||
@@ -131,6 +194,10 @@
|
||||
authLoginStep: getAuthLoginStepForVisibleStep(visibleStep),
|
||||
timeoutMs: await getStep8ReadyTimeoutMs('确认登录验证码页已就绪', state?.oauthUrl || '', visibleStep),
|
||||
});
|
||||
if (pageState?.state === 'oauth_consent_page') {
|
||||
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep);
|
||||
return;
|
||||
}
|
||||
const shouldCompareVerificationEmail = mail.provider !== '2925';
|
||||
const displayedVerificationEmail = shouldCompareVerificationEmail
|
||||
? normalizeStep8VerificationTargetEmail(pageState?.displayedEmail)
|
||||
@@ -201,9 +268,11 @@
|
||||
getRemainingTimeMs: getStep8RemainingTimeResolver(state?.oauthUrl || '', visibleStep),
|
||||
requestFreshCodeFirst: false,
|
||||
targetEmail: fixedTargetEmail,
|
||||
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
resendIntervalMs: mail.provider === LUCKMAIL_PROVIDER
|
||||
? 15000
|
||||
: ((mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -224,24 +293,47 @@
|
||||
} catch (err) {
|
||||
const visibleStep = getVisibleStep(currentState, 8);
|
||||
const authLoginStep = getAuthLoginStepForVisibleStep(visibleStep);
|
||||
if (!isVerificationMailPollingError(err) && !isStep8RestartStep7Error(err)) {
|
||||
throw err;
|
||||
let currentError = err;
|
||||
let retryWithoutStep7 = false;
|
||||
const isMailPollingError = isVerificationMailPollingError(err);
|
||||
if (isMailPollingError && !isStep8RestartStep7Error(err)) {
|
||||
const recovery = await recoverStep8PollingFailure(currentState, visibleStep);
|
||||
if (recovery?.outcome === 'completed') {
|
||||
return;
|
||||
}
|
||||
if (recovery?.outcome === 'retry_without_step7') {
|
||||
retryWithoutStep7 = true;
|
||||
}
|
||||
if (recovery?.error) {
|
||||
currentError = recovery.error;
|
||||
}
|
||||
}
|
||||
if (!isVerificationMailPollingError(currentError) && !isStep8RestartStep7Error(currentError)) {
|
||||
throw currentError;
|
||||
}
|
||||
|
||||
lastMailPollingError = err;
|
||||
lastMailPollingError = currentError;
|
||||
if (mailPollingAttempt >= STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS) {
|
||||
break;
|
||||
}
|
||||
|
||||
mailPollingAttempt += 1;
|
||||
if (retryWithoutStep7) {
|
||||
await addLog(
|
||||
`步骤 ${visibleStep}:认证页仍保持在验证码页,将在当前链路直接重试(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS}),不回到步骤 ${authLoginStep}。`,
|
||||
'warn'
|
||||
);
|
||||
currentState = await getState();
|
||||
continue;
|
||||
}
|
||||
await addLog(
|
||||
isStep8RestartStep7Error(err)
|
||||
isStep8RestartStep7Error(currentError)
|
||||
? `步骤 ${visibleStep}:检测到认证页进入重试/超时报错状态,准备从步骤 ${authLoginStep} 重新开始(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS})...`
|
||||
: `步骤 ${visibleStep}:检测到邮箱轮询类失败,准备从步骤 ${authLoginStep} 重新开始(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS})...`,
|
||||
'warn'
|
||||
);
|
||||
await rerunStep7ForStep8Recovery({
|
||||
logMessage: isStep8RestartStep7Error(err)
|
||||
logMessage: isStep8RestartStep7Error(currentError)
|
||||
? `步骤 ${visibleStep}:认证页进入重试/超时报错状态,正在回到步骤 ${authLoginStep} 重新发起登录流程...`
|
||||
: `步骤 ${visibleStep}:正在回到步骤 ${authLoginStep},重新发起登录验证码流程...`,
|
||||
});
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
|
||||
const shouldRequestFreshCodeFirst = ![
|
||||
HOTMAIL_PROVIDER,
|
||||
LUCKMAIL_PROVIDER,
|
||||
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
|
||||
].includes(mail.provider);
|
||||
|
||||
@@ -157,9 +158,11 @@
|
||||
sessionKey: verificationSessionKey,
|
||||
disableTimeBudgetCap: mail.provider === '2925',
|
||||
requestFreshCodeFirst: shouldRequestFreshCodeFirst,
|
||||
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
resendIntervalMs: mail.provider === LUCKMAIL_PROVIDER
|
||||
? 15000
|
||||
: ((mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,86 @@
|
||||
};
|
||||
}
|
||||
|
||||
async function detectStep8PostSubmitFallback(options = {}) {
|
||||
const timeoutMs = Math.max(1000, Number(options.timeoutMs) || 9000);
|
||||
const pollIntervalMs = Math.max(100, Number(options.pollIntervalMs) || 300);
|
||||
const step = Number(options.step) || 8;
|
||||
const startedAt = Date.now();
|
||||
let lastSnapshot = null;
|
||||
|
||||
while (Date.now() - startedAt < timeoutMs) {
|
||||
throwIfStopped();
|
||||
try {
|
||||
const request = {
|
||||
type: 'GET_LOGIN_AUTH_STATE',
|
||||
source: 'background',
|
||||
payload: {},
|
||||
};
|
||||
const requestTimeoutMs = Math.max(1200, Math.min(5000, timeoutMs));
|
||||
const result = typeof sendToContentScriptResilient === 'function'
|
||||
? await sendToContentScriptResilient(
|
||||
'signup-page',
|
||||
request,
|
||||
{
|
||||
timeoutMs: requestTimeoutMs,
|
||||
responseTimeoutMs: requestTimeoutMs,
|
||||
retryDelayMs: 400,
|
||||
logMessage: `步骤 ${step}:验证码提交后页面正在切换,等待页面恢复并确认授权状态...`,
|
||||
}
|
||||
)
|
||||
: await sendToContentScript('signup-page', request, {
|
||||
responseTimeoutMs: requestTimeoutMs,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
const authState = String(result?.state || '').trim();
|
||||
const authUrl = String(result?.url || '').trim();
|
||||
lastSnapshot = {
|
||||
state: authState || 'unknown',
|
||||
url: authUrl,
|
||||
};
|
||||
|
||||
if (authState === 'oauth_consent_page') {
|
||||
return {
|
||||
success: true,
|
||||
reason: 'oauth_consent_page',
|
||||
addPhonePage: false,
|
||||
url: authUrl,
|
||||
};
|
||||
}
|
||||
if (authState === 'add_phone_page' || authState === 'phone_verification_page') {
|
||||
return {
|
||||
success: true,
|
||||
reason: 'add_phone_page',
|
||||
addPhonePage: true,
|
||||
url: authUrl || 'https://auth.openai.com/add-phone',
|
||||
};
|
||||
}
|
||||
if (authState === 'login_timeout_error_page') {
|
||||
return {
|
||||
success: false,
|
||||
reason: 'login_timeout_error_page',
|
||||
restartStep7: true,
|
||||
url: authUrl,
|
||||
};
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore transient inspect failures and keep polling.
|
||||
}
|
||||
|
||||
await sleepWithStop(pollIntervalMs);
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
reason: 'unknown',
|
||||
snapshot: lastSnapshot,
|
||||
};
|
||||
}
|
||||
|
||||
function getVerificationResendStateKey() {
|
||||
return 'verificationResendCount';
|
||||
}
|
||||
@@ -772,6 +852,31 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
if (step === 8 && isRetryableVerificationTransportError(err)) {
|
||||
const fallback = await detectStep8PostSubmitFallback({
|
||||
step,
|
||||
timeoutMs: 9000,
|
||||
pollIntervalMs: 300,
|
||||
});
|
||||
if (fallback.success) {
|
||||
if (fallback.addPhonePage) {
|
||||
await addLog('步骤 8:验证码提交后通信中断,但页面已进入手机号验证页,按提交成功继续。', 'warn');
|
||||
} else {
|
||||
await addLog('步骤 8:验证码提交后通信中断,但页面已进入 OAuth 授权页,按提交成功继续。', 'warn');
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
assumed: true,
|
||||
transportRecovered: true,
|
||||
addPhonePage: Boolean(fallback.addPhonePage),
|
||||
url: fallback.url || '',
|
||||
};
|
||||
}
|
||||
if (fallback.restartStep7) {
|
||||
const urlPart = fallback.url ? ` URL: ${fallback.url}` : '';
|
||||
throw new Error(`STEP8_RESTART_STEP7::步骤 8:验证码提交后认证页进入登录超时报错页,请回到步骤 7 重新开始。${urlPart}`.trim());
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
@@ -812,7 +917,7 @@
|
||||
getLegacyVerificationResendCountDefault(step, { requestFreshCodeFirst })
|
||||
)
|
||||
: getConfiguredVerificationResendCount(step, state, { requestFreshCodeFirst });
|
||||
const maxSubmitAttempts = 15;
|
||||
const maxSubmitAttempts = mail.provider === LUCKMAIL_PROVIDER ? 3 : 15;
|
||||
const resendIntervalMs = Math.max(0, Number(options.resendIntervalMs) || 0);
|
||||
let lastResendAt = Number(options.lastResendAt) || 0;
|
||||
|
||||
@@ -897,6 +1002,12 @@
|
||||
throw new Error(`步骤 ${step}:验证码连续失败,已达到 ${maxSubmitAttempts} 次重试上限。`);
|
||||
}
|
||||
|
||||
if (mail.provider === LUCKMAIL_PROVIDER) {
|
||||
await addLog(`步骤 ${step}:LuckMail 验证码提交失败,等待 15 秒后重新轮询 /code 接口(${attempt + 1}/${maxSubmitAttempts})...`, 'warn');
|
||||
await sleepWithStop(15000);
|
||||
continue;
|
||||
}
|
||||
|
||||
const remainingBeforeResendMs = resendIntervalMs > 0 && lastResendAt > 0
|
||||
? Math.max(0, resendIntervalMs - (Date.now() - lastResendAt))
|
||||
: 0;
|
||||
|
||||
Reference in New Issue
Block a user