feat: continue OAuth flow through HeroSMS phone verification
- 合并 dev 最新主线改动:同步当前认证页恢复、来源配置和侧栏能力的现有基线 - 本地补充修复:补齐 HeroSMS 手机验证与 sidepanel 初始化冲突,修正 Step 9 等待逻辑里的未声明变量,并同步结构/链路文档与回归测试 - 影响范围:OAuth 步骤 8/9、HeroSMS 配置、认证页内容脚本、侧栏初始化、项目文档与测试
This commit is contained in:
+330
-54
@@ -140,22 +140,26 @@ const ICLOUD_LOGIN_URLS = [
|
||||
];
|
||||
const ICLOUD_PROVIDER = 'icloud';
|
||||
const GMAIL_PROVIDER = 'gmail';
|
||||
const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
|
||||
const HOTMAIL_PROVIDER = 'hotmail-api';
|
||||
const LUCKMAIL_PROVIDER = 'luckmail-api';
|
||||
const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
|
||||
const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email';
|
||||
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
|
||||
const HOTMAIL_MAILBOXES = ['INBOX', 'Junk'];
|
||||
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
|
||||
const CLOUDFLARE_SECURITY_BLOCK_ERROR_PREFIX = 'CF_SECURITY_BLOCKED::';
|
||||
const CLOUDFLARE_SECURITY_BLOCK_USER_MESSAGE = '您已触发Cloudflare 安全防护系统,已完全停止流程,请不要短时间内多次进行重新发送验证码,连续刷新、反复点击重试会加重风控;请先关闭页面等待 15-30 分钟,让系统的临时限制自动解除。或者更换浏览器';
|
||||
const BROWSER_SWITCH_REQUIRED_ERROR_PREFIX = 'BROWSER_SWITCH_REQUIRED::';
|
||||
const HUMAN_STEP_DELAY_MIN = 700;
|
||||
const HUMAN_STEP_DELAY_MAX = 2200;
|
||||
const STEP6_MAX_ATTEMPTS = 3;
|
||||
const STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS = 8;
|
||||
const OAUTH_FLOW_TIMEOUT_MS = 6 * 60 * 1000;
|
||||
const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
const SUB2API_STEP1_RESPONSE_TIMEOUT_MS = 90000;
|
||||
const SUB2API_STEP9_RESPONSE_TIMEOUT_MS = 120000;
|
||||
const DEFAULT_SUB2API_URL = 'https://sub2api.hisence.fun/admin/accounts';
|
||||
const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts';
|
||||
const DEFAULT_SUB2API_GROUP_NAME = 'codex';
|
||||
const DEFAULT_SUB2API_PROXY_NAME = '';
|
||||
const DEFAULT_SUB2API_REDIRECT_URI = 'http://localhost:1455/auth/callback';
|
||||
@@ -257,6 +261,8 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
sub2apiPassword: '',
|
||||
sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME,
|
||||
sub2apiDefaultProxyName: DEFAULT_SUB2API_PROXY_NAME,
|
||||
codex2apiUrl: DEFAULT_CODEX2API_URL,
|
||||
codex2apiAdminKey: '',
|
||||
customPassword: '',
|
||||
autoRunSkipFailures: false,
|
||||
autoRunFallbackThreadIntervalMinutes: 0,
|
||||
@@ -268,12 +274,16 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
mail2925Mode: DEFAULT_MAIL_2925_MODE,
|
||||
mail2925UseAccountPool: false,
|
||||
emailGenerator: 'duck',
|
||||
customMailProviderPool: [],
|
||||
customEmailPool: [],
|
||||
autoDeleteUsedIcloudAlias: false,
|
||||
icloudHostPreference: 'auto',
|
||||
icloudFetchMode: 'reuse_existing',
|
||||
accountRunHistoryTextEnabled: false,
|
||||
accountRunHistoryHelperBaseUrl: DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL,
|
||||
gmailBaseEmail: '',
|
||||
mail2925BaseEmail: '',
|
||||
currentMail2925AccountId: '',
|
||||
emailPrefix: '',
|
||||
inbucketHost: '',
|
||||
inbucketMailbox: '',
|
||||
@@ -286,6 +296,7 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
cloudflareTempEmailAdminAuth: '',
|
||||
cloudflareTempEmailCustomAuth: '',
|
||||
cloudflareTempEmailReceiveMailbox: '',
|
||||
cloudflareTempEmailUseRandomSubdomain: false,
|
||||
cloudflareTempEmailDomain: '',
|
||||
cloudflareTempEmailDomains: [],
|
||||
hotmailAccounts: [],
|
||||
@@ -336,6 +347,8 @@ const DEFAULT_STATE = {
|
||||
sub2apiGroupId: null, // SUB2API 目标分组 ID。
|
||||
sub2apiDraftName: null, // SUB2API 本轮预生成的账号名称。
|
||||
sub2apiProxyId: null, // SUB2API 本轮使用的代理 ID。
|
||||
codex2apiSessionId: null, // Codex2API OAuth 会话 ID。
|
||||
codex2apiOAuthState: null, // Codex2API OAuth state。
|
||||
flowStartTime: null, // 当前流程开始时间。
|
||||
tabRegistry: {}, // 程序维护的标签页注册表。
|
||||
sourceLastUrls: {}, // 各来源页面最近一次打开的地址记录。
|
||||
@@ -650,9 +663,21 @@ function getAutoRunTimerStatusPayload(plan) {
|
||||
|
||||
function normalizeEmailGenerator(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string'
|
||||
? CUSTOM_EMAIL_POOL_GENERATOR
|
||||
: 'custom-pool';
|
||||
const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string'
|
||||
? GMAIL_ALIAS_GENERATOR
|
||||
: 'gmail-alias';
|
||||
if (normalized === 'custom' || normalized === 'manual') {
|
||||
return 'custom';
|
||||
}
|
||||
if (normalized === gmailAliasGenerator) {
|
||||
return gmailAliasGenerator;
|
||||
}
|
||||
if (normalized === customEmailPoolGenerator) {
|
||||
return customEmailPoolGenerator;
|
||||
}
|
||||
if (normalized === 'icloud') {
|
||||
return 'icloud';
|
||||
}
|
||||
@@ -661,8 +686,60 @@ function normalizeEmailGenerator(value = '') {
|
||||
return 'duck';
|
||||
}
|
||||
|
||||
function normalizeIcloudFetchMode(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === 'always_new' ? 'always_new' : 'reuse_existing';
|
||||
}
|
||||
|
||||
function normalizeCustomEmailPool(value = []) {
|
||||
const source = Array.isArray(value)
|
||||
? value
|
||||
: String(value || '').split(/[\r\n,,;;]+/);
|
||||
|
||||
return source
|
||||
.map((item) => String(item || '').trim().toLowerCase())
|
||||
.filter((item) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(item));
|
||||
}
|
||||
|
||||
function isCustomEmailPoolGenerator(stateOrValue = {}) {
|
||||
const generator = typeof stateOrValue === 'string'
|
||||
? stateOrValue
|
||||
: stateOrValue?.emailGenerator;
|
||||
const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string'
|
||||
? CUSTOM_EMAIL_POOL_GENERATOR
|
||||
: 'custom-pool';
|
||||
return normalizeEmailGenerator(generator) === customEmailPoolGenerator;
|
||||
}
|
||||
|
||||
function getCustomEmailPool(state = {}) {
|
||||
return normalizeCustomEmailPool(state?.customEmailPool);
|
||||
}
|
||||
|
||||
function getCustomEmailPoolEmailForRun(state = {}, targetRun = 1) {
|
||||
const entries = getCustomEmailPool(state);
|
||||
const numericRun = Math.max(1, Math.floor(Number(targetRun) || 1));
|
||||
return entries[numericRun - 1] || '';
|
||||
}
|
||||
|
||||
function getCustomMailProviderPool(state = {}) {
|
||||
return normalizeCustomEmailPool(state?.customMailProviderPool);
|
||||
}
|
||||
|
||||
function getCustomMailProviderPoolEmailForRun(state = {}, targetRun = 1) {
|
||||
const entries = getCustomMailProviderPool(state);
|
||||
const numericRun = Math.max(1, Math.floor(Number(targetRun) || 1));
|
||||
return entries[numericRun - 1] || '';
|
||||
}
|
||||
|
||||
function normalizePanelMode(value = '') {
|
||||
return String(value || '').trim().toLowerCase() === 'sub2api' ? 'sub2api' : 'cpa';
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
if (normalized === 'sub2api') {
|
||||
return 'sub2api';
|
||||
}
|
||||
if (normalized === 'codex2api') {
|
||||
return 'codex2api';
|
||||
}
|
||||
return 'cpa';
|
||||
}
|
||||
|
||||
function normalizeMailProvider(value = '') {
|
||||
@@ -838,6 +915,7 @@ function getCloudflareTempEmailConfig(state = {}) {
|
||||
adminAuth: String(state.cloudflareTempEmailAdminAuth || ''),
|
||||
customAuth: String(state.cloudflareTempEmailCustomAuth || ''),
|
||||
receiveMailbox: normalizeCloudflareTempEmailReceiveMailbox(state.cloudflareTempEmailReceiveMailbox),
|
||||
useRandomSubdomain: Boolean(state.cloudflareTempEmailUseRandomSubdomain),
|
||||
domain: normalizeCloudflareTempEmailDomain(state.cloudflareTempEmailDomain),
|
||||
domains: normalizeCloudflareTempEmailDomains(state.cloudflareTempEmailDomains),
|
||||
};
|
||||
@@ -883,6 +961,10 @@ function normalizePersistentSettingValue(key, value) {
|
||||
return String(value || '').trim();
|
||||
case 'sub2apiDefaultProxyName':
|
||||
return String(value || '').trim();
|
||||
case 'codex2apiUrl':
|
||||
return normalizeCodex2ApiUrl(value);
|
||||
case 'codex2apiAdminKey':
|
||||
return String(value || '').trim();
|
||||
case 'customPassword':
|
||||
return String(value || '');
|
||||
case 'autoRunSkipFailures':
|
||||
@@ -904,15 +986,22 @@ function normalizePersistentSettingValue(key, value) {
|
||||
return Boolean(value);
|
||||
case 'emailGenerator':
|
||||
return normalizeEmailGenerator(value);
|
||||
case 'customMailProviderPool':
|
||||
case 'customEmailPool':
|
||||
return normalizeCustomEmailPool(value);
|
||||
case 'autoDeleteUsedIcloudAlias':
|
||||
case 'accountRunHistoryTextEnabled':
|
||||
case 'cloudflareTempEmailUseRandomSubdomain':
|
||||
return Boolean(value);
|
||||
case 'icloudHostPreference':
|
||||
return normalizeIcloudHost(value) || 'auto';
|
||||
case 'icloudFetchMode':
|
||||
return normalizeIcloudFetchMode(value);
|
||||
case 'accountRunHistoryHelperBaseUrl':
|
||||
return normalizeAccountRunHistoryHelperBaseUrl(value);
|
||||
case 'gmailBaseEmail':
|
||||
case 'mail2925BaseEmail':
|
||||
case 'currentMail2925AccountId':
|
||||
case 'emailPrefix':
|
||||
return String(value || '').trim();
|
||||
case 'inbucketHost':
|
||||
@@ -2146,6 +2235,18 @@ function parseGmailBaseEmail(rawValue) {
|
||||
}
|
||||
|
||||
function isGeneratedAliasProvider(stateOrProvider, mail2925Mode = undefined) {
|
||||
if (
|
||||
stateOrProvider
|
||||
&& typeof stateOrProvider === 'object'
|
||||
&& !Array.isArray(stateOrProvider)
|
||||
&& normalizeEmailGenerator(stateOrProvider.emailGenerator) === (
|
||||
typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string'
|
||||
? CUSTOM_EMAIL_POOL_GENERATOR
|
||||
: 'custom-pool'
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const provider = typeof stateOrProvider === 'string'
|
||||
? stateOrProvider
|
||||
: stateOrProvider?.mailProvider;
|
||||
@@ -2303,6 +2404,18 @@ function getManagedAliasBaseEmail(state = {}, provider = state?.mailProvider) {
|
||||
}
|
||||
|
||||
function isGeneratedAliasProvider(stateOrProvider, mail2925Mode = undefined) {
|
||||
if (
|
||||
stateOrProvider
|
||||
&& typeof stateOrProvider === 'object'
|
||||
&& !Array.isArray(stateOrProvider)
|
||||
&& normalizeEmailGenerator(stateOrProvider.emailGenerator) === (
|
||||
typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string'
|
||||
? CUSTOM_EMAIL_POOL_GENERATOR
|
||||
: 'custom-pool'
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const provider = typeof stateOrProvider === 'string'
|
||||
? stateOrProvider
|
||||
: stateOrProvider?.mailProvider;
|
||||
@@ -3017,6 +3130,16 @@ async function pollLuckmailVerificationCode(step, state, pollPayload = {}) {
|
||||
excludeCodes: pollPayload.excludeCodes || [],
|
||||
};
|
||||
|
||||
const initialCursor = normalizeLuckmailMailCursor((await getState()).currentLuckmailMailCursor);
|
||||
if (!initialCursor.messageId && !initialCursor.receivedAt) {
|
||||
const mailList = await client.user.getTokenMails(purchase.token);
|
||||
const baselineCursor = buildLuckmailBaselineCursor(mailList?.mails || []);
|
||||
await setLuckmailMailCursorState(baselineCursor);
|
||||
if (baselineCursor?.messageId || baselineCursor?.receivedAt) {
|
||||
await addLog(`步骤 ${step}:LuckMail 已保存当前邮箱旧邮件快照,后续仅使用新收到的验证码。`, 'info');
|
||||
}
|
||||
}
|
||||
|
||||
let lastError = null;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
throwIfStopped();
|
||||
@@ -3360,10 +3483,16 @@ async function validateIcloudSession(setupUrl) {
|
||||
return data;
|
||||
}
|
||||
|
||||
async function resolveIcloudPremiumMailService() {
|
||||
async function resolveIcloudPremiumMailService(options = {}) {
|
||||
const errors = [];
|
||||
const state = await getState();
|
||||
const setupUrls = await getPreferredIcloudSetupUrls(state);
|
||||
const explicitHost = normalizeIcloudHost(options?.hostPreference || options?.preferredHost || '');
|
||||
const setupUrls = explicitHost
|
||||
? (() => {
|
||||
const forcedSetupUrl = getIcloudSetupUrlForHost(explicitHost);
|
||||
return forcedSetupUrl ? [forcedSetupUrl] : [];
|
||||
})()
|
||||
: await getPreferredIcloudSetupUrls(state);
|
||||
|
||||
for (const setupUrl of setupUrls) {
|
||||
try {
|
||||
@@ -3392,17 +3521,19 @@ function getIcloudAliasLabel() {
|
||||
return `MultiPage ${dateStr}`;
|
||||
}
|
||||
|
||||
async function checkIcloudSession() {
|
||||
return withIcloudLoginHelp('检查 iCloud 会话', async () => {
|
||||
const { setupUrl } = await resolveIcloudPremiumMailService();
|
||||
async function checkIcloudSession(options = {}) {
|
||||
const actionLabel = String(options?.actionLabel || '检查 iCloud 会话').trim() || '检查 iCloud 会话';
|
||||
const { actionLabel: _ignoredActionLabel, ...resolveOptions } = options || {};
|
||||
return withIcloudLoginHelp(actionLabel, async () => {
|
||||
const { setupUrl } = await resolveIcloudPremiumMailService(resolveOptions);
|
||||
await addLog(`iCloud:会话校验通过(${new URL(setupUrl).host})`, 'ok');
|
||||
return { ok: true, setupUrl };
|
||||
});
|
||||
}
|
||||
|
||||
async function listIcloudAliases() {
|
||||
async function listIcloudAliases(options = {}) {
|
||||
return withIcloudLoginHelp('加载 iCloud 隐私邮箱列表', async () => {
|
||||
const { serviceUrl } = await resolveIcloudPremiumMailService();
|
||||
const { serviceUrl } = await resolveIcloudPremiumMailService(options);
|
||||
const response = await icloudRequest('GET', `${serviceUrl}/v2/hme/list`);
|
||||
const state = await getState();
|
||||
return normalizeIcloudAliasList(response, {
|
||||
@@ -3492,9 +3623,10 @@ async function deleteUsedIcloudAliases() {
|
||||
return { deleted, skipped };
|
||||
}
|
||||
|
||||
async function fetchIcloudHideMyEmail() {
|
||||
async function fetchIcloudHideMyEmail(options = {}) {
|
||||
return withIcloudLoginHelp('获取 iCloud 隐私邮箱', async () => {
|
||||
throwIfStopped();
|
||||
const generateNew = Boolean(options?.generateNew);
|
||||
await addLog('iCloud:正在校验当前浏览器登录状态...', 'info');
|
||||
|
||||
const { serviceUrl, setupUrl } = await resolveIcloudPremiumMailService();
|
||||
@@ -3507,12 +3639,16 @@ async function fetchIcloudHideMyEmail() {
|
||||
preservedEmails: getPreservedAliasMap(state),
|
||||
});
|
||||
|
||||
const reusableAlias = pickReusableIcloudAlias(existingAliases);
|
||||
if (reusableAlias) {
|
||||
await setEmailState(reusableAlias.email);
|
||||
await addLog(`iCloud:复用未使用别名 ${reusableAlias.email}`, 'ok');
|
||||
broadcastIcloudAliasesChanged({ reason: 'selected', email: reusableAlias.email });
|
||||
return reusableAlias.email;
|
||||
if (!generateNew) {
|
||||
const reusableAlias = pickReusableIcloudAlias(existingAliases);
|
||||
if (reusableAlias) {
|
||||
await setEmailState(reusableAlias.email);
|
||||
await addLog(`iCloud:复用未使用别名 ${reusableAlias.email}`, 'ok');
|
||||
broadcastIcloudAliasesChanged({ reason: 'selected', email: reusableAlias.email });
|
||||
return reusableAlias.email;
|
||||
}
|
||||
} else {
|
||||
await addLog('iCloud:已启用“始终创建新别名”,本次将跳过复用。', 'info');
|
||||
}
|
||||
|
||||
await addLog('iCloud:没有可复用别名,开始生成新的 Hide My Email 地址...', 'warn');
|
||||
@@ -3637,11 +3773,31 @@ function normalizeSub2ApiUrl(rawUrl) {
|
||||
return parsed.toString();
|
||||
}
|
||||
|
||||
function normalizeCodex2ApiUrl(rawUrl) {
|
||||
if (typeof navigationUtils !== 'undefined' && navigationUtils?.normalizeCodex2ApiUrl) {
|
||||
return navigationUtils.normalizeCodex2ApiUrl(rawUrl);
|
||||
}
|
||||
const input = (rawUrl || '').trim() || DEFAULT_CODEX2API_URL;
|
||||
const withProtocol = /^https?:\/\//i.test(input) ? input : `http://${input}`;
|
||||
const parsed = new URL(withProtocol);
|
||||
if (!parsed.pathname || parsed.pathname === '/' || parsed.pathname === '/admin') {
|
||||
parsed.pathname = '/admin/accounts';
|
||||
}
|
||||
parsed.hash = '';
|
||||
return parsed.toString();
|
||||
}
|
||||
|
||||
function getPanelMode(state = {}) {
|
||||
if (typeof navigationUtils !== 'undefined' && navigationUtils?.getPanelMode) {
|
||||
return navigationUtils.getPanelMode(state);
|
||||
}
|
||||
return state.panelMode === 'sub2api' ? 'sub2api' : 'cpa';
|
||||
if (state.panelMode === 'sub2api') {
|
||||
return 'sub2api';
|
||||
}
|
||||
if (state.panelMode === 'codex2api') {
|
||||
return 'codex2api';
|
||||
}
|
||||
return 'cpa';
|
||||
}
|
||||
|
||||
function getPanelModeLabel(modeOrState) {
|
||||
@@ -3649,7 +3805,13 @@ function getPanelModeLabel(modeOrState) {
|
||||
return navigationUtils.getPanelModeLabel(modeOrState);
|
||||
}
|
||||
const mode = typeof modeOrState === 'string' ? modeOrState : getPanelMode(modeOrState);
|
||||
return mode === 'sub2api' ? 'SUB2API' : 'CPA';
|
||||
if (mode === 'sub2api') {
|
||||
return 'SUB2API';
|
||||
}
|
||||
if (mode === 'codex2api') {
|
||||
return 'Codex2API';
|
||||
}
|
||||
return 'CPA';
|
||||
}
|
||||
|
||||
function isSignupPageHost(hostname = '') {
|
||||
@@ -3953,6 +4115,7 @@ function isRetryableContentScriptTransportError(error) {
|
||||
}
|
||||
|
||||
const navigationUtils = self.MultiPageBackgroundNavigationUtils?.createNavigationUtils({
|
||||
DEFAULT_CODEX2API_URL,
|
||||
DEFAULT_SUB2API_URL,
|
||||
normalizeLocalCpaStep9Mode,
|
||||
});
|
||||
@@ -4017,6 +4180,17 @@ function getTerminalSecurityBlockedTitle(error) {
|
||||
return 'Cloudflare 风控拦截';
|
||||
}
|
||||
|
||||
function isBrowserSwitchRequiredError(error) {
|
||||
return getErrorMessage(error).startsWith(BROWSER_SWITCH_REQUIRED_ERROR_PREFIX);
|
||||
}
|
||||
|
||||
function getBrowserSwitchRequiredMessage(error) {
|
||||
const message = getErrorMessage(error);
|
||||
return message.startsWith(BROWSER_SWITCH_REQUIRED_ERROR_PREFIX)
|
||||
? message.slice(BROWSER_SWITCH_REQUIRED_ERROR_PREFIX.length).trim()
|
||||
: message;
|
||||
}
|
||||
|
||||
function broadcastSecurityBlockedAlert(title = '流程已完全停止', message = CLOUDFLARE_SECURITY_BLOCK_USER_MESSAGE, alertText = '检测到 Cloudflare 风控,请暂停当前操作。') {
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'SECURITY_BLOCKED_ALERT',
|
||||
@@ -4040,6 +4214,13 @@ async function handleCloudflareSecurityBlocked(error) {
|
||||
return message;
|
||||
}
|
||||
|
||||
async function handleBrowserSwitchRequired(error) {
|
||||
const message = getBrowserSwitchRequiredMessage(error)
|
||||
|| '检测到第 10 步的特殊冲突状态,请更换浏览器后重新进行注册登录。';
|
||||
await requestStop({ logMessage: message });
|
||||
return message;
|
||||
}
|
||||
|
||||
function isVerificationMailPollingError(error) {
|
||||
if (typeof loggingStatus !== 'undefined' && loggingStatus?.isVerificationMailPollingError) {
|
||||
return loggingStatus.isVerificationMailPollingError(error);
|
||||
@@ -4128,6 +4309,8 @@ function getDownstreamStateResets(step) {
|
||||
sub2apiOAuthState: null,
|
||||
sub2apiGroupId: null,
|
||||
sub2apiDraftName: null,
|
||||
codex2apiSessionId: null,
|
||||
codex2apiOAuthState: null,
|
||||
flowStartTime: null,
|
||||
password: null,
|
||||
lastEmailTimestamp: null,
|
||||
@@ -4913,6 +5096,8 @@ async function handleStepData(step, payload) {
|
||||
if (payload.sub2apiGroupId !== undefined) updates.sub2apiGroupId = payload.sub2apiGroupId || null;
|
||||
if (payload.sub2apiDraftName !== undefined) updates.sub2apiDraftName = payload.sub2apiDraftName || null;
|
||||
if (payload.sub2apiProxyId !== undefined) updates.sub2apiProxyId = payload.sub2apiProxyId || null;
|
||||
if (payload.codex2apiSessionId !== undefined) updates.codex2apiSessionId = payload.codex2apiSessionId || null;
|
||||
if (payload.codex2apiOAuthState !== undefined) updates.codex2apiOAuthState = payload.codex2apiOAuthState || null;
|
||||
if (Object.keys(updates).length) {
|
||||
await setState(updates);
|
||||
}
|
||||
@@ -4997,7 +5182,12 @@ async function handleStepData(step, payload) {
|
||||
});
|
||||
}
|
||||
await finalizeIcloudAliasAfterSuccessfulFlow(latestState);
|
||||
if (shouldUseCustomRegistrationEmail(latestState) && latestState.email) {
|
||||
const shouldClearCustomPoolEmail = String(latestState?.emailGenerator || '').trim().toLowerCase() === (
|
||||
typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string'
|
||||
? CUSTOM_EMAIL_POOL_GENERATOR
|
||||
: 'custom-pool'
|
||||
);
|
||||
if ((shouldUseCustomRegistrationEmail(latestState) || shouldClearCustomPoolEmail) && latestState.email) {
|
||||
await setEmailStateSilently(null);
|
||||
}
|
||||
break;
|
||||
@@ -5343,6 +5533,10 @@ async function executeStep(step, options = {}) {
|
||||
await handleCloudflareSecurityBlocked(err);
|
||||
throw new Error(STOP_ERROR_MESSAGE);
|
||||
}
|
||||
if (isBrowserSwitchRequiredError(err)) {
|
||||
await handleBrowserSwitchRequired(err);
|
||||
throw new Error(STOP_ERROR_MESSAGE);
|
||||
}
|
||||
if (!(deferRetryableTransportError && doesStepUseCompletionSignal(step) && isRetryableContentScriptTransportError(err))) {
|
||||
await setStepStatus(step, 'failed');
|
||||
await addLog(`步骤 ${step} 失败:${err.message}`, 'error');
|
||||
@@ -5407,9 +5601,21 @@ async function executeStepAndWait(step, delayAfter = 2000) {
|
||||
}
|
||||
|
||||
function getEmailGeneratorLabel(generator) {
|
||||
const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string'
|
||||
? CUSTOM_EMAIL_POOL_GENERATOR
|
||||
: 'custom-pool';
|
||||
const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string'
|
||||
? GMAIL_ALIAS_GENERATOR
|
||||
: 'gmail-alias';
|
||||
if (generator === 'custom') {
|
||||
return '自定义邮箱';
|
||||
}
|
||||
if (generator === gmailAliasGenerator) {
|
||||
return 'Gmail +tag 邮箱';
|
||||
}
|
||||
if (generator === customEmailPoolGenerator) {
|
||||
return '自定义邮箱池';
|
||||
}
|
||||
if (generator === 'icloud') {
|
||||
return 'iCloud 隐私邮箱';
|
||||
}
|
||||
@@ -5440,6 +5646,7 @@ const mail2925SessionManager = self.MultiPageBackgroundMail2925Session?.createMa
|
||||
sleepWithStop,
|
||||
throwIfStopped,
|
||||
upsertMail2925AccountInList,
|
||||
waitForTabComplete,
|
||||
waitForTabUrlMatch,
|
||||
});
|
||||
|
||||
@@ -5509,11 +5716,13 @@ const generatedEmailHelpers = self.MultiPageGeneratedEmailHelpers?.createGenerat
|
||||
buildGeneratedAliasEmail,
|
||||
buildCloudflareTempEmailHeaders,
|
||||
CLOUDFLARE_TEMP_EMAIL_GENERATOR,
|
||||
CUSTOM_EMAIL_POOL_GENERATOR,
|
||||
DUCK_AUTOFILL_URL,
|
||||
fetch,
|
||||
fetchIcloudHideMyEmail,
|
||||
getCloudflareTempEmailAddressFromResponse,
|
||||
getCloudflareTempEmailConfig,
|
||||
getCustomEmailPoolEmail: getCustomEmailPoolEmailForRun,
|
||||
getState,
|
||||
ensureMail2925AccountForFlow,
|
||||
joinCloudflareTempEmailUrl,
|
||||
@@ -5756,6 +5965,34 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
||||
return currentState.email;
|
||||
}
|
||||
|
||||
if (isCustomMailProvider(currentState)) {
|
||||
const poolSize = getCustomMailProviderPool(currentState).length;
|
||||
if (poolSize > 0) {
|
||||
const queuedEmail = getCustomMailProviderPoolEmailForRun(currentState, targetRun);
|
||||
if (!queuedEmail) {
|
||||
throw new Error(`自定义邮箱号池第 ${targetRun} 个邮箱不存在,请检查号池数量是否与自动轮数一致。`);
|
||||
}
|
||||
await setEmailState(queuedEmail);
|
||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:自定义邮箱号池已就绪:${queuedEmail}(第 ${attemptRuns} 次尝试;第 4/8 步仍需手动输入验证码)===`, 'ok');
|
||||
return queuedEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCustomEmailPoolGenerator(currentState)) {
|
||||
const queuedEmail = getCustomEmailPoolEmailForRun(currentState, targetRun);
|
||||
if (!queuedEmail) {
|
||||
const poolSize = getCustomEmailPool(currentState).length;
|
||||
throw new Error(
|
||||
poolSize > 0
|
||||
? `自定义邮箱池第 ${targetRun} 个邮箱不存在,请检查邮箱池数量是否与自动轮数一致。`
|
||||
: '自定义邮箱池为空,请先至少填写 1 个邮箱。'
|
||||
);
|
||||
}
|
||||
await setEmailState(queuedEmail);
|
||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:自定义邮箱池已就绪:${queuedEmail}(第 ${attemptRuns} 次尝试)===`, 'ok');
|
||||
return queuedEmail;
|
||||
}
|
||||
|
||||
if (shouldUseCustomRegistrationEmail(currentState)) {
|
||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮已暂停:请先填写自定义注册邮箱,然后继续 ===`, 'warn');
|
||||
await broadcastAutoRunStatus('waiting_email', {
|
||||
@@ -5781,7 +6018,10 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
||||
if (attempt > 1) {
|
||||
await addLog(`${generatorLabel}:正在进行第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次自动获取重试...`, 'warn');
|
||||
}
|
||||
const generatedEmail = await fetchGeneratedEmail(currentState, { generateNew: true, generator });
|
||||
const generatedEmail = await fetchGeneratedEmail(currentState, {
|
||||
generateNew: generator !== 'icloud' || normalizeIcloudFetchMode(currentState.icloudFetchMode) === 'always_new',
|
||||
generator,
|
||||
});
|
||||
await addLog(
|
||||
`=== 目标 ${targetRun}/${totalRuns} 轮:${generatorLabel}已就绪:${generatedEmail}(第 ${attemptRuns} 次尝试,第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次获取)===`,
|
||||
'ok'
|
||||
@@ -5874,6 +6114,34 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
||||
return currentState.email;
|
||||
}
|
||||
|
||||
if (isCustomMailProvider(currentState)) {
|
||||
const poolSize = getCustomMailProviderPool(currentState).length;
|
||||
if (poolSize > 0) {
|
||||
const queuedEmail = getCustomMailProviderPoolEmailForRun(currentState, targetRun);
|
||||
if (!queuedEmail) {
|
||||
throw new Error(`自定义邮箱号池第 ${targetRun} 个邮箱不存在,请检查号池数量是否与自动轮数一致。`);
|
||||
}
|
||||
await setEmailState(queuedEmail);
|
||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:自定义邮箱号池已就绪:${queuedEmail}(第 ${attemptRuns} 次尝试;第 4/8 步仍需手动输入验证码)===`, 'ok');
|
||||
return queuedEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCustomEmailPoolGenerator(currentState)) {
|
||||
const queuedEmail = getCustomEmailPoolEmailForRun(currentState, targetRun);
|
||||
if (!queuedEmail) {
|
||||
const poolSize = getCustomEmailPool(currentState).length;
|
||||
throw new Error(
|
||||
poolSize > 0
|
||||
? `自定义邮箱池第 ${targetRun} 个邮箱不存在,请检查邮箱池数量是否与自动轮数一致。`
|
||||
: '自定义邮箱池为空,请先至少填写 1 个邮箱。'
|
||||
);
|
||||
}
|
||||
await setEmailState(queuedEmail);
|
||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:自定义邮箱池已就绪:${queuedEmail}(第 ${attemptRuns} 次尝试)===`, 'ok');
|
||||
return queuedEmail;
|
||||
}
|
||||
|
||||
if (shouldUseCustomRegistrationEmail(currentState)) {
|
||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮已暂停:请先填写自定义注册邮箱,然后继续 ===`, 'warn');
|
||||
await broadcastAutoRunStatus('waiting_email', {
|
||||
@@ -5899,7 +6167,10 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
||||
if (attempt > 1) {
|
||||
await addLog(`${generatorLabel}:正在进行第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次自动获取重试...`, 'warn');
|
||||
}
|
||||
const generatedEmail = await fetchGeneratedEmail(currentState, { generateNew: true, generator });
|
||||
const generatedEmail = await fetchGeneratedEmail(currentState, {
|
||||
generateNew: generator !== 'icloud' || normalizeIcloudFetchMode(currentState.icloudFetchMode) === 'always_new',
|
||||
generator,
|
||||
});
|
||||
await addLog(
|
||||
`=== 目标 ${targetRun}/${totalRuns} 轮:${generatorLabel}已就绪:${generatedEmail}(第 ${attemptRuns} 次尝试,第 ${attempt}/${EMAIL_FETCH_MAX_ATTEMPTS} 次获取)===`,
|
||||
'ok'
|
||||
@@ -5984,9 +6255,15 @@ async function runAutoSequenceFromStep(startStep, context = {}) {
|
||||
let restartFromStep1WithCurrentEmail = false;
|
||||
let step = Math.max(currentStartStep, 4);
|
||||
while (step <= LAST_STEP_ID) {
|
||||
const latestState = await getState();
|
||||
const currentStatus = latestState.stepStatuses?.[step] || 'pending';
|
||||
if (isStepDoneStatus(currentStatus)) {
|
||||
await addLog(`自动运行:步骤 ${step} 当前状态为 ${currentStatus},将直接继续后续流程。`, 'info');
|
||||
step += 1;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await executeStepAndWait(step, AUTO_STEP_DELAYS[step]);
|
||||
const latestState = await getState();
|
||||
step += 1;
|
||||
} catch (err) {
|
||||
if (isStopError(err)) {
|
||||
@@ -6176,6 +6453,7 @@ const panelBridge = self.MultiPageBackgroundPanelBridge?.createPanelBridge({
|
||||
closeConflictingTabsForSource,
|
||||
ensureContentScriptReadyOnTab,
|
||||
getPanelMode,
|
||||
normalizeCodex2ApiUrl,
|
||||
normalizeSub2ApiUrl,
|
||||
rememberSourceLastUrl,
|
||||
sendToContentScript,
|
||||
@@ -6196,6 +6474,7 @@ const signupFlowHelpers = self.MultiPageSignupFlowHelpers?.createSignupFlowHelpe
|
||||
isGeneratedAliasProvider,
|
||||
isReusableGeneratedAliasEmail,
|
||||
isSignupEmailVerificationPageUrl,
|
||||
isRetryableContentScriptTransportError,
|
||||
isHotmailProvider,
|
||||
isLuckmailProvider,
|
||||
isSignupPasswordPageUrl,
|
||||
@@ -6223,6 +6502,7 @@ const verificationFlowHelpers = self.MultiPageBackgroundVerificationFlow?.create
|
||||
getTabId,
|
||||
HOTMAIL_PROVIDER,
|
||||
isMail2925LimitReachedError,
|
||||
isRetryableContentScriptTransportError,
|
||||
isStopError,
|
||||
LUCKMAIL_PROVIDER,
|
||||
MAIL_2925_VERIFICATION_INTERVAL_MS,
|
||||
@@ -6231,6 +6511,7 @@ const verificationFlowHelpers = self.MultiPageBackgroundVerificationFlow?.create
|
||||
pollHotmailVerificationCode,
|
||||
pollLuckmailVerificationCode,
|
||||
sendToContentScript,
|
||||
sendToContentScriptResilient,
|
||||
sendToMailContentScriptResilient,
|
||||
setState,
|
||||
setStepStatus,
|
||||
@@ -6263,6 +6544,7 @@ const step2Executor = self.MultiPageBackgroundStep2?.createStep2Executor({
|
||||
chrome,
|
||||
completeStepFromBackground,
|
||||
ensureContentScriptReadyOnTab,
|
||||
ensureSignupAuthEntryPageReady,
|
||||
ensureSignupEntryPageReady,
|
||||
ensureSignupPostEmailPageReadyInTab,
|
||||
getTabId,
|
||||
@@ -6283,12 +6565,24 @@ const step3Executor = self.MultiPageBackgroundStep3?.createStep3Executor({
|
||||
setState,
|
||||
SIGNUP_PAGE_INJECT_FILES,
|
||||
});
|
||||
|
||||
async function ensureIcloudMailSessionForVerification(options = {}) {
|
||||
const flowState = options?.state || await getState().catch(() => ({}));
|
||||
const hostPreference = getConfiguredIcloudHostPreference(flowState)
|
||||
|| normalizeIcloudHost(flowState?.preferredIcloudHost);
|
||||
return checkIcloudSession({
|
||||
...(hostPreference ? { hostPreference } : {}),
|
||||
actionLabel: options?.actionLabel || '检查 iCloud 会话',
|
||||
});
|
||||
}
|
||||
|
||||
const step4Executor = self.MultiPageBackgroundStep4?.createStep4Executor({
|
||||
addLog,
|
||||
chrome,
|
||||
completeStepFromBackground,
|
||||
confirmCustomVerificationStepBypass: verificationFlowHelpers.confirmCustomVerificationStepBypass,
|
||||
ensureMail2925MailboxSession,
|
||||
ensureIcloudMailSession: ensureIcloudMailSessionForVerification,
|
||||
getMailConfig,
|
||||
getTabId,
|
||||
HOTMAIL_PROVIDER,
|
||||
@@ -6335,6 +6629,7 @@ const step8Executor = self.MultiPageBackgroundStep8?.createStep8Executor({
|
||||
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
|
||||
confirmCustomVerificationStepBypass: verificationFlowHelpers.confirmCustomVerificationStepBypass,
|
||||
ensureMail2925MailboxSession,
|
||||
ensureIcloudMailSession: ensureIcloudMailSessionForVerification,
|
||||
ensureStep8VerificationPageReady,
|
||||
getOAuthFlowRemainingMs,
|
||||
getOAuthFlowStepTimeoutMs,
|
||||
@@ -6366,6 +6661,7 @@ const step10Executor = self.MultiPageBackgroundStep10?.createStep10Executor({
|
||||
getTabId,
|
||||
isLocalhostOAuthCallbackUrl,
|
||||
isTabAlive,
|
||||
normalizeCodex2ApiUrl,
|
||||
normalizeSub2ApiUrl,
|
||||
rememberSourceLastUrl,
|
||||
reuseOrCreateTab,
|
||||
@@ -6432,6 +6728,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
|
||||
getPendingAutoRunTimerPlan,
|
||||
getSourceLabel,
|
||||
getState,
|
||||
getTabId,
|
||||
getStopRequested: () => stopRequested,
|
||||
handleCloudflareSecurityBlocked,
|
||||
handleAutoRunLoopUnhandledError,
|
||||
@@ -6443,6 +6740,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
|
||||
isLocalhostOAuthCallbackUrl,
|
||||
isLuckmailProvider,
|
||||
isStopError,
|
||||
isTabAlive,
|
||||
launchAutoRunTimerPlan,
|
||||
listIcloudAliases,
|
||||
listLuckmailPurchasesForManagement,
|
||||
@@ -6514,6 +6812,10 @@ async function ensureSignupEntryPageReady(step = 1) {
|
||||
return signupFlowHelpers.ensureSignupEntryPageReady(step);
|
||||
}
|
||||
|
||||
async function ensureSignupAuthEntryPageReady(step = 1) {
|
||||
return signupFlowHelpers.ensureSignupEntryPageReady(step);
|
||||
}
|
||||
|
||||
async function ensureSignupPasswordPageReadyInTab(tabId, step = 2, options = {}) {
|
||||
return signupFlowHelpers.ensureSignupPasswordPageReadyInTab(tabId, step, options);
|
||||
}
|
||||
@@ -6814,7 +7116,7 @@ async function runPreStep6CookieCleanup() {
|
||||
|
||||
async function refreshOAuthUrlBeforeStep6(state) {
|
||||
if (state?.contributionModeExpected && !state?.contributionMode) {
|
||||
throw new Error('步骤 7:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API 链路。请重新进入贡献模式后再点击自动。');
|
||||
throw new Error('步骤 7:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API / Codex2API 链路。请重新进入贡献模式后再点击自动。');
|
||||
}
|
||||
if (state?.contributionMode && contributionOAuthManager?.startContributionFlow) {
|
||||
await addLog('步骤 7:contributionMode=true,走公开贡献接口,正在申请 OAuth 登录地址...', 'info');
|
||||
@@ -6830,7 +7132,7 @@ async function refreshOAuthUrlBeforeStep6(state) {
|
||||
await handleStepData(1, { oauthUrl });
|
||||
return oauthUrl;
|
||||
}
|
||||
await addLog(`步骤 7:contributionMode=false,走普通 CPA / SUB2API 链路(当前面板:${getPanelModeLabel(state)}),正在刷新 OAuth 登录地址...`, 'info');
|
||||
await addLog(`步骤 7:contributionMode=false,走普通 CPA / SUB2API / Codex2API 链路(当前面板:${getPanelModeLabel(state)}),正在刷新 OAuth 登录地址...`, 'info');
|
||||
console.log(LOG_PREFIX, '[refreshOAuthUrlBeforeStep6] requesting fresh OAuth directly from panel');
|
||||
const refreshResult = await requestOAuthUrlFromPanel(state, { logLabel: '步骤 7' });
|
||||
await handleStepData(1, refreshResult);
|
||||
@@ -6868,7 +7170,7 @@ async function startOAuthFlowTimeoutWindow(options = {}) {
|
||||
oauthFlowDeadlineAt: deadlineAt,
|
||||
oauthFlowDeadlineSourceUrl: normalizeOAuthFlowSourceUrl(options.oauthUrl),
|
||||
});
|
||||
await addLog(`步骤 ${step}:已拿到新的 OAuth 登录地址,开始 6 分钟倒计时。`, 'info');
|
||||
await addLog(`步骤 ${step}:已拿到新的 OAuth 登录地址,开始 ${Math.round(OAUTH_FLOW_TIMEOUT_MS / 60000)} 分钟倒计时。`, 'info');
|
||||
return deadlineAt;
|
||||
}
|
||||
|
||||
@@ -7218,7 +7520,6 @@ async function getStep8PageState(tabId, responseTimeoutMs = 1500) {
|
||||
async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS) {
|
||||
const start = Date.now();
|
||||
let recovered = false;
|
||||
let retryRecovered = false;
|
||||
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
throwIfStopped();
|
||||
@@ -7229,7 +7530,6 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
|
||||
if (pageState?.addPhonePage || pageState?.phoneVerificationPage) {
|
||||
await phoneVerificationHelpers.completePhoneVerificationFlow(tabId, pageState);
|
||||
recovered = false;
|
||||
retryRecovered = false;
|
||||
await sleepWithStop(250);
|
||||
continue;
|
||||
}
|
||||
@@ -7237,20 +7537,9 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
|
||||
throw new Error('步骤 9:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。');
|
||||
}
|
||||
if (pageState?.retryPage) {
|
||||
await recoverAuthRetryPageOnTab(tabId, {
|
||||
flow: 'auth',
|
||||
logLabel: '步骤 9:检测到认证页重试页,正在点击“重试”恢复',
|
||||
step: 8,
|
||||
timeoutMs: Math.max(1000, Math.min(12000, timeoutMs)),
|
||||
});
|
||||
retryRecovered = true;
|
||||
await sleepWithStop(250);
|
||||
continue;
|
||||
throw new Error(`步骤 9:当前认证页已进入重试页,当前流程将直接报错。URL: ${pageState.url || 'unknown'}`);
|
||||
}
|
||||
if (pageState?.consentReady) {
|
||||
if (retryRecovered) {
|
||||
await addLog('步骤 9:认证页重试页已恢复,准备重新定位“继续”按钮...', 'info');
|
||||
}
|
||||
return pageState;
|
||||
}
|
||||
if (pageState === null && !recovered) {
|
||||
@@ -7415,18 +7704,7 @@ async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLI
|
||||
throw new Error('步骤 9:点击“继续”后页面跳到了手机号页面,当前流程无法继续自动授权。');
|
||||
}
|
||||
if (pageState?.retryPage) {
|
||||
await recoverAuthRetryPageOnTab(tabId, {
|
||||
flow: 'auth',
|
||||
logLabel: '步骤 9:点击“继续”后进入重试页,正在点击“重试”恢复',
|
||||
step: 8,
|
||||
timeoutMs: Math.max(1000, Math.min(12000, timeoutMs)),
|
||||
});
|
||||
return {
|
||||
progressed: false,
|
||||
reason: 'retry_page_recovered',
|
||||
restartCurrentStep: true,
|
||||
url: pageState.url || baselineUrl || '',
|
||||
};
|
||||
throw new Error(`步骤 9:点击“继续”后页面进入认证页重试页,当前流程将直接报错。URL: ${pageState.url || baselineUrl || 'unknown'}`);
|
||||
}
|
||||
if (pageState === null) {
|
||||
if (!recovered) {
|
||||
@@ -7460,8 +7738,6 @@ function getStep8EffectLabel(effect) {
|
||||
switch (effect?.reason) {
|
||||
case 'url_changed':
|
||||
return `URL 已变化:${effect.url}`;
|
||||
case 'retry_page_recovered':
|
||||
return '页面进入重试页并已恢复,需要重新执行当前步骤';
|
||||
case 'page_reloading':
|
||||
return '页面正在跳转或重载';
|
||||
case 'left_consent_page':
|
||||
@@ -7578,7 +7854,7 @@ async function executeContributionStep10(state) {
|
||||
|
||||
async function executeStep10(state) {
|
||||
if (state?.contributionModeExpected && !state?.contributionMode) {
|
||||
throw new Error('步骤 10:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API 提交。请重新进入贡献模式后再点击自动。');
|
||||
throw new Error('步骤 10:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API / Codex2API 提交。请重新进入贡献模式后再点击自动。');
|
||||
}
|
||||
if (state?.contributionMode) {
|
||||
return executeContributionStep10(state);
|
||||
|
||||
Reference in New Issue
Block a user