抽取cookie清理逻辑为新的第六步

This commit is contained in:
QLHazyCoder
2026-04-17 15:56:19 +08:00
parent b387ebfa91
commit cbe9b1fabf
30 changed files with 630 additions and 603 deletions
+19
View File
@@ -0,0 +1,19 @@
(function attachBackgroundStep6(root, factory) {
root.MultiPageBackgroundStep6 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() {
function createStep6Executor(deps = {}) {
const {
completeStepFromBackground,
runPreStep6CookieCleanup,
} = deps;
async function executeStep6() {
await runPreStep6CookieCleanup();
await completeStepFromBackground(6);
}
return { executeStep6 };
}
return { createStep6Executor };
});
+27 -27
View File
@@ -1,7 +1,7 @@
(function attachBackgroundStep8(root, factory) {
root.MultiPageBackgroundStep8 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep8Module() {
function createStep8Executor(deps = {}) {
(function attachBackgroundStep9(root, factory) {
root.MultiPageBackgroundStep9 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep9Module() {
function createStep9Executor(deps = {}) {
const {
addLog,
chrome,
@@ -32,12 +32,12 @@
setStep8TabUpdatedListener,
} = deps;
async function executeStep8(state) {
async function executeStep9(state) {
if (!state.oauthUrl) {
throw new Error('缺少登录用 OAuth 链接,请先完成步骤 6。');
throw new Error('缺少登录用 OAuth 链接,请先完成步骤 7。');
}
await addLog('步骤 8:正在监听 localhost 回调地址...');
await addLog('步骤 9:正在监听 localhost 回调地址...');
return new Promise((resolve, reject) => {
let resolved = false;
@@ -48,7 +48,7 @@
setStep8PendingReject(null);
};
const rejectStep8 = (error) => {
const rejectStep9 = (error) => {
if (resolved) return;
resolved = true;
clearTimeout(timeout);
@@ -56,15 +56,15 @@
reject(error);
};
const finalizeStep8Callback = (callbackUrl) => {
const finalizeStep9Callback = (callbackUrl) => {
if (resolved || !callbackUrl) return;
resolved = true;
cleanupListener();
clearTimeout(timeout);
addLog(`步骤 8:已捕获 localhost 地址:${callbackUrl}`, 'ok').then(() => {
return completeStepFromBackground(8, { localhostUrl: callbackUrl });
addLog(`步骤 9:已捕获 localhost 地址:${callbackUrl}`, 'ok').then(() => {
return completeStepFromBackground(9, { localhostUrl: callbackUrl });
}).then(() => {
resolve();
}).catch((err) => {
@@ -73,26 +73,26 @@
};
const timeout = setTimeout(() => {
rejectStep8(new Error('120 秒内未捕获到 localhost 回调跳转,步骤 8 的点击可能被拦截了。'));
rejectStep9(new Error('120 秒内未捕获到 localhost 回调跳转,步骤 9 的点击可能被拦截了。'));
}, 120000);
setStep8PendingReject((error) => {
rejectStep8(error);
rejectStep9(error);
});
setWebNavListener((details) => {
const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
finalizeStep8Callback(callbackUrl);
finalizeStep9Callback(callbackUrl);
});
setWebNavCommittedListener((details) => {
const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
finalizeStep8Callback(callbackUrl);
finalizeStep9Callback(callbackUrl);
});
setStep8TabUpdatedListener((tabId, changeInfo, tab) => {
const callbackUrl = getStep8CallbackUrlFromTabUpdate(tabId, changeInfo, tab, signupTabId);
finalizeStep8Callback(callbackUrl);
finalizeStep9Callback(callbackUrl);
});
(async () => {
@@ -103,10 +103,10 @@
if (signupTabId && await isTabAlive('signup-page')) {
await chrome.tabs.update(signupTabId, { active: true });
await addLog('步骤 8:已切回认证页,正在准备调试器点击...');
await addLog('步骤 9:已切回认证页,正在准备调试器点击...');
} else {
signupTabId = await reuseOrCreateTab('signup-page', state.oauthUrl);
await addLog('步骤 8:已重新打开认证页,正在准备调试器点击...');
await addLog('步骤 9:已重新打开认证页,正在准备调试器点击...');
}
throwIfStep8SettledOrStopped(resolved);
@@ -115,7 +115,7 @@
chrome.tabs.onUpdated.addListener(deps.getStep8TabUpdatedListener());
await ensureStep8SignupPageReady(signupTabId, {
timeoutMs: 15000,
logMessage: '步骤 8:认证页内容脚本尚未就绪,正在等待页面恢复...',
logMessage: '步骤 9:认证页内容脚本尚未就绪,正在等待页面恢复...',
});
for (let round = 1; round <= STEP8_MAX_ROUNDS && !resolved; round++) {
@@ -128,7 +128,7 @@
const strategy = STEP8_STRATEGIES[Math.min(round - 1, STEP8_STRATEGIES.length - 1)];
await addLog(`步骤 8:第 ${round}/${STEP8_MAX_ROUNDS} 轮尝试点击“继续”(${strategy.label}...`);
await addLog(`步骤 9:第 ${round}/${STEP8_MAX_ROUNDS} 轮尝试点击“继续”(${strategy.label}...`);
if (strategy.mode === 'debugger') {
const clickTarget = await prepareStep8DebuggerClick(signupTabId);
@@ -148,33 +148,33 @@
}
if (effect.progressed) {
await addLog(`步骤 8:检测到本次点击已生效,${getStep8EffectLabel(effect)},继续等待 localhost 回调...`, 'info');
await addLog(`步骤 9:检测到本次点击已生效,${getStep8EffectLabel(effect)},继续等待 localhost 回调...`, 'info');
break;
}
if (effect.restartCurrentStep) {
await addLog(`步骤 8${getStep8EffectLabel(effect)},准备重新定位“继续”按钮并重试...`, 'warn');
await addLog(`步骤 9${getStep8EffectLabel(effect)},准备重新定位“继续”按钮并重试...`, 'warn');
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
continue;
}
if (round >= STEP8_MAX_ROUNDS) {
throw new Error(`步骤 8:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
throw new Error(`步骤 9:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
}
await addLog(`步骤 8${strategy.label} 本轮点击后页面无反应,正在刷新认证页后重试(下一轮 ${round + 1}/${STEP8_MAX_ROUNDS}...`, 'warn');
await addLog(`步骤 9${strategy.label} 本轮点击后页面无反应,正在刷新认证页后重试(下一轮 ${round + 1}/${STEP8_MAX_ROUNDS}...`, 'warn');
await reloadStep8ConsentPage(signupTabId);
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
}
} catch (err) {
rejectStep8(err);
rejectStep9(err);
}
})();
});
}
return { executeStep8 };
return { executeStep9 };
}
return { createStep8Executor };
return { createStep9Executor };
});
+35 -37
View File
@@ -1,14 +1,14 @@
(function attachBackgroundStep7(root, factory) {
root.MultiPageBackgroundStep7 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep7Module() {
function createStep7Executor(deps = {}) {
(function attachBackgroundStep8(root, factory) {
root.MultiPageBackgroundStep8 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep8Module() {
function createStep8Executor(deps = {}) {
const {
addLog,
chrome,
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
confirmCustomVerificationStepBypass,
ensureStep7VerificationPageReady,
executeStep6,
ensureStep8VerificationPageReady,
executeStep7,
getPanelMode,
getMailConfig,
getState,
@@ -29,7 +29,7 @@
throwIfStopped,
} = deps;
async function runStep7Attempt(state) {
async function runStep8Attempt(state) {
const mail = getMailConfig(state);
if (mail.error) throw new Error(mail.error);
const stepStartedAt = Date.now();
@@ -39,25 +39,25 @@
await chrome.tabs.update(authTabId, { active: true });
} else {
if (!state.oauthUrl) {
throw new Error('缺少登录用 OAuth 链接,请先完成步骤 6。');
throw new Error('缺少登录用 OAuth 链接,请先完成步骤 7。');
}
await reuseOrCreateTab('signup-page', state.oauthUrl);
}
throwIfStopped();
await ensureStep7VerificationPageReady();
await addLog('步骤 7:登录验证码页面已就绪,开始获取验证码。', 'info');
await ensureStep8VerificationPageReady();
await addLog('步骤 8:登录验证码页面已就绪,开始获取验证码。', 'info');
if (shouldUseCustomRegistrationEmail(state)) {
await confirmCustomVerificationStepBypass(7);
await confirmCustomVerificationStepBypass(8);
return;
}
throwIfStopped();
if (mail.provider === HOTMAIL_PROVIDER || mail.provider === LUCKMAIL_PROVIDER || mail.provider === CLOUDFLARE_TEMP_EMAIL_PROVIDER) {
await addLog(`步骤 7:正在通过 ${mail.label} 轮询验证码...`);
await addLog(`步骤 8:正在通过 ${mail.label} 轮询验证码...`);
} else {
await addLog(`步骤 7:正在打开${mail.label}...`);
await addLog(`步骤 8:正在打开${mail.label}...`);
const alive = await isTabAlive(mail.source);
if (alive) {
@@ -79,54 +79,52 @@
}
const shouldRefreshOAuthBeforeSubmit = getPanelMode(state) === 'cpa';
let step6ReplayCompleted = false;
let step7ReplayCompleted = false;
await resolveVerificationStep(7, state, mail, {
await resolveVerificationStep(8, state, mail, {
filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : Math.max(0, stepStartedAt - 60000),
requestFreshCodeFirst: false,
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
? 0
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
beforeSubmit: shouldRefreshOAuthBeforeSubmit ? async (result) => {
if (step6ReplayCompleted) {
if (step7ReplayCompleted) {
return;
}
step6ReplayCompleted = true;
await addLog(`步骤 7:已拿到登录验证码 ${result.code},先刷新 CPA OAuth 链接并重走步骤 6,再回填验证码。`, 'warn');
await rerunStep6ForStep7Recovery({
logMessage: '步骤 7:正在重新获取最新 CPA OAuth 链接,并快速重走步骤 6...',
skipPreLoginCleanup: true,
step7ReplayCompleted = true;
await addLog(`步骤 8:已拿到登录验证码 ${result.code},先刷新 CPA OAuth 链接并重走步骤 7,再回填验证码。`, 'warn');
await rerunStep7ForStep8Recovery({
logMessage: '步骤 8:正在重新获取最新 CPA OAuth 链接,并快速重走步骤 7...',
postStepDelayMs: 1200,
});
await ensureStep7VerificationPageReady();
await addLog('步骤 7:登录验证码页面已重新就绪,开始回填刚才获取到的验证码。', 'info');
await ensureStep8VerificationPageReady();
await addLog('步骤 8:登录验证码页面已重新就绪,开始回填刚才获取到的验证码。', 'info');
} : undefined,
});
}
async function rerunStep6ForStep7Recovery(options = {}) {
async function rerunStep7ForStep8Recovery(options = {}) {
const {
logMessage = '步骤 7:正在回到步骤 6,重新发起登录验证码流程...',
skipPreLoginCleanup = false,
logMessage = '步骤 8:正在回到步骤 7,重新发起登录验证码流程...',
postStepDelayMs = 3000,
} = options;
const currentState = await getState();
await addLog(logMessage, 'warn');
await executeStep6(currentState, { skipPreLoginCleanup });
await executeStep7(currentState);
if (postStepDelayMs > 0) {
await sleepWithStop(postStepDelayMs);
}
}
async function executeStep7(state) {
async function executeStep8(state) {
if (shouldSkipLoginVerificationForCpaCallback(state)) {
await setState({
lastLoginCode: null,
loginVerificationRequestedAt: null,
});
await setStepStatus(7, 'skipped');
await addLog('步骤 7:当前已选择“第步回调”,本轮无需获取登录验证码。', 'warn');
await setStepStatus(8, 'skipped');
await addLog('步骤 8:当前已选择“第步回调”,本轮无需获取登录验证码。', 'warn');
return;
}
@@ -136,7 +134,7 @@
while (true) {
try {
await runStep7Attempt(currentState);
await runStep8Attempt(currentState);
return;
} catch (err) {
if (!isVerificationMailPollingError(err)) {
@@ -150,25 +148,25 @@
mailPollingAttempt += 1;
await addLog(
`步骤 7:检测到邮箱轮询类失败,准备从步骤 6 重新开始(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS}...`,
`步骤 8:检测到邮箱轮询类失败,准备从步骤 7 重新开始(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS}...`,
'warn'
);
await rerunStep6ForStep7Recovery();
await rerunStep7ForStep8Recovery();
currentState = await getState();
}
}
if (lastMailPollingError) {
throw new Error(
`步骤 7:登录验证码流程在 ${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS} 轮邮箱轮询恢复后仍未成功。最后一次原因:${lastMailPollingError.message}`
`步骤 8:登录验证码流程在 ${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS} 轮邮箱轮询恢复后仍未成功。最后一次原因:${lastMailPollingError.message}`
);
}
throw new Error('步骤 7:登录验证码流程未成功完成。');
throw new Error('步骤 8:登录验证码流程未成功完成。');
}
return { executeStep7 };
return { executeStep8 };
}
return { createStep7Executor };
return { createStep8Executor };
});
+16 -22
View File
@@ -1,7 +1,7 @@
(function attachBackgroundStep6(root, factory) {
root.MultiPageBackgroundStep6 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() {
function createStep6Executor(deps = {}) {
(function attachBackgroundStep7(root, factory) {
root.MultiPageBackgroundStep7 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep7Module() {
function createStep7Executor(deps = {}) {
const {
addLog,
completeStepFromBackground,
@@ -12,7 +12,6 @@
isStep6SuccessResult,
refreshOAuthUrlBeforeStep6,
reuseOrCreateTab,
runPreStep6CookieCleanup,
sendToContentScriptResilient,
shouldSkipLoginVerificationForCpaCallback,
skipLoginVerificationStepsForCpaCallback,
@@ -20,8 +19,7 @@
throwIfStopped,
} = deps;
async function executeStep6(state, options = {}) {
const { skipPreLoginCleanup = false } = options;
async function executeStep7(state) {
if (shouldSkipLoginVerificationForCpaCallback(state)) {
await skipLoginVerificationStepsForCpaCallback();
return;
@@ -30,10 +28,6 @@
throw new Error('缺少邮箱地址,请先完成步骤 3。');
}
if (!skipPreLoginCleanup) {
await runPreStep6CookieCleanup();
}
let attempt = 0;
let lastError = null;
@@ -46,9 +40,9 @@
const oauthUrl = await refreshOAuthUrlBeforeStep6(currentState);
if (attempt === 1) {
await addLog('步骤 6:正在打开最新 OAuth 链接并登录...');
await addLog('步骤 7:正在打开最新 OAuth 链接并登录...');
} else {
await addLog(`步骤 6:上一轮失败后,正在进行第 ${attempt} 次尝试(最多 ${STEP6_MAX_ATTEMPTS} 次)...`, 'warn');
await addLog(`步骤 7:上一轮失败后,正在进行第 ${attempt} 次尝试(最多 ${STEP6_MAX_ATTEMPTS} 次)...`, 'warn');
}
await reuseOrCreateTab('signup-page', oauthUrl);
@@ -57,7 +51,7 @@
'signup-page',
{
type: 'EXECUTE_STEP',
step: 6,
step: 7,
source: 'background',
payload: {
email: currentState.email,
@@ -67,7 +61,7 @@
{
timeoutMs: 180000,
retryDelayMs: 700,
logMessage: '步骤 6:认证页正在切换,等待页面重新就绪后继续登录...',
logMessage: '步骤 7:认证页正在切换,等待页面重新就绪后继续登录...',
}
);
@@ -76,7 +70,7 @@
}
if (isStep6SuccessResult(result)) {
await completeStepFromBackground(6, {
await completeStepFromBackground(7, {
loginVerificationRequestedAt: result.loginVerificationRequestedAt || null,
});
return;
@@ -84,11 +78,11 @@
if (isStep6RecoverableResult(result)) {
const reasonMessage = result.message
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 6`;
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 7`;
throw new Error(reasonMessage);
}
throw new Error('步骤 6:认证页未返回可识别的登录结果。');
throw new Error('步骤 7:认证页未返回可识别的登录结果。');
} catch (err) {
throwIfStopped(err);
lastError = err;
@@ -96,15 +90,15 @@
break;
}
await addLog(`步骤 6:第 ${attempt} 次尝试失败,原因:${getErrorMessage(err)};准备重试...`, 'warn');
await addLog(`步骤 7:第 ${attempt} 次尝试失败,原因:${getErrorMessage(err)};准备重试...`, 'warn');
}
}
throw new Error(`步骤 6:判断失败后已重试 ${STEP6_MAX_ATTEMPTS - 1} 次,仍未成功。最后原因:${getErrorMessage(lastError)}`);
throw new Error(`步骤 7:判断失败后已重试 ${STEP6_MAX_ATTEMPTS - 1} 次,仍未成功。最后原因:${getErrorMessage(lastError)}`);
}
return { executeStep6 };
return { executeStep7 };
}
return { createStep6Executor };
return { createStep7Executor };
});
+27 -27
View File
@@ -1,7 +1,7 @@
(function attachBackgroundStep9(root, factory) {
root.MultiPageBackgroundStep9 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep9Module() {
function createStep9Executor(deps = {}) {
(function attachBackgroundStep10(root, factory) {
root.MultiPageBackgroundStep10 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep10Module() {
function createStep10Executor(deps = {}) {
const {
addLog,
chrome,
@@ -21,34 +21,34 @@
SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
} = deps;
async function executeStep9(state) {
async function executeStep10(state) {
if (getPanelMode(state) === 'sub2api') {
return executeSub2ApiStep9(state);
return executeSub2ApiStep10(state);
}
return executeCpaStep9(state);
return executeCpaStep10(state);
}
async function executeCpaStep9(state) {
async function executeCpaStep10(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 8 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 8。');
throw new Error('步骤 9 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 9。');
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 8。');
throw new Error('缺少 localhost 回调地址,请先完成步骤 9。');
}
if (!state.vpsUrl) {
throw new Error('尚未填写 CPA 地址,请先在侧边栏输入。');
}
if (shouldBypassStep9ForLocalCpa(state)) {
await addLog('步骤 9:检测到本地 CPA,且当前策略为“跳过第9步”,本轮不再重复提交回调地址。', 'info');
await completeStepFromBackground(9, {
await addLog('步骤 10:检测到本地 CPA,且当前策略为“跳过第10步”,本轮不再重复提交回调地址。', 'info');
await completeStepFromBackground(10, {
localhostUrl: state.localhostUrl,
verifiedStatus: 'local-auto',
});
return;
}
await addLog('步骤 9:正在打开 CPA 面板...');
await addLog('步骤 10:正在打开 CPA 面板...');
const injectFiles = ['content/activation-utils.js', 'content/utils.js', 'content/vps-panel.js'];
let tabId = await getTabId('vps-panel');
@@ -69,19 +69,19 @@
inject: injectFiles,
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: '步骤 9:CPA 面板仍在加载,正在重试连接...',
logMessage: '步骤 10:CPA 面板仍在加载,正在重试连接...',
});
await addLog('步骤 9:正在填写回调地址...');
await addLog('步骤 10:正在填写回调地址...');
const result = await sendToContentScriptResilient('vps-panel', {
type: 'EXECUTE_STEP',
step: 9,
step: 10,
source: 'background',
payload: { localhostUrl: state.localhostUrl, vpsPassword: state.vpsPassword },
}, {
timeoutMs: 30000,
retryDelayMs: 700,
logMessage: '步骤 9:CPA 面板通信未就绪,正在等待页面恢复...',
logMessage: '步骤 10:CPA 面板通信未就绪,正在等待页面恢复...',
});
if (result?.error) {
@@ -89,12 +89,12 @@
}
}
async function executeSub2ApiStep9(state) {
async function executeSub2ApiStep10(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 8 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 8。');
throw new Error('步骤 9 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 9。');
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 8。');
throw new Error('缺少 localhost 回调地址,请先完成步骤 9。');
}
if (!state.sub2apiSessionId) {
throw new Error('缺少 SUB2API 会话信息,请重新执行步骤 1。');
@@ -109,7 +109,7 @@
const sub2apiUrl = normalizeSub2ApiUrl(state.sub2apiUrl);
const injectFiles = ['content/utils.js', 'content/sub2api-panel.js'];
await addLog('步骤 9:正在打开 SUB2API 后台...');
await addLog('步骤 10:正在打开 SUB2API 后台...');
let tabId = await getTabId('sub2api-panel');
const alive = tabId && await isTabAlive('sub2api-panel');
@@ -131,10 +131,10 @@
injectSource: 'sub2api-panel',
});
await addLog('步骤 9:正在向 SUB2API 提交回调并创建账号...');
await addLog('步骤 10:正在向 SUB2API 提交回调并创建账号...');
const result = await sendToContentScript('sub2api-panel', {
type: 'EXECUTE_STEP',
step: 9,
step: 10,
source: 'background',
payload: {
localhostUrl: state.localhostUrl,
@@ -157,11 +157,11 @@
}
return {
executeCpaStep9,
executeStep9,
executeSub2ApiStep9,
executeCpaStep10,
executeStep10,
executeSub2ApiStep10,
};
}
return { createStep9Executor };
return { createStep10Executor };
});