贡献功能开发

This commit is contained in:
QLHazyCoder
2026-04-18 23:51:56 +08:00
parent f13b90b7db
commit c994d7ae12
14 changed files with 2658 additions and 19 deletions
+176 -1
View File
@@ -3,6 +3,7 @@
importScripts(
'managed-alias-utils.js',
'background/account-run-history.js',
'background/contribution-oauth.js',
'background/panel-bridge.js',
'background/generated-email-helpers.js',
'background/signup-flow-helpers.js',
@@ -177,6 +178,22 @@ const DISPLAY_TIMEZONE = 'Asia/Shanghai';
const MICROSOFT_TOKEN_DNR_RULE_ID = 1001;
const PERSISTENT_ALIAS_STATE_KEYS = ['manualAliasUsage', 'preservedAliases'];
const ACCOUNT_RUN_HISTORY_STORAGE_KEY = 'accountRunHistory';
const CONTRIBUTION_RUNTIME_DEFAULTS = self.MultiPageBackgroundContributionOAuth?.RUNTIME_DEFAULTS || {
...CONTRIBUTION_RUNTIME_DEFAULTS,
contributionSessionId: '',
contributionAuthUrl: '',
contributionAuthState: '',
contributionCallbackUrl: '',
contributionStatus: '',
contributionStatusMessage: '',
contributionLastPollAt: 0,
contributionCallbackStatus: 'idle',
contributionCallbackMessage: '',
contributionAuthOpenedAt: 0,
contributionAuthTabId: 0,
};
const CONTRIBUTION_RUNTIME_KEYS = self.MultiPageBackgroundContributionOAuth?.RUNTIME_KEYS
|| Object.keys(CONTRIBUTION_RUNTIME_DEFAULTS);
initializeSessionStorageAccess();
setupDeclarativeNetRequestRules();
@@ -278,6 +295,7 @@ const PRE_LOGIN_COOKIE_CLEAR_ORIGINS = [
const DEFAULT_STATE = {
currentStep: 0, // 当前流程执行到的步骤编号。
stepStatuses: Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending'])),
...CONTRIBUTION_RUNTIME_DEFAULTS,
oauthUrl: null, // 运行时抓取到的 OAuth 地址,不要手动预填。
email: null, // 运行时邮箱,由程序自动获取并写入,不能手动预填。
password: null, // 运行时实际密码,由 customPassword 或程序自动生成后写入。
@@ -1119,6 +1137,65 @@ async function setPasswordState(password) {
broadcastDataUpdate({ password });
}
function buildContributionModeState(enabled, persistedSettings = {}, currentState = {}) {
const currentContributionState = {};
for (const key of CONTRIBUTION_RUNTIME_KEYS) {
currentContributionState[key] = currentState[key] !== undefined
? currentState[key]
: CONTRIBUTION_RUNTIME_DEFAULTS[key];
}
if (enabled) {
return {
...currentContributionState,
contributionMode: true,
panelMode: 'cpa',
customPassword: '',
accountRunHistoryTextEnabled: false,
};
}
return {
...CONTRIBUTION_RUNTIME_DEFAULTS,
contributionMode: false,
panelMode: persistedSettings.panelMode || DEFAULT_STATE.panelMode,
customPassword: persistedSettings.customPassword || '',
accountRunHistoryTextEnabled: Boolean(persistedSettings.accountRunHistoryTextEnabled),
};
}
async function setContributionMode(enabled) {
const normalizedEnabled = Boolean(enabled);
const [persistedSettings, currentState] = await Promise.all([
getPersistedSettings(),
getState(),
]);
if (normalizedEnabled) {
await setPersistentSettings({ panelMode: 'cpa' });
}
const updates = buildContributionModeState(normalizedEnabled, {
...persistedSettings,
...(normalizedEnabled ? { panelMode: 'cpa' } : {}),
}, currentState);
await setState(updates);
const nextState = await getState();
const contributionBroadcast = {};
for (const key of CONTRIBUTION_RUNTIME_KEYS) {
contributionBroadcast[key] = nextState[key];
}
broadcastDataUpdate({
...contributionBroadcast,
panelMode: nextState.panelMode,
customPassword: nextState.customPassword,
accountRunHistoryTextEnabled: nextState.accountRunHistoryTextEnabled,
accountRunHistoryHelperBaseUrl: nextState.accountRunHistoryHelperBaseUrl,
});
return nextState;
}
function getLuckmailUsedPurchases(state = {}) {
return normalizeLuckmailUsedPurchases(state?.luckmailUsedPurchases);
}
@@ -1269,15 +1346,21 @@ async function resetState() {
'luckmailPreserveTagId',
'luckmailPreserveTagName',
'preferredIcloudHost',
...CONTRIBUTION_RUNTIME_KEYS,
]),
getPersistedSettings(),
getPersistedAliasState(),
]);
const contributionModeState = buildContributionModeState(Boolean(prev.contributionMode), {
...persistedSettings,
...(prev.contributionMode ? { panelMode: 'cpa' } : {}),
}, prev);
await chrome.storage.session.clear();
await chrome.storage.session.set({
...DEFAULT_STATE,
...persistedSettings,
...persistedAliasState,
...contributionModeState,
seenCodes: prev.seenCodes || [],
seenInbucketMailIds: prev.seenInbucketMailIds || [],
accounts: prev.accounts || [],
@@ -5215,6 +5298,15 @@ const accountRunHistoryHelpers = self.MultiPageBackgroundAccountRunHistory?.crea
getState,
normalizeAccountRunHistoryHelperBaseUrl,
});
const contributionOAuthManager = self.MultiPageBackgroundContributionOAuth?.createContributionOAuthManager({
addLog,
broadcastDataUpdate,
chrome,
closeLocalhostCallbackTabs,
getState,
setState,
});
contributionOAuthManager?.ensureCallbackListeners?.();
async function broadcastAccountRunHistoryUpdate() {
if (!accountRunHistoryHelpers?.getPersistedAccountRunHistory) {
@@ -5944,7 +6036,7 @@ const stepExecutorsByKey = {
'oauth-login': (state) => step7Executor.executeStep7(state),
'fetch-login-code': (state) => step8Executor.executeStep8(state),
'confirm-oauth': (state) => step9Executor.executeStep9(state),
'platform-verify': (state) => step10Executor.executeStep10(state),
'platform-verify': (state) => executeStep10(state),
};
const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter({
addLog,
@@ -6016,6 +6108,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
scheduleAutoRun,
selectLuckmailPurchase,
setCurrentHotmailAccount,
setContributionMode,
setEmailState,
setEmailStateSilently,
setIcloudAliasPreservedState,
@@ -6028,7 +6121,9 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
setStepStatus,
skipAutoRunCountdown,
skipStep,
startContributionFlow: (...args) => contributionOAuthManager?.startContributionFlow?.(...args),
startAutoRunLoop,
pollContributionStatus: (...args) => contributionOAuthManager?.pollContributionStatus?.(...args),
syncHotmailAccounts,
testHotmailAccountMailAccess,
upsertHotmailAccount,
@@ -6359,6 +6454,20 @@ async function runPreStep6CookieCleanup() {
// ============================================================
async function refreshOAuthUrlBeforeStep6(state) {
if (state?.contributionMode && contributionOAuthManager?.startContributionFlow) {
await addLog('步骤 7:贡献模式正在申请贡献登录地址...');
const contributionState = await contributionOAuthManager.startContributionFlow({
nickname: state.email,
openAuthTab: false,
stateOverride: state,
});
const oauthUrl = String(contributionState?.contributionAuthUrl || '').trim();
if (!oauthUrl) {
throw new Error('贡献模式未返回可用的登录地址,请稍后重试。');
}
await handleStepData(1, { oauthUrl });
return oauthUrl;
}
await addLog(`步骤 7:正在刷新登录用的 ${getPanelModeLabel(state)} OAuth 链接...`);
console.log(LOG_PREFIX, '[refreshOAuthUrlBeforeStep6] requesting fresh OAuth directly from panel');
const refreshResult = await requestOAuthUrlFromPanel(state, { logLabel: '步骤 7' });
@@ -6972,7 +7081,73 @@ async function executeStep9(state) {
// Step 10: 平台回调验证
// ============================================================
async function executeContributionStep10(state) {
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 9 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 9。');
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 9。');
}
if (!state.contributionSessionId) {
throw new Error('缺少贡献会话信息,请重新从步骤 7 开始。');
}
if (!contributionOAuthManager?.pollContributionStatus) {
throw new Error('贡献 OAuth 流程尚未接入,无法完成贡献模式的步骤 10。');
}
await addLog('步骤 10:贡献模式正在提交回调并等待最终结果...');
let latestState = await getState();
const callbackUrl = latestState.localhostUrl || state.localhostUrl;
if (!latestState.contributionCallbackUrl && contributionOAuthManager?.handleCapturedCallback) {
latestState = await contributionOAuthManager.handleCapturedCallback(callbackUrl, {
source: 'step10',
});
} else {
latestState = await contributionOAuthManager.pollContributionStatus({
reason: 'step10_initial',
stateOverride: latestState,
});
}
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(120000, {
step: 10,
actionLabel: '贡献流程最终结果',
})
: 120000;
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const status = String(latestState.contributionStatus || '').trim().toLowerCase();
if (contributionOAuthManager?.isContributionFinalStatus?.(status)) {
if (status === 'auto_approved' || status === 'manual_review_required') {
await addLog(`步骤 10:贡献流程已结束,最终状态:${latestState.contributionStatusMessage || status}`, status === 'auto_approved' ? 'ok' : 'warn');
await completeStepFromBackground(10, {
contributionStatus: status,
contributionStatusMessage: latestState.contributionStatusMessage || '',
localhostUrl: callbackUrl,
});
return;
}
throw new Error(latestState.contributionStatusMessage || '贡献流程失败。');
}
await sleepWithStop(2500);
latestState = await contributionOAuthManager.pollContributionStatus({
reason: 'step10_wait_final',
stateOverride: latestState,
});
}
throw new Error('步骤 10:等待贡献流程最终结果超时。');
}
async function executeStep10(state) {
if (state?.contributionMode) {
return executeContributionStep10(state);
}
return step10Executor.executeStep10(state);
}