diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js index 156bd46..9c36e1f 100644 --- a/sidepanel/sidepanel.js +++ b/sidepanel/sidepanel.js @@ -276,6 +276,7 @@ const DEFAULT_MAIL_2925_MODE = MAIL_2925_MODE_PROVIDE; const NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-new-user-guide-prompt-dismissed'; const AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-skip-failures-prompt-dismissed'; const AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-run-fallback-risk-prompt-dismissed'; +const AUTO_RUN_PLUS_RISK_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-run-plus-risk-prompt-dismissed'; function getStepDefinitionsForMode(plusModeEnabled = false) { return (window.MultiPageStepDefinitions?.getSteps?.({ plusModeEnabled }) || []) @@ -296,6 +297,7 @@ function rebuildStepDefinitionState(plusModeEnabled = false) { } const CONTRIBUTION_CONTENT_PROMPT_DISMISSED_VERSION_STORAGE_KEY = 'multipage-contribution-content-prompt-dismissed-version'; const AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS = 3; +const AUTO_RUN_PLUS_RISK_WARNING_MAX_SAFE_RUNS = 3; const HOTMAIL_SERVICE_MODE_REMOTE = 'remote'; const HOTMAIL_SERVICE_MODE_LOCAL = 'local'; const ICLOUD_PROVIDER = 'icloud'; @@ -1020,10 +1022,23 @@ function setAutoRunFallbackRiskPromptDismissed(dismissed) { setPromptDismissed(AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY, dismissed); } +function isAutoRunPlusRiskPromptDismissed() { + return isPromptDismissed(AUTO_RUN_PLUS_RISK_PROMPT_DISMISSED_STORAGE_KEY); +} + +function setAutoRunPlusRiskPromptDismissed(dismissed) { + setPromptDismissed(AUTO_RUN_PLUS_RISK_PROMPT_DISMISSED_STORAGE_KEY, dismissed); +} + function shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures) { return totalRuns >= AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS; } +function shouldWarnPlusAutoRunRisk(totalRuns, plusModeEnabled) { + return Boolean(plusModeEnabled) + && Math.floor(Number(totalRuns) || 0) > AUTO_RUN_PLUS_RISK_WARNING_MAX_SAFE_RUNS; +} + async function openAutoSkipFailuresConfirmModal() { const result = await openConfirmModalWithOption({ title: '自动重试说明', @@ -1050,6 +1065,19 @@ async function openAutoRunFallbackRiskConfirmModal(totalRuns) { }; } +async function openPlusAutoRunRiskConfirmModal(totalRuns) { + const result = await openConfirmModalWithOption({ + title: 'Plus 自动轮数提醒', + message: `Plus 模式下当前设置为 ${totalRuns} 轮。轮数过多可能造成 PayPal 或账号快速封号。建议够用就好:我注册了几个使用,没多注册,完全足够使用,并且没有封号。这个模式下只要可以注册成功就能使用,所以不要贪杯哦。`, + confirmLabel: '我知道了,继续', + }); + + return { + confirmed: result.confirmed, + dismissPrompt: result.optionChecked, + }; +} + function updateConfigMenuControls() { const disabled = configActionInFlight || settingsSaveInFlight; const contributionModeEnabled = Boolean(latestState?.contributionMode); @@ -4519,6 +4547,20 @@ async function startAutoRunFromCurrentSettings() { } } + const plusModeEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled + ? Boolean(inputPlusModeEnabled.checked) + : Boolean(currentPlusModeEnabled || latestState?.plusModeEnabled); + if (shouldWarnPlusAutoRunRisk(totalRuns, plusModeEnabled) + && !isAutoRunPlusRiskPromptDismissed()) { + const result = await openPlusAutoRunRiskConfirmModal(totalRuns); + if (!result.confirmed) { + return false; + } + if (result.dismissPrompt) { + setAutoRunPlusRiskPromptDismissed(true); + } + } + btnAutoRun.disabled = true; inputRunCount.disabled = true; const delayEnabled = inputAutoDelayEnabled.checked; diff --git a/tests/sidepanel-auto-run-content-refresh.test.js b/tests/sidepanel-auto-run-content-refresh.test.js index c91cb51..dc6ed2f 100644 --- a/tests/sidepanel-auto-run-content-refresh.test.js +++ b/tests/sidepanel-auto-run-content-refresh.test.js @@ -48,12 +48,23 @@ function extractFunction(name) { return sidepanelSource.slice(start, end); } -function createApi({ refreshImpl, confirmImpl, dismissImpl } = {}) { +function createApi({ + refreshImpl, + confirmImpl, + dismissImpl, + runCount = 3, + plusModeEnabled = false, + plusRiskEnabled = false, + plusRiskConfirmed = true, + plusRiskDismissPrompt = false, +} = {}) { const bundle = extractFunction('startAutoRunFromCurrentSettings'); return new Function(` const events = []; const latestState = { contributionMode: false }; +const currentPlusModeEnabled = ${JSON.stringify(Boolean(plusModeEnabled))}; +const inputPlusModeEnabled = { checked: ${JSON.stringify(Boolean(plusModeEnabled))} }; const inputAutoSkipFailures = { checked: false }; const inputContributionNickname = { value: 'tester' }; const inputContributionQq = { value: '123456' }; @@ -75,7 +86,7 @@ const console = { events.push({ type: 'warn', args }); }, }; -function getRunCountValue() { return 3; } +function getRunCountValue() { return ${Math.max(1, Number(runCount) || 1)}; } function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } function shouldOfferAutoModeChoice() { return false; } async function openAutoStartChoiceDialog() { throw new Error('should not be called'); } @@ -85,6 +96,20 @@ function shouldWarnAutoRunFallbackRisk() { return false; } function isAutoRunFallbackRiskPromptDismissed() { return false; } async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); } function setAutoRunFallbackRiskPromptDismissed() {} +function shouldWarnPlusAutoRunRisk(totalRuns, plusModeEnabled) { + return ${JSON.stringify(Boolean(plusRiskEnabled))} && Boolean(plusModeEnabled) && Number(totalRuns) > 3; +} +function isAutoRunPlusRiskPromptDismissed() { return false; } +async function openPlusAutoRunRiskConfirmModal(totalRuns) { + events.push({ type: 'plus-risk-modal', totalRuns }); + return { + confirmed: ${JSON.stringify(Boolean(plusRiskConfirmed))}, + dismissPrompt: ${JSON.stringify(Boolean(plusRiskDismissPrompt))}, + }; +} +function setAutoRunPlusRiskPromptDismissed(dismissed) { + events.push({ type: 'plus-risk-dismiss', dismissed }); +} function normalizeAutoDelayMinutes(value) { return Number(value) || 30; } async function refreshContributionContentHint() { events.push({ type: 'refresh' }); @@ -155,3 +180,39 @@ test('startAutoRunFromCurrentSettings aborts when contribution update confirmati ['refresh', 'confirm-check'] ); }); + +test('startAutoRunFromCurrentSettings shows Plus risk warning before starting more than 3 runs', async () => { + const api = createApi({ + runCount: 4, + plusModeEnabled: true, + plusRiskEnabled: true, + }); + + const result = await api.startAutoRunFromCurrentSettings(); + const events = api.getEvents(); + + assert.equal(result, true); + assert.deepEqual( + events.map((entry) => entry.type), + ['refresh', 'confirm-check', 'plus-risk-modal', 'send'] + ); + assert.equal(events[2].totalRuns, 4); + assert.equal(events[3].message.payload.totalRuns, 4); +}); + +test('startAutoRunFromCurrentSettings aborts when Plus risk warning is declined', async () => { + const api = createApi({ + runCount: 4, + plusModeEnabled: true, + plusRiskEnabled: true, + plusRiskConfirmed: false, + }); + + const result = await api.startAutoRunFromCurrentSettings(); + + assert.equal(result, false); + assert.deepEqual( + api.getEvents().map((entry) => entry.type), + ['refresh', 'confirm-check', 'plus-risk-modal'] + ); +}); diff --git a/tests/sidepanel-auto-run-risk-warning.test.js b/tests/sidepanel-auto-run-risk-warning.test.js index 5a7ba4a..aa059d3 100644 --- a/tests/sidepanel-auto-run-risk-warning.test.js +++ b/tests/sidepanel-auto-run-risk-warning.test.js @@ -65,6 +65,48 @@ return { shouldWarnAutoRunFallbackRisk }; assert.equal(api.shouldWarnAutoRunFallbackRisk(10, true), true); }); +test('Plus auto-run risk warning only starts above 3 runs in Plus mode', () => { + const bundle = extractFunction('shouldWarnPlusAutoRunRisk'); + + const api = new Function(` +const AUTO_RUN_PLUS_RISK_WARNING_MAX_SAFE_RUNS = 3; +${bundle} +return { shouldWarnPlusAutoRunRisk }; +`)(); + + assert.equal(api.shouldWarnPlusAutoRunRisk(3, true), false); + assert.equal(api.shouldWarnPlusAutoRunRisk(4, true), true); + assert.equal(api.shouldWarnPlusAutoRunRisk(10, false), false); +}); + +test('Plus auto-run risk modal uses PayPal account warning copy', async () => { + const bundle = extractFunction('openPlusAutoRunRiskConfirmModal'); + + const api = new Function(` +let capturedOptions = null; +async function openConfirmModalWithOption(options) { + capturedOptions = options; + return { confirmed: true, optionChecked: true }; +} +${bundle} +return { + openPlusAutoRunRiskConfirmModal, + getCapturedOptions() { + return capturedOptions; + }, +}; +`)(); + + const result = await api.openPlusAutoRunRiskConfirmModal(4); + const options = api.getCapturedOptions(); + + assert.deepStrictEqual(result, { confirmed: true, dismissPrompt: true }); + assert.equal(options.title, 'Plus 自动轮数提醒'); + assert.match(options.message, /轮数过多可能造成 PayPal 或账号快速封号/); + assert.match(options.message, /不要贪杯哦/); + assert.equal(options.confirmLabel, '我知道了,继续'); +}); + test('auto-run fallback risk modal uses the single-node warning copy', async () => { const bundle = extractFunction('openAutoRunFallbackRiskConfirmModal');