feat: implement multi-step background process for user registration

- Add step 1: Open ChatGPT homepage and prepare for signup.
- Add step 2: Handle email submission and ensure the signup entry page is ready.
- Add step 3: Fill in the password using either a custom or generated password.
- Add step 4: Confirm the registration verification page and handle email verification.
- Add step 5: Generate random user details and handle onboarding redirection.
- Add step 6: Manage OAuth login process and handle login verification.
- Add step 7: Retrieve verification code from email and manage retries.
- Add step 8: Handle consent page interactions and manage callback URLs.
- Add step 9: Submit callback information to CPA or SUB2API based on user settings.
- Implement tests to ensure all steps are correctly imported and functional.
This commit is contained in:
QLHazyCoder
2026-04-16 23:38:43 +08:00
parent f0c3dcda62
commit 4f12d67d9f
12 changed files with 1212 additions and 659 deletions
+21
View File
@@ -0,0 +1,21 @@
(function attachBackgroundStep1(root, factory) {
root.MultiPageBackgroundStep1 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep1Module() {
function createStep1Executor(deps = {}) {
const {
addLog,
completeStepFromBackground,
ensureSignupEntryPageReady,
} = deps;
async function executeStep1() {
await addLog('步骤 1:正在打开 ChatGPT 官网...');
await ensureSignupEntryPageReady(1);
await completeStepFromBackground(1, {});
}
return { executeStep1 };
}
return { createStep1Executor };
});
+66
View File
@@ -0,0 +1,66 @@
(function attachBackgroundStep2(root, factory) {
root.MultiPageBackgroundStep2 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep2Module() {
function createStep2Executor(deps = {}) {
const {
addLog,
chrome,
completeStepFromBackground,
ensureContentScriptReadyOnTab,
ensureSignupEntryPageReady,
ensureSignupPasswordPageReadyInTab,
getTabId,
isTabAlive,
resolveSignupEmailForFlow,
sendToContentScriptResilient,
SIGNUP_PAGE_INJECT_FILES,
} = deps;
async function executeStep2(state) {
const resolvedEmail = await resolveSignupEmailForFlow(state);
let signupTabId = await getTabId('signup-page');
if (!signupTabId || !(await isTabAlive('signup-page'))) {
await addLog('步骤 2:未发现可用的注册页标签,正在重新打开 ChatGPT 官网...', 'warn');
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
} else {
await chrome.tabs.update(signupTabId, { active: true });
await ensureContentScriptReadyOnTab('signup-page', signupTabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: '步骤 2:注册入口页内容脚本未就绪,正在等待页面恢复...',
});
}
const step2Result = await sendToContentScriptResilient('signup-page', {
type: 'EXECUTE_STEP',
step: 2,
source: 'background',
payload: { email: resolvedEmail },
}, {
timeoutMs: 20000,
retryDelayMs: 700,
logMessage: '步骤 2:官网注册入口正在切换,等待页面恢复后继续输入邮箱...',
});
if (step2Result?.error) {
throw new Error(step2Result.error);
}
if (!step2Result?.alreadyOnPasswordPage) {
await addLog(`步骤 2:邮箱 ${resolvedEmail} 已提交,正在等待进入密码页...`);
}
await ensureSignupPasswordPageReadyInTab(signupTabId, 2, {
skipUrlWait: Boolean(step2Result?.alreadyOnPasswordPage),
});
await completeStepFromBackground(2, {});
}
return { executeStep2 };
}
return { createStep2Executor };
});
+60
View File
@@ -0,0 +1,60 @@
(function attachBackgroundStep3(root, factory) {
root.MultiPageBackgroundStep3 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep3Module() {
function createStep3Executor(deps = {}) {
const {
addLog,
chrome,
ensureContentScriptReadyOnTab,
generatePassword,
getTabId,
isTabAlive,
sendToContentScript,
setPasswordState,
setState,
SIGNUP_PAGE_INJECT_FILES,
} = deps;
async function executeStep3(state) {
const resolvedEmail = state.email;
if (!resolvedEmail) {
throw new Error('缺少邮箱地址,请先完成步骤 2。');
}
const signupTabId = await getTabId('signup-page');
if (!signupTabId || !(await isTabAlive('signup-page'))) {
throw new Error('认证页面标签页已关闭,请先重新完成步骤 2。');
}
const password = state.customPassword || generatePassword();
await setPasswordState(password);
const accounts = state.accounts || [];
accounts.push({ email: resolvedEmail, password, createdAt: new Date().toISOString() });
await setState({ accounts });
await chrome.tabs.update(signupTabId, { active: true });
await ensureContentScriptReadyOnTab('signup-page', signupTabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: '步骤 3:密码页内容脚本未就绪,正在等待页面恢复...',
});
await addLog(
`步骤 3:正在填写密码,邮箱为 ${resolvedEmail},密码为${state.customPassword ? '自定义' : '自动生成'}${password.length} 位)`
);
await sendToContentScript('signup-page', {
type: 'EXECUTE_STEP',
step: 3,
source: 'background',
payload: { email: resolvedEmail, password },
});
}
return { executeStep3 };
}
return { createStep3Executor };
});
+100
View File
@@ -0,0 +1,100 @@
(function attachBackgroundStep4(root, factory) {
root.MultiPageBackgroundStep4 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep4Module() {
function createStep4Executor(deps = {}) {
const {
addLog,
chrome,
completeStepFromBackground,
confirmCustomVerificationStepBypass,
getMailConfig,
getTabId,
HOTMAIL_PROVIDER,
isTabAlive,
LUCKMAIL_PROVIDER,
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
resolveVerificationStep,
reuseOrCreateTab,
sendToContentScriptResilient,
shouldUseCustomRegistrationEmail,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
throwIfStopped,
} = deps;
async function executeStep4(state) {
const mail = getMailConfig(state);
if (mail.error) throw new Error(mail.error);
const stepStartedAt = Date.now();
const signupTabId = await getTabId('signup-page');
if (!signupTabId) {
throw new Error('认证页面标签页已关闭,无法继续步骤 4。');
}
await chrome.tabs.update(signupTabId, { active: true });
throwIfStopped();
await addLog('步骤 4:正在确认注册验证码页面是否就绪,必要时自动恢复密码页超时报错...');
const prepareResult = await sendToContentScriptResilient(
'signup-page',
{
type: 'PREPARE_SIGNUP_VERIFICATION',
step: 4,
source: 'background',
payload: { password: state.password || state.customPassword || '' },
},
{
timeoutMs: 30000,
retryDelayMs: 700,
logMessage: '步骤 4:认证页正在切换,等待页面重新就绪后继续检测...',
}
);
if (prepareResult && prepareResult.error) {
throw new Error(prepareResult.error);
}
if (prepareResult?.alreadyVerified) {
await completeStepFromBackground(4, {});
return;
}
if (shouldUseCustomRegistrationEmail(state)) {
await confirmCustomVerificationStepBypass(4);
return;
}
throwIfStopped();
if (mail.provider === HOTMAIL_PROVIDER || mail.provider === LUCKMAIL_PROVIDER || mail.provider === CLOUDFLARE_TEMP_EMAIL_PROVIDER) {
await addLog(`步骤 4:正在通过 ${mail.label} 轮询验证码...`);
} else {
await addLog(`步骤 4:正在打开${mail.label}...`);
const alive = await isTabAlive(mail.source);
if (alive) {
if (mail.navigateOnReuse) {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
} else {
const tabId = await getTabId(mail.source);
await chrome.tabs.update(tabId, { active: true });
}
} else {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
}
}
await resolveVerificationStep(4, state, mail, {
filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : stepStartedAt,
requestFreshCodeFirst: mail.provider === HOTMAIL_PROVIDER ? false : true,
resendIntervalMs: mail.provider === HOTMAIL_PROVIDER ? 0 : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
});
}
return { executeStep4 };
}
return { createStep4Executor };
});
+69
View File
@@ -0,0 +1,69 @@
(function attachBackgroundStep5(root, factory) {
root.MultiPageBackgroundStep5 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep5Module() {
function createStep5Executor(deps = {}) {
const {
addLog,
generateRandomBirthday,
generateRandomName,
getTabId,
handleChatgptOnboardingSkip,
isRetryableContentScriptTransportError,
LOG_PREFIX,
sendToContentScript,
waitForStep5ChatgptRedirect,
} = deps;
async function executeStep5() {
const { firstName, lastName } = generateRandomName();
const { year, month, day } = generateRandomBirthday();
await addLog(`步骤 5:已生成姓名 ${firstName} ${lastName},生日 ${year}-${month}-${day}`);
let step5Result = null;
let step5TransportError = null;
try {
step5Result = await sendToContentScript('signup-page', {
type: 'EXECUTE_STEP',
step: 5,
source: 'background',
payload: { firstName, lastName, year, month, day },
});
} catch (err) {
if (isRetryableContentScriptTransportError(err)) {
step5TransportError = err;
console.log(LOG_PREFIX, '步骤 5:内容脚本通信中断,正在检查是否跳转到 ChatGPT 引导页...', err?.message);
} else {
throw err;
}
}
if (step5Result?.chatgptOnboarding) {
await addLog('步骤 5:检测到 ChatGPT 引导页跳转,正在处理引导页跳过...');
await handleChatgptOnboardingSkip();
return;
}
if (step5Result?.chatgptHome) {
await addLog('步骤 5:检测到已进入 ChatGPT 页面,注册成功。', 'ok');
return;
}
if (step5TransportError) {
const signupTabId = await getTabId('signup-page');
const redirectedTab = await waitForStep5ChatgptRedirect(signupTabId);
if (redirectedTab) {
await addLog('步骤 5:内容脚本因页面跳转到 ChatGPT 而断开,正在处理引导页跳过...');
await handleChatgptOnboardingSkip(redirectedTab.id);
return;
}
throw step5TransportError;
}
}
return { executeStep5 };
}
return { createStep5Executor };
});
+93
View File
@@ -0,0 +1,93 @@
(function attachBackgroundStep6(root, factory) {
root.MultiPageBackgroundStep6 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() {
function createStep6Executor(deps = {}) {
const {
addLog,
completeStepFromBackground,
getLoginAuthStateLabel,
getState,
isStep6RecoverableResult,
isStep6SuccessResult,
refreshOAuthUrlBeforeStep6,
reuseOrCreateTab,
runPreStep6CookieCleanup,
sendToContentScriptResilient,
shouldSkipLoginVerificationForCpaCallback,
skipLoginVerificationStepsForCpaCallback,
throwIfStopped,
} = deps;
async function executeStep6(state) {
if (shouldSkipLoginVerificationForCpaCallback(state)) {
await skipLoginVerificationStepsForCpaCallback();
return;
}
if (!state.email) {
throw new Error('缺少邮箱地址,请先完成步骤 3。');
}
await runPreStep6CookieCleanup();
let attempt = 0;
while (true) {
throwIfStopped();
attempt += 1;
const currentState = attempt === 1 ? state : await getState();
const password = currentState.password || currentState.customPassword || '';
const oauthUrl = await refreshOAuthUrlBeforeStep6(currentState);
if (attempt === 1) {
await addLog('步骤 6:正在打开最新 OAuth 链接并登录...');
} else {
await addLog(`步骤 6:上一轮登录未进入验证码页,正在重新发起第 ${attempt} 轮登录尝试...`, 'warn');
}
await reuseOrCreateTab('signup-page', oauthUrl);
const result = await sendToContentScriptResilient(
'signup-page',
{
type: 'EXECUTE_STEP',
step: 6,
source: 'background',
payload: {
email: currentState.email,
password,
},
},
{
timeoutMs: 180000,
retryDelayMs: 700,
logMessage: '步骤 6:认证页正在切换,等待页面重新就绪后继续登录...',
}
);
if (result?.error) {
throw new Error(result.error);
}
if (isStep6SuccessResult(result)) {
await completeStepFromBackground(6, {
loginVerificationRequestedAt: result.loginVerificationRequestedAt || null,
});
return;
}
if (isStep6RecoverableResult(result)) {
const reasonMessage = result.message
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 6。`;
await addLog(`步骤 6${reasonMessage}`, 'warn');
continue;
}
throw new Error('步骤 6:认证页未返回可识别的登录结果。');
}
}
return { executeStep6 };
}
return { createStep6Executor };
});
+146
View File
@@ -0,0 +1,146 @@
(function attachBackgroundStep7(root, factory) {
root.MultiPageBackgroundStep7 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep7Module() {
function createStep7Executor(deps = {}) {
const {
addLog,
chrome,
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
confirmCustomVerificationStepBypass,
ensureStep7VerificationPageReady,
executeStep6,
getMailConfig,
getState,
getTabId,
HOTMAIL_PROVIDER,
isTabAlive,
isVerificationMailPollingError,
LUCKMAIL_PROVIDER,
resolveVerificationStep,
reuseOrCreateTab,
setState,
setStepStatus,
shouldSkipLoginVerificationForCpaCallback,
shouldUseCustomRegistrationEmail,
sleepWithStop,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS,
throwIfStopped,
} = deps;
async function runStep7Attempt(state) {
const mail = getMailConfig(state);
if (mail.error) throw new Error(mail.error);
const stepStartedAt = Date.now();
const authTabId = await getTabId('signup-page');
if (authTabId) {
await chrome.tabs.update(authTabId, { active: true });
} else {
if (!state.oauthUrl) {
throw new Error('缺少登录用 OAuth 链接,请先完成步骤 6。');
}
await reuseOrCreateTab('signup-page', state.oauthUrl);
}
throwIfStopped();
await ensureStep7VerificationPageReady();
await addLog('步骤 7:登录验证码页面已就绪,开始获取验证码。', 'info');
if (shouldUseCustomRegistrationEmail(state)) {
await confirmCustomVerificationStepBypass(7);
return;
}
throwIfStopped();
if (mail.provider === HOTMAIL_PROVIDER || mail.provider === LUCKMAIL_PROVIDER || mail.provider === CLOUDFLARE_TEMP_EMAIL_PROVIDER) {
await addLog(`步骤 7:正在通过 ${mail.label} 轮询验证码...`);
} else {
await addLog(`步骤 7:正在打开${mail.label}...`);
const alive = await isTabAlive(mail.source);
if (alive) {
if (mail.navigateOnReuse) {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
} else {
const tabId = await getTabId(mail.source);
await chrome.tabs.update(tabId, { active: true });
}
} else {
await reuseOrCreateTab(mail.source, mail.url, {
inject: mail.inject,
injectSource: mail.injectSource,
});
}
}
await resolveVerificationStep(7, state, mail, {
filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : Math.max(0, stepStartedAt - 60000),
requestFreshCodeFirst: false,
resendIntervalMs: mail.provider === HOTMAIL_PROVIDER ? 0 : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
});
}
async function rerunStep6ForStep7Recovery() {
const currentState = await getState();
await addLog('步骤 7:正在回到步骤 6,重新发起登录验证码流程...', 'warn');
await executeStep6(currentState);
await sleepWithStop(3000);
}
async function executeStep7(state) {
if (shouldSkipLoginVerificationForCpaCallback(state)) {
await setState({
lastLoginCode: null,
loginVerificationRequestedAt: null,
});
await setStepStatus(7, 'skipped');
await addLog('步骤 7:当前已选择“第六步回调”,本轮无需获取登录验证码。', 'warn');
return;
}
let currentState = state;
let mailPollingAttempt = 1;
let lastMailPollingError = null;
while (true) {
try {
await runStep7Attempt(currentState);
return;
} catch (err) {
if (!isVerificationMailPollingError(err)) {
throw err;
}
lastMailPollingError = err;
if (mailPollingAttempt >= STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS) {
break;
}
mailPollingAttempt += 1;
await addLog(
`步骤 7:检测到邮箱轮询类失败,准备从步骤 6 重新开始(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS}...`,
'warn'
);
await rerunStep6ForStep7Recovery();
currentState = await getState();
}
}
if (lastMailPollingError) {
throw new Error(
`步骤 7:登录验证码流程在 ${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS} 轮邮箱轮询恢复后仍未成功。最后一次原因:${lastMailPollingError.message}`
);
}
throw new Error('步骤 7:登录验证码流程未成功完成。');
}
return { executeStep7 };
}
return { createStep7Executor };
});
+174
View File
@@ -0,0 +1,174 @@
(function attachBackgroundStep8(root, factory) {
root.MultiPageBackgroundStep8 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep8Module() {
function createStep8Executor(deps = {}) {
const {
addLog,
chrome,
cleanupStep8NavigationListeners,
clickWithDebugger,
completeStepFromBackground,
ensureStep8SignupPageReady,
getStep8CallbackUrlFromNavigation,
getStep8CallbackUrlFromTabUpdate,
getStep8EffectLabel,
getTabId,
isTabAlive,
prepareStep8DebuggerClick,
reloadStep8ConsentPage,
reuseOrCreateTab,
sleepWithStop,
STEP8_CLICK_RETRY_DELAY_MS,
STEP8_MAX_ROUNDS,
STEP8_READY_WAIT_TIMEOUT_MS,
STEP8_STRATEGIES,
throwIfStep8SettledOrStopped,
triggerStep8ContentStrategy,
waitForStep8ClickEffect,
waitForStep8Ready,
setWebNavListener,
setWebNavCommittedListener,
setStep8PendingReject,
setStep8TabUpdatedListener,
} = deps;
async function executeStep8(state) {
if (!state.oauthUrl) {
throw new Error('缺少登录用 OAuth 链接,请先完成步骤 6。');
}
await addLog('步骤 8:正在监听 localhost 回调地址...');
return new Promise((resolve, reject) => {
let resolved = false;
let signupTabId = null;
const cleanupListener = () => {
cleanupStep8NavigationListeners();
setStep8PendingReject(null);
};
const rejectStep8 = (error) => {
if (resolved) return;
resolved = true;
clearTimeout(timeout);
cleanupListener();
reject(error);
};
const finalizeStep8Callback = (callbackUrl) => {
if (resolved || !callbackUrl) return;
resolved = true;
cleanupListener();
clearTimeout(timeout);
addLog(`步骤 8:已捕获 localhost 地址:${callbackUrl}`, 'ok').then(() => {
return completeStepFromBackground(8, { localhostUrl: callbackUrl });
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
};
const timeout = setTimeout(() => {
rejectStep8(new Error('120 秒内未捕获到 localhost 回调跳转,步骤 8 的点击可能被拦截了。'));
}, 120000);
setStep8PendingReject((error) => {
rejectStep8(error);
});
setWebNavListener((details) => {
const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
finalizeStep8Callback(callbackUrl);
});
setWebNavCommittedListener((details) => {
const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
finalizeStep8Callback(callbackUrl);
});
setStep8TabUpdatedListener((tabId, changeInfo, tab) => {
const callbackUrl = getStep8CallbackUrlFromTabUpdate(tabId, changeInfo, tab, signupTabId);
finalizeStep8Callback(callbackUrl);
});
(async () => {
try {
throwIfStep8SettledOrStopped(resolved);
signupTabId = await getTabId('signup-page');
throwIfStep8SettledOrStopped(resolved);
if (signupTabId && await isTabAlive('signup-page')) {
await chrome.tabs.update(signupTabId, { active: true });
await addLog('步骤 8:已切回认证页,正在准备调试器点击...');
} else {
signupTabId = await reuseOrCreateTab('signup-page', state.oauthUrl);
await addLog('步骤 8:已重新打开认证页,正在准备调试器点击...');
}
throwIfStep8SettledOrStopped(resolved);
chrome.webNavigation.onBeforeNavigate.addListener(deps.getWebNavListener());
chrome.webNavigation.onCommitted.addListener(deps.getWebNavCommittedListener());
chrome.tabs.onUpdated.addListener(deps.getStep8TabUpdatedListener());
await ensureStep8SignupPageReady(signupTabId, {
timeoutMs: 15000,
logMessage: '步骤 8:认证页内容脚本尚未就绪,正在等待页面恢复...',
});
for (let round = 1; round <= STEP8_MAX_ROUNDS && !resolved; round++) {
throwIfStep8SettledOrStopped(resolved);
const pageState = await waitForStep8Ready(signupTabId, STEP8_READY_WAIT_TIMEOUT_MS);
if (!pageState?.consentReady) {
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
continue;
}
const strategy = STEP8_STRATEGIES[Math.min(round - 1, STEP8_STRATEGIES.length - 1)];
await addLog(`步骤 8:第 ${round}/${STEP8_MAX_ROUNDS} 轮尝试点击“继续”(${strategy.label}...`);
if (strategy.mode === 'debugger') {
const clickTarget = await prepareStep8DebuggerClick(signupTabId);
throwIfStep8SettledOrStopped(resolved);
await clickWithDebugger(signupTabId, clickTarget?.rect);
} else {
await triggerStep8ContentStrategy(signupTabId, strategy.strategy);
}
if (resolved) {
return;
}
const effect = await waitForStep8ClickEffect(signupTabId, pageState.url);
if (resolved) {
return;
}
if (effect.progressed) {
await addLog(`步骤 8:检测到本次点击已生效,${getStep8EffectLabel(effect)},继续等待 localhost 回调...`, 'info');
break;
}
if (round >= STEP8_MAX_ROUNDS) {
throw new Error(`步骤 8:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
}
await addLog(`步骤 8${strategy.label} 本轮点击后页面无反应,正在刷新认证页后重试(下一轮 ${round + 1}/${STEP8_MAX_ROUNDS}...`, 'warn');
await reloadStep8ConsentPage(signupTabId);
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
}
} catch (err) {
rejectStep8(err);
}
})();
});
}
return { executeStep8 };
}
return { createStep8Executor };
});
+167
View File
@@ -0,0 +1,167 @@
(function attachBackgroundStep9(root, factory) {
root.MultiPageBackgroundStep9 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep9Module() {
function createStep9Executor(deps = {}) {
const {
addLog,
chrome,
closeConflictingTabsForSource,
completeStepFromBackground,
ensureContentScriptReadyOnTab,
getPanelMode,
getTabId,
isLocalhostOAuthCallbackUrl,
isTabAlive,
normalizeSub2ApiUrl,
rememberSourceLastUrl,
reuseOrCreateTab,
sendToContentScript,
sendToContentScriptResilient,
shouldBypassStep9ForLocalCpa,
SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
} = deps;
async function executeStep9(state) {
if (getPanelMode(state) === 'sub2api') {
return executeSub2ApiStep9(state);
}
return executeCpaStep9(state);
}
async function executeCpaStep9(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 8 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 8。');
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 8。');
}
if (!state.vpsUrl) {
throw new Error('尚未填写 CPA 地址,请先在侧边栏输入。');
}
if (shouldBypassStep9ForLocalCpa(state)) {
await addLog('步骤 9:检测到本地 CPA,且当前策略为“跳过第9步”,本轮不再重复提交回调地址。', 'info');
await completeStepFromBackground(9, {
localhostUrl: state.localhostUrl,
verifiedStatus: 'local-auto',
});
return;
}
await addLog('步骤 9:正在打开 CPA 面板...');
const injectFiles = ['content/activation-utils.js', 'content/utils.js', 'content/vps-panel.js'];
let tabId = await getTabId('vps-panel');
const alive = tabId && await isTabAlive('vps-panel');
if (!alive) {
tabId = await reuseOrCreateTab('vps-panel', state.vpsUrl, {
inject: injectFiles,
reloadIfSameUrl: true,
});
} else {
await closeConflictingTabsForSource('vps-panel', state.vpsUrl, { excludeTabIds: [tabId] });
await chrome.tabs.update(tabId, { active: true });
await rememberSourceLastUrl('vps-panel', state.vpsUrl);
}
await ensureContentScriptReadyOnTab('vps-panel', tabId, {
inject: injectFiles,
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: '步骤 9:CPA 面板仍在加载,正在重试连接...',
});
await addLog('步骤 9:正在填写回调地址...');
const result = await sendToContentScriptResilient('vps-panel', {
type: 'EXECUTE_STEP',
step: 9,
source: 'background',
payload: { localhostUrl: state.localhostUrl, vpsPassword: state.vpsPassword },
}, {
timeoutMs: 30000,
retryDelayMs: 700,
logMessage: '步骤 9:CPA 面板通信未就绪,正在等待页面恢复...',
});
if (result?.error) {
throw new Error(result.error);
}
}
async function executeSub2ApiStep9(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 8 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 8。');
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 8。');
}
if (!state.sub2apiSessionId) {
throw new Error('缺少 SUB2API 会话信息,请重新执行步骤 1。');
}
if (!state.sub2apiEmail) {
throw new Error('尚未配置 SUB2API 登录邮箱,请先在侧边栏填写。');
}
if (!state.sub2apiPassword) {
throw new Error('尚未配置 SUB2API 登录密码,请先在侧边栏填写。');
}
const sub2apiUrl = normalizeSub2ApiUrl(state.sub2apiUrl);
const injectFiles = ['content/utils.js', 'content/sub2api-panel.js'];
await addLog('步骤 9:正在打开 SUB2API 后台...');
let tabId = await getTabId('sub2api-panel');
const alive = tabId && await isTabAlive('sub2api-panel');
if (!alive) {
tabId = await reuseOrCreateTab('sub2api-panel', sub2apiUrl, {
inject: injectFiles,
injectSource: 'sub2api-panel',
reloadIfSameUrl: true,
});
} else {
await closeConflictingTabsForSource('sub2api-panel', sub2apiUrl, { excludeTabIds: [tabId] });
await chrome.tabs.update(tabId, { active: true });
await rememberSourceLastUrl('sub2api-panel', sub2apiUrl);
}
await ensureContentScriptReadyOnTab('sub2api-panel', tabId, {
inject: injectFiles,
injectSource: 'sub2api-panel',
});
await addLog('步骤 9:正在向 SUB2API 提交回调并创建账号...');
const result = await sendToContentScript('sub2api-panel', {
type: 'EXECUTE_STEP',
step: 9,
source: 'background',
payload: {
localhostUrl: state.localhostUrl,
sub2apiUrl,
sub2apiEmail: state.sub2apiEmail,
sub2apiPassword: state.sub2apiPassword,
sub2apiGroupName: state.sub2apiGroupName,
sub2apiSessionId: state.sub2apiSessionId,
sub2apiOAuthState: state.sub2apiOAuthState,
sub2apiGroupId: state.sub2apiGroupId,
sub2apiDraftName: state.sub2apiDraftName,
},
}, {
responseTimeoutMs: SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
});
if (result?.error) {
throw new Error(result.error);
}
}
return {
executeCpaStep9,
executeStep9,
executeSub2ApiStep9,
};
}
return { createStep9Executor };
});