feat(contribution): 增强贡献模式逻辑,支持 SUB2API 和 CPA 源,优化状态处理和界面显示

This commit is contained in:
QLHazycoder
2026-04-26 00:28:25 +00:00
parent d9ae1c42cf
commit 596bf38d5c
9 changed files with 334 additions and 40 deletions
+145 -3
View File
@@ -84,14 +84,16 @@ test('contribution oauth module exposes a factory', () => {
assert.equal(Array.isArray(api?.RUNTIME_KEYS), true);
});
test('buildContributionModeState preserves active contribution runtime while forcing CPA mode', () => {
test('buildContributionModeState preserves active contribution runtime while keeping contribution on sub2api', () => {
const bundle = extractFunction(backgroundSource, 'buildContributionModeState');
const api = new Function(`
const api = new Function(`
const DEFAULT_STATE = { panelMode: 'cpa' };
const CONTRIBUTION_RUNTIME_DEFAULTS = {
contributionMode: false,
contributionModeExpected: false,
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionNickname: '',
contributionQq: '',
contributionSessionId: '',
@@ -107,6 +109,35 @@ const CONTRIBUTION_RUNTIME_DEFAULTS = {
contributionAuthTabId: 0,
};
const CONTRIBUTION_RUNTIME_KEYS = Object.keys(CONTRIBUTION_RUNTIME_DEFAULTS);
function isPlusModeState(state = {}) { return Boolean(state?.plusModeEnabled); }
function normalizeContributionModeSource(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'sub2api' ? 'sub2api' : 'cpa';
}
function resolveContributionModeRoutingState(state = {}) {
const currentStatus = String(state?.contributionStatus || '').trim().toLowerCase();
const currentSource = normalizeContributionModeSource(state?.contributionSource);
const hasActiveSession = Boolean(
String(state?.contributionSessionId || '').trim()
&& currentStatus
&& !['auto_approved', 'auto_rejected', 'expired', 'error'].includes(currentStatus)
);
if (hasActiveSession) {
return {
source: currentSource,
targetGroupName: currentSource === 'sub2api'
? (String(state?.contributionTargetGroupName || '').trim() || 'codex号池')
: '',
};
}
const source = 'sub2api';
return {
source,
targetGroupName: isPlusModeState(state)
? 'openai-plus'
: (String(state?.contributionTargetGroupName || '').trim() || 'codex号池'),
};
}
${bundle}
return { buildContributionModeState };
`)();
@@ -125,6 +156,8 @@ return { buildContributionModeState };
{
contributionMode: true,
contributionModeExpected: true,
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionNickname: '',
contributionQq: '',
contributionSessionId: 'session-001',
@@ -138,7 +171,7 @@ return { buildContributionModeState };
contributionCallbackMessage: '',
contributionAuthOpenedAt: 0,
contributionAuthTabId: 0,
panelMode: 'cpa',
panelMode: 'sub2api',
customPassword: '',
accountRunHistoryTextEnabled: false,
}
@@ -157,6 +190,8 @@ return { buildContributionModeState };
{
contributionMode: false,
contributionModeExpected: false,
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionNickname: '',
contributionQq: '',
contributionSessionId: '',
@@ -175,6 +210,37 @@ return { buildContributionModeState };
accountRunHistoryTextEnabled: true,
}
);
assert.deepStrictEqual(
api.buildContributionModeState(true, {
panelMode: 'cpa',
plusModeEnabled: true,
customPassword: 'Secret123!',
accountRunHistoryTextEnabled: true,
}, {}),
{
contributionMode: true,
contributionModeExpected: true,
contributionSource: 'sub2api',
contributionTargetGroupName: 'openai-plus',
contributionNickname: '',
contributionQq: '',
contributionSessionId: '',
contributionAuthUrl: '',
contributionAuthState: '',
contributionCallbackUrl: '',
contributionStatus: '',
contributionStatusMessage: '',
contributionLastPollAt: 0,
contributionCallbackStatus: 'idle',
contributionCallbackMessage: '',
contributionAuthOpenedAt: 0,
contributionAuthTabId: 0,
panelMode: 'sub2api',
customPassword: '',
accountRunHistoryTextEnabled: false,
}
);
});
test('resetState preserves contribution runtime across reset', () => {
@@ -334,6 +400,8 @@ test('contribution oauth manager starts session, opens auth url, submits callbac
let statusPollCount = 0;
let currentState = {
contributionMode: true,
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
email: 'user@example.com',
contributionSessionId: '',
contributionStatus: '',
@@ -424,6 +492,8 @@ test('contribution oauth manager starts session, opens auth url, submits callbac
assert.match(String(fetchCalls[0].options.body || ''), /"nickname":""/);
assert.match(String(fetchCalls[0].options.body || ''), /"qq":""/);
assert.match(String(fetchCalls[0].options.body || ''), /"email":"user@example\.com"/);
assert.match(String(fetchCalls[0].options.body || ''), /"source":"sub2api"/);
assert.match(String(fetchCalls[0].options.body || ''), /"target_group_name":"codex号池"/);
assert.match(fetchCalls[1].url, /\/status\?/);
const callbackState = await manager.handleCapturedCallback(
@@ -471,6 +541,78 @@ test('contribution oauth manager accepts localhost callback urls that contain er
);
});
test('contribution oauth manager switches Plus contribution traffic to sub2api openai-plus', async () => {
const source = fs.readFileSync('background/contribution-oauth.js', 'utf8');
const globalScope = {};
const fetchCalls = [];
let currentState = {
contributionMode: true,
plusModeEnabled: true,
contributionSource: 'sub2api',
contributionTargetGroupName: 'openai-plus',
contributionSessionId: '',
contributionStatus: '',
contributionCallbackStatus: 'idle',
};
const api = new Function('self', 'fetch', `${source}; return self.MultiPageBackgroundContributionOAuth;`)(
globalScope,
async (url, options = {}) => {
fetchCalls.push({ url, options });
if (String(url).endsWith('/start')) {
return createMockResponse(true, 200, {
ok: true,
session_id: 'session-plus-001',
state: 'oauth-state-plus-001',
source: 'sub2api',
target_group_name: 'openai-plus',
auth_url: 'https://auth.example.com/oauth?state=oauth-state-plus-001',
});
}
if (String(url).includes('/status?')) {
return createMockResponse(true, 200, {
ok: true,
session_id: 'session-plus-001',
status: 'waiting',
source: 'sub2api',
target_group_name: 'openai-plus',
});
}
return createMockResponse(true, 200, { ok: true });
}
);
const manager = api.createContributionOAuthManager({
chrome: {
tabs: {
async create(payload) {
return { id: 91, url: payload.url };
},
async update() {
return null;
},
onUpdated: { addListener() {} },
},
webNavigation: {
onCommitted: { addListener() {} },
onHistoryStateUpdated: { addListener() {} },
},
},
getState: async () => currentState,
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
broadcastDataUpdate: (updates) => {
currentState = { ...currentState, ...updates };
},
});
await manager.startContributionFlow();
assert.match(String(fetchCalls[0].options.body || ''), /"source":"sub2api"/);
assert.match(String(fetchCalls[0].options.body || ''), /"target_group_name":"openai-plus"/);
});
test('refreshOAuthUrlBeforeStep6 uses contribution oauth session instead of panel bridge in contribution mode', async () => {
const bundle = extractFunction(backgroundSource, 'refreshOAuthUrlBeforeStep6');
const calls = [];
+20 -5
View File
@@ -191,6 +191,7 @@ return {
`)();
const contributionPayload = api.collectSettingsPayload();
assert.equal('panelMode' in contributionPayload, false);
assert.equal('customPassword' in contributionPayload, false);
assert.equal('accountRunHistoryTextEnabled' in contributionPayload, false);
assert.equal('accountRunHistoryHelperBaseUrl' in contributionPayload, false);
@@ -199,6 +200,7 @@ return {
api.setLatestState({ contributionMode: false });
const normalPayload = api.collectSettingsPayload();
assert.equal(normalPayload.panelMode, 'cpa');
assert.equal(normalPayload.customPassword, 'Secret123!');
assert.equal(normalPayload.accountRunHistoryTextEnabled, true);
assert.equal(normalPayload.accountRunHistoryHelperBaseUrl, 'http://127.0.0.1:17373');
@@ -231,6 +233,8 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
let latestState = {
contributionMode: false,
panelMode: 'sub2api',
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionSessionId: '',
contributionStatus: '',
contributionStatusMessage: '',
@@ -258,6 +262,7 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
btnOpenAccountRecords: createElement(),
btnOpenContributionUpload: createElement(),
btnStartContribution: createElement(),
contributionModeBadge: createElement(),
inputContributionNickname: createElement({ value: '贡献者昵称' }),
inputContributionQq: createElement({ value: '123456' }),
contributionCallbackStatus: createElement(),
@@ -353,7 +358,9 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
state: message.payload.enabled
? {
contributionMode: true,
panelMode: 'cpa',
panelMode: 'sub2api',
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionNickname: '',
contributionQq: '',
contributionSessionId: '',
@@ -368,6 +375,8 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
: {
contributionMode: false,
panelMode: 'cpa',
contributionSource: 'cpa',
contributionTargetGroupName: '',
contributionNickname: '',
contributionQq: '',
contributionSessionId: '',
@@ -387,7 +396,7 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
state: {
...latestState,
contributionStatus: 'processing',
contributionStatusMessage: '已提交回调,等待 CPA 确认',
contributionStatusMessage: '已提交回调,等待服务端确认',
contributionCallbackStatus: 'submitted',
contributionCallbackMessage: '已提交回调',
},
@@ -414,13 +423,15 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
manager.render();
assert.equal(dom.contributionModePanel.hidden, true);
assert.equal(dom.btnContributionMode.disabled, false);
assert.equal(dom.contributionModeBadge.textContent, '');
manager.bindEvents();
await dom.btnContributionMode.listeners.click();
assert.equal(dom.contributionModePanel.hidden, false);
assert.equal(dom.selectPanelMode.value, 'cpa');
assert.equal(dom.selectPanelMode.value, 'sub2api');
assert.equal(dom.selectPanelMode.disabled, true);
assert.equal(dom.contributionModeBadge.textContent, 'SUB2API');
assert.equal(dom.btnOpenAccountRecords.disabled, true);
assert.equal(dom.contributionOauthStatus.textContent, '\u672a\u751f\u6210\u767b\u5f55\u5730\u5740');
assert.equal(dom.contributionCallbackStatus.textContent, '\u7b49\u5f85\u56de\u8c03');
@@ -453,7 +464,7 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
assert.equal(statusState.contributionStatus, 'processing');
assert.equal(dom.contributionOauthStatus.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03');
assert.equal(dom.contributionCallbackStatus.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03');
assert.equal(dom.contributionModeSummary.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03\uff0c\u7b49\u5f85 CPA \u786e\u8ba4');
assert.equal(dom.contributionModeSummary.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03\uff0c\u7b49\u5f85\u670d\u52a1\u7aef\u786e\u8ba4');
dom.btnOpenContributionUpload.listeners.click();
assert.deepStrictEqual(openedUrls, ['https://apikey.qzz.io', 'https://apikey.qzz.io/upload']);
@@ -476,7 +487,9 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
blocked = true;
latestState = {
contributionMode: true,
panelMode: 'cpa',
panelMode: 'sub2api',
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionNickname: '贡献者昵称',
contributionQq: '123456',
contributionSessionId: 'session-002',
@@ -487,6 +500,8 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
contributionCallbackMessage: '\u7b49\u5f85\u56de\u8c03',
};
manager.render();
assert.equal(dom.selectPanelMode.value, 'sub2api');
assert.equal(dom.contributionModeBadge.textContent, 'SUB2API');
assert.equal(dom.btnExitContributionMode.disabled, true);
manager.stopPolling();
});