Merge pull request #151 from que01/icloud-forward-mailbox-config

feat: support icloud forward mailbox provider config
This commit is contained in:
Q3CC
2026-04-26 14:48:37 +08:00
committed by GitHub
15 changed files with 690 additions and 85 deletions
+3 -1
View File
@@ -75,6 +75,7 @@
- 至少准备一种验证码接收方式:
- DuckDuckGo `@duck.com` + QQ / 163 / Inbucket 转发
- Cloudflare 自定义域邮箱前缀 + QQ / 163 / Inbucket 转发
- iCloud Hide My Email,可选择直接从 iCloud 收件箱收码,或转发到 QQ / 163 / 163 VIP / 126 / Gmail 后收码
- 手动填写一个可收信邮箱
- 如果使用 `QQ` / `163` / `163 VIP` / `126` / `Inbucket`,对应页面需要提前能正常打开
@@ -592,6 +593,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。
支持:
- `Hotmail`(远程服务 / 本地助手)
- `iCloud` 收件箱,或 iCloud Hide My Email 转发到 QQ / 163 / 163 VIP / 126 / Gmail 后收码
- `content/qq-mail.js`
- `content/mail-163.js`163 / 163 VIP / 126
- `content/inbucket-mail.js`
@@ -773,7 +775,7 @@ content/utils.js 通用工具:等待元素、点击、日志、停
content/vps-panel.js CPA 面板步骤:内部 OAuth 刷新 / Step 10
content/signup-page.js ChatGPT 官网 + OpenAI 注册/登录页步骤:Step 1 / 2 / 3 / 5 / 7 / 9
hotmail-utils.js Hotmail 收信相关通用辅助
mail-provider-utils.js 网页邮箱 provider 配置辅助
mail-provider-utils.js 网页邮箱 provider 与 iCloud 转发收码配置辅助
content/duck-mail.js Duck 邮箱自动获取
content/qq-mail.js QQ 邮箱验证码轮询
content/mail-163.js 163 / 163 VIP / 126 邮箱验证码轮询
+23
View File
@@ -39,6 +39,7 @@ importScripts(
'luckmail-utils.js',
'cloudflare-temp-email-utils.js',
'icloud-utils.js',
'mail-provider-utils.js',
'content/activation-utils.js'
);
@@ -145,6 +146,11 @@ const {
pickReusableIcloudAlias,
toNormalizedEmailSet,
} = self.IcloudUtils;
const {
getIcloudForwardMailConfig: getSharedIcloudForwardMailConfig,
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = self.MailProviderUtils;
const {
isRecoverableStep9AuthFailure,
} = self.MultiPageActivationUtils;
@@ -369,6 +375,8 @@ const PERSISTED_SETTING_DEFAULTS = {
customEmailPool: [],
autoDeleteUsedIcloudAlias: false,
icloudHostPreference: 'auto',
icloudTargetMailboxType: 'icloud-inbox',
icloudForwardMailProvider: 'qq',
icloudFetchMode: 'reuse_existing',
accountRunHistoryTextEnabled: true,
accountRunHistoryHelperBaseUrl: DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL,
@@ -1105,6 +1113,10 @@ function normalizePersistentSettingValue(key, value) {
return Boolean(value);
case 'icloudHostPreference':
return normalizeIcloudHost(value) || 'auto';
case 'icloudTargetMailboxType':
return normalizeIcloudTargetMailboxType(value);
case 'icloudForwardMailProvider':
return normalizeIcloudForwardMailProvider(value);
case 'icloudFetchMode':
return normalizeIcloudFetchMode(value);
case 'accountRunHistoryHelperBaseUrl':
@@ -7317,6 +7329,17 @@ function getMailConfig(state) {
const configuredHost = getConfiguredIcloudHostPreference(state)
|| normalizeIcloudHost(state?.preferredIcloudHost)
|| 'icloud.com';
const targetMailboxType = normalizeIcloudTargetMailboxType(state?.icloudTargetMailboxType);
const useForwardMailbox = targetMailboxType === 'forward-mailbox';
if (useForwardMailbox) {
const forwardProvider = normalizeIcloudForwardMailProvider(state?.icloudForwardMailProvider);
const forwardConfig = getSharedIcloudForwardMailConfig(forwardProvider);
return {
...forwardConfig,
label: `iCloud 转发(${forwardConfig.label}`,
icloudForwarding: true,
};
}
const loginUrl = getIcloudLoginUrlForHost(configuredHost) || 'https://www.icloud.com/';
const mailUrl = getIcloudMailUrlForHost(configuredHost) || loginUrl;
return {
+47
View File
@@ -1,5 +1,15 @@
const HOTMAIL_PROVIDER = 'hotmail-api';
const GMAIL_PROVIDER = 'gmail';
const NETEASE_LIST_PATH = '/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D';
const ICLOUD_TARGET_MAILBOX_TYPE_INBOX = 'icloud-inbox';
const ICLOUD_TARGET_MAILBOX_TYPE_FORWARD = 'forward-mailbox';
const ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS = [
{ value: 'qq', label: 'QQ 邮箱' },
{ value: '163', label: '163 邮箱' },
{ value: '163-vip', label: '163 VIP 邮箱' },
{ value: '126', label: '126 邮箱' },
{ value: GMAIL_PROVIDER, label: 'Gmail 邮箱' },
];
function normalizeMailProvider(value = '') {
const normalized = String(value || '').trim().toLowerCase();
@@ -16,6 +26,38 @@ function normalizeMailProvider(value = '') {
}
}
function normalizeIcloudTargetMailboxType(value = '') {
return String(value || '').trim().toLowerCase() === ICLOUD_TARGET_MAILBOX_TYPE_FORWARD
? ICLOUD_TARGET_MAILBOX_TYPE_FORWARD
: ICLOUD_TARGET_MAILBOX_TYPE_INBOX;
}
function normalizeIcloudForwardMailProvider(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS.some((option) => option.value === normalized)
? normalized
: 'qq';
}
function getIcloudForwardMailProviderOptions() {
return ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS.map((option) => ({ ...option }));
}
function getIcloudForwardMailConfig(provider = 'qq') {
const normalizedProvider = normalizeIcloudForwardMailProvider(provider);
if (normalizedProvider === GMAIL_PROVIDER) {
return {
source: 'gmail-mail',
url: 'https://mail.google.com/mail/u/0/#inbox',
label: 'Gmail 邮箱',
inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
injectSource: 'gmail-mail',
};
}
return getMailProviderConfig({ mailProvider: normalizedProvider });
}
function getMailProviderConfig(state = {}, options = {}) {
const provider = normalizeMailProvider(state.mailProvider);
const normalizeInbucketOrigin = options.normalizeInbucketOrigin || (() => '');
@@ -66,8 +108,13 @@ function getMailProviderConfig(state = {}, options = {}) {
}
const api = {
GMAIL_PROVIDER,
HOTMAIL_PROVIDER,
getIcloudForwardMailConfig,
getIcloudForwardMailProviderOptions,
getMailProviderConfig,
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
normalizeMailProvider,
};
+18
View File
@@ -685,6 +685,23 @@
<option value="icloud.com.cn">iCloud.com.cn</option>
</select>
</div>
<div class="data-row" id="row-icloud-target-mailbox-type">
<span class="data-label">目标邮箱类型</span>
<select id="select-icloud-target-mailbox-type" class="data-select">
<option value="icloud-inbox">iCloud 收件箱</option>
<option value="forward-mailbox">转发到其他邮箱</option>
</select>
</div>
<div class="data-row" id="row-icloud-forward-mail-provider" style="display:none;">
<span class="data-label">转发邮箱</span>
<select id="select-icloud-forward-mail-provider" class="data-select">
<option value="qq">QQ 邮箱</option>
<option value="163">163 邮箱</option>
<option value="163-vip">163 VIP 邮箱</option>
<option value="126">126 邮箱</option>
<option value="gmail">Gmail 邮箱</option>
</select>
</div>
<div class="data-row">
<span class="data-label">获取策略</span>
<select id="select-icloud-fetch-mode" class="data-select">
@@ -839,6 +856,7 @@
<script src="../managed-alias-utils.js"></script>
<script src="../mail2925-utils.js"></script>
<script src="../icloud-utils.js"></script>
<script src="../mail-provider-utils.js"></script>
<script src="../hotmail-utils.js"></script>
<script src="../luckmail-utils.js"></script>
<script src="../data/step-definitions.js"></script>
+113
View File
@@ -147,6 +147,10 @@ const btnIcloudLoginDone = document.getElementById('btn-icloud-login-done');
const btnIcloudRefresh = document.getElementById('btn-icloud-refresh');
const btnIcloudDeleteUsed = document.getElementById('btn-icloud-delete-used');
const selectIcloudHostPreference = document.getElementById('select-icloud-host-preference');
const rowIcloudTargetMailboxType = document.getElementById('row-icloud-target-mailbox-type');
const selectIcloudTargetMailboxType = document.getElementById('select-icloud-target-mailbox-type');
const rowIcloudForwardMailProvider = document.getElementById('row-icloud-forward-mail-provider');
const selectIcloudForwardMailProvider = document.getElementById('select-icloud-forward-mail-provider');
const selectIcloudFetchMode = document.getElementById('select-icloud-fetch-mode');
const checkboxAutoDeleteIcloud = document.getElementById('checkbox-auto-delete-icloud');
const inputIcloudSearch = document.getElementById('input-icloud-search');
@@ -610,6 +614,28 @@ const normalizeIcloudFetchMode = (value) => {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'always_new' ? 'always_new' : 'reuse_existing';
};
const normalizeIcloudTargetMailboxType = window.MailProviderUtils?.normalizeIcloudTargetMailboxType
|| ((value) => String(value || '').trim().toLowerCase() === 'forward-mailbox'
? 'forward-mailbox'
: 'icloud-inbox');
const getIcloudForwardMailProviderOptions = window.MailProviderUtils?.getIcloudForwardMailProviderOptions
|| (() => Array.from(selectIcloudForwardMailProvider?.options || [])
.map((option) => ({
value: String(option?.value || '').trim().toLowerCase(),
label: String(option?.textContent || option?.label || option?.value || '').trim(),
}))
.filter((option) => option.value));
const normalizeIcloudForwardMailProvider = window.MailProviderUtils?.normalizeIcloudForwardMailProvider
|| ((value) => {
const normalized = String(value || '').trim().toLowerCase();
const options = getIcloudForwardMailProviderOptions();
return options.some((option) => option.value === normalized)
? normalized
: (options[0]?.value || 'qq');
});
const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = Object.fromEntries(
getIcloudForwardMailProviderOptions().map((option) => [option.value, option.label])
);
const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost
|| ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : ''));
@@ -1866,6 +1892,14 @@ function collectSettingsPayload() {
const icloudFetchModeRawValue = typeof selectIcloudFetchMode !== 'undefined'
? String(selectIcloudFetchMode?.value || '')
: '';
const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined'
? selectIcloudTargetMailboxType?.value
: '';
const icloudForwardMailProviderValue = typeof selectIcloudForwardMailProvider !== 'undefined'
? selectIcloudForwardMailProvider?.value
: '';
const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue);
const normalizedIcloudForwardMailProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue);
const mail2925UseAccountPool = typeof inputMail2925UseAccountPool !== 'undefined'
? Boolean(inputMail2925UseAccountPool?.checked)
: Boolean(latestState?.mail2925UseAccountPool);
@@ -1917,6 +1951,8 @@ function collectSettingsPayload() {
: [],
autoDeleteUsedIcloudAlias: checkboxAutoDeleteIcloud?.checked,
icloudHostPreference: selectIcloudHostPreference?.value || 'auto',
icloudTargetMailboxType: normalizedIcloudTargetMailboxType,
icloudForwardMailProvider: normalizedIcloudForwardMailProvider,
icloudFetchMode: (icloudFetchModeRawValue.trim().toLowerCase() === 'always_new'
? 'always_new'
: 'reuse_existing'),
@@ -2462,6 +2498,12 @@ function applySettingsState(state) {
if (selectIcloudFetchMode) {
selectIcloudFetchMode.value = normalizeIcloudFetchMode(state?.icloudFetchMode);
}
if (selectIcloudTargetMailboxType) {
selectIcloudTargetMailboxType.value = normalizeIcloudTargetMailboxType(state?.icloudTargetMailboxType);
}
if (selectIcloudForwardMailProvider) {
selectIcloudForwardMailProvider.value = normalizeIcloudForwardMailProvider(state?.icloudForwardMailProvider);
}
if (checkboxAutoDeleteIcloud) {
checkboxAutoDeleteIcloud.checked = Boolean(state?.autoDeleteUsedIcloudAlias);
}
@@ -3370,6 +3412,21 @@ function updateMailLoginButtonState() {
}
function updateMailProviderUI() {
const normalizeIcloudHostValue = typeof normalizeIcloudHost === 'function'
? normalizeIcloudHost
: ((value) => {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
});
const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined'
? selectIcloudTargetMailboxType?.value
: latestState?.icloudTargetMailboxType;
const icloudForwardMailProviderValue = typeof selectIcloudForwardMailProvider !== 'undefined'
? selectIcloudForwardMailProvider?.value
: latestState?.icloudForwardMailProvider;
const icloudHostPreferenceValue = typeof selectIcloudHostPreference !== 'undefined'
? selectIcloudHostPreference?.value
: latestState?.icloudHostPreference;
const use2925 = selectMailProvider.value === '2925';
const useGmail = selectMailProvider.value === GMAIL_PROVIDER;
const useMail2925 = selectMailProvider.value === '2925';
@@ -3431,6 +3488,15 @@ function updateMailProviderUI() {
const showCloudflareDomain = useEmailGenerator && useCloudflare;
const showCloudflareTempEmailSettings = useCloudflareTempEmailProvider || (useEmailGenerator && useCloudflareTempEmailGenerator);
const showCloudflareTempEmailReceiveMailbox = useCloudflareTempEmailProvider && !useCloudflareTempEmailGenerator;
const selectedIcloudHost = typeof getSelectedIcloudHostPreference === 'function'
? getSelectedIcloudHostPreference()
: (normalizeIcloudHostValue(icloudHostPreferenceValue || latestState?.icloudHostPreference || '')
|| normalizeIcloudHostValue(latestState?.preferredIcloudHost)
|| 'icloud.com');
const icloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue);
const isIcloudComCnHost = selectedIcloudHost === 'icloud.com.cn';
const showIcloudTargetMailboxType = useIcloudProvider;
const showIcloudForwardMailProvider = useIcloudProvider && icloudTargetMailboxType === 'forward-mailbox';
const showCloudflareTempEmailRandomSubdomainToggle = useEmailGenerator && useCloudflareTempEmailGenerator;
const showCloudflareTempEmailDomain = useEmailGenerator && useCloudflareTempEmailGenerator;
if (rowEmailGenerator) {
@@ -3452,6 +3518,12 @@ function updateMailProviderUI() {
hideIcloudLoginHelp();
}
}
if (typeof rowIcloudTargetMailboxType !== 'undefined' && rowIcloudTargetMailboxType) {
rowIcloudTargetMailboxType.style.display = showIcloudTargetMailboxType ? '' : 'none';
}
if (typeof rowIcloudForwardMailProvider !== 'undefined' && rowIcloudForwardMailProvider) {
rowIcloudForwardMailProvider.style.display = showIcloudForwardMailProvider ? '' : 'none';
}
rowCfDomain.style.display = showCloudflareDomain ? '' : 'none';
const { domains } = getCloudflareDomainsFromState();
if (showCloudflareDomain) {
@@ -3567,6 +3639,13 @@ function updateMailProviderUI() {
if (autoHintText && showCloudflareTempEmailRandomSubdomainToggle && inputTempEmailUseRandomSubdomain?.checked) {
autoHintText.textContent = '已启用随机子域名:扩展会按当前选中的 Temp 域名提交,并额外携带 enableRandomSubdomain;是否生效取决于后端 RANDOM_SUBDOMAIN_DOMAINS 配置。';
}
if (autoHintText && useIcloudProvider && showIcloudForwardMailProvider) {
const forwardProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue);
const forwardProviderLabel = ICLOUD_FORWARD_MAIL_PROVIDER_LABELS[forwardProvider]
|| MAIL_PROVIDER_LOGIN_CONFIGS[forwardProvider]?.label
|| '目标邮箱';
autoHintText.textContent = `iCloud ${isIcloudComCnHost ? 'com.cn' : ''} 当前使用转发收码:第 4/8 步会从 ${forwardProviderLabel} 轮询验证码。`;
}
if (useHotmail) {
inputEmail.value = getCurrentHotmailEmail();
} else if (useLuckmail) {
@@ -3724,6 +3803,9 @@ function updateButtonStates() {
const anyRunning = Object.values(statuses).some(s => s === 'running');
const autoLocked = isAutoRunLockedPhase();
const autoScheduled = isAutoRunScheduledPhase();
const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined'
? selectIcloudTargetMailboxType?.value
: latestState?.icloudTargetMailboxType;
for (const step of STEP_IDS) {
const btn = document.querySelector(`.step-btn[data-step="${step}"]`);
@@ -3773,6 +3855,15 @@ function updateButtonStates() {
if (btnIcloudRefresh) btnIcloudRefresh.disabled = disableIcloudControls;
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = disableIcloudControls || !hasDeletableUsedIcloudAliases();
if (selectIcloudHostPreference) selectIcloudHostPreference.disabled = disableIcloudControls;
if (typeof selectIcloudTargetMailboxType !== 'undefined' && selectIcloudTargetMailboxType) {
selectIcloudTargetMailboxType.disabled = disableIcloudControls;
}
if (typeof selectIcloudForwardMailProvider !== 'undefined' && selectIcloudForwardMailProvider) {
const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue);
const allowIcloudForwardMailProvider = isIcloudMailProvider()
&& normalizedIcloudTargetMailboxType === 'forward-mailbox';
selectIcloudForwardMailProvider.disabled = disableIcloudControls || !allowIcloudForwardMailProvider;
}
if (selectIcloudFetchMode) {
const allowIcloudFetchMode = getSelectedEmailGenerator() === ICLOUD_PROVIDER
&& !isCustomMailProvider()
@@ -5028,6 +5119,7 @@ selectEmailGenerator.addEventListener('change', () => {
});
selectIcloudHostPreference?.addEventListener('change', () => {
updateMailProviderUI();
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
if (getSelectedEmailGenerator() === 'icloud') {
@@ -5035,6 +5127,18 @@ selectIcloudHostPreference?.addEventListener('change', () => {
}
});
selectIcloudTargetMailboxType?.addEventListener('change', () => {
updateMailProviderUI();
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
selectIcloudForwardMailProvider?.addEventListener('change', () => {
updateMailProviderUI();
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
selectIcloudFetchMode?.addEventListener('change', () => {
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
@@ -5619,6 +5723,15 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
selectIcloudHostPreference.value = hostPreference === 'icloud.com'
? 'icloud.com'
: (hostPreference === 'icloud.com.cn' ? 'icloud.com.cn' : 'auto');
updateMailProviderUI();
}
if (message.payload.icloudTargetMailboxType !== undefined && selectIcloudTargetMailboxType) {
selectIcloudTargetMailboxType.value = normalizeIcloudTargetMailboxType(message.payload.icloudTargetMailboxType);
updateMailProviderUI();
}
if (message.payload.icloudForwardMailProvider !== undefined && selectIcloudForwardMailProvider) {
selectIcloudForwardMailProvider.value = normalizeIcloudForwardMailProvider(message.payload.icloudForwardMailProvider);
updateMailProviderUI();
}
if (message.payload.icloudFetchMode !== undefined && selectIcloudFetchMode) {
selectIcloudFetchMode.value = normalizeIcloudFetchMode(message.payload.icloudFetchMode);
+81 -68
View File
@@ -1,6 +1,11 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const {
getIcloudForwardMailConfig,
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = require('../mail-provider-utils.js');
const source = fs.readFileSync('background.js', 'utf8');
@@ -51,6 +56,41 @@ function extractFunction(name) {
return source.slice(start, end);
}
function createGetMailConfigApi() {
const bundle = extractFunction('getMailConfig');
return new Function('shared', `
const ICLOUD_PROVIDER = 'icloud';
const GMAIL_PROVIDER = 'gmail';
const HOTMAIL_PROVIDER = 'hotmail-api';
const LUCKMAIL_PROVIDER = 'luckmail-api';
const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
const getSharedIcloudForwardMailConfig = shared.getIcloudForwardMailConfig;
const normalizeIcloudTargetMailboxType = shared.normalizeIcloudTargetMailboxType;
const normalizeIcloudForwardMailProvider = shared.normalizeIcloudForwardMailProvider;
function normalizeIcloudHost(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
}
function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
function getConfiguredIcloudHostPreference(state) {
const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase();
return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
}
function getIcloudLoginUrlForHost(host) {
return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
}
function getIcloudMailUrlForHost(host) {
return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
}
${bundle}
return { getMailConfig };
`)({
getIcloudForwardMailConfig,
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
});
}
test('normalizeMailProvider keeps icloud provider', () => {
const bundle = extractFunction('normalizeMailProvider');
const api = new Function(`
@@ -69,97 +109,36 @@ return { normalizeMailProvider };
});
test('getMailConfig returns icloud mail tab config with host preference', () => {
const bundle = extractFunction('getMailConfig');
const api = new Function(`
const ICLOUD_PROVIDER = 'icloud';
const GMAIL_PROVIDER = 'gmail';
const HOTMAIL_PROVIDER = 'hotmail-api';
const LUCKMAIL_PROVIDER = 'luckmail-api';
const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
function normalizeIcloudHost(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
}
function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
function getConfiguredIcloudHostPreference(state) {
const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase();
return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
}
function getIcloudLoginUrlForHost(host) {
return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
}
function getIcloudMailUrlForHost(host) {
return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
}
${bundle}
return { getMailConfig };
`)();
const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
icloudHostPreference: 'icloud.com.cn',
icloudHostPreference: 'icloud.com',
}), {
source: 'icloud-mail',
url: 'https://www.icloud.com.cn/mail/',
url: 'https://www.icloud.com/mail/',
label: 'iCloud 邮箱',
navigateOnReuse: true,
});
});
test('getMailConfig reuses preferred icloud host when preference is auto', () => {
const bundle = extractFunction('getMailConfig');
const api = new Function(`
const ICLOUD_PROVIDER = 'icloud';
const GMAIL_PROVIDER = 'gmail';
const HOTMAIL_PROVIDER = 'hotmail-api';
const LUCKMAIL_PROVIDER = 'luckmail-api';
const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
function normalizeIcloudHost(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
}
function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
function getConfiguredIcloudHostPreference() {
return '';
}
function getIcloudLoginUrlForHost(host) {
return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
}
function getIcloudMailUrlForHost(host) {
return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
}
${bundle}
return { getMailConfig };
`)();
const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
icloudHostPreference: 'auto',
preferredIcloudHost: 'icloud.com.cn',
preferredIcloudHost: 'icloud.com',
}), {
source: 'icloud-mail',
url: 'https://www.icloud.com.cn/mail/',
url: 'https://www.icloud.com/mail/',
label: 'iCloud 邮箱',
navigateOnReuse: true,
});
});
test('getMailConfig keeps provider metadata for 2925 mailboxes', () => {
const bundle = extractFunction('getMailConfig');
const api = new Function(`
const ICLOUD_PROVIDER = 'icloud';
const GMAIL_PROVIDER = 'gmail';
const HOTMAIL_PROVIDER = 'hotmail-api';
const LUCKMAIL_PROVIDER = 'luckmail-api';
const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
function normalizeIcloudHost(value = '') { return String(value || '').trim().toLowerCase(); }
function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
function getConfiguredIcloudHostPreference() { return ''; }
function getIcloudLoginUrlForHost(host) { return host; }
function getIcloudMailUrlForHost(host) { return host; }
${bundle}
return { getMailConfig };
`)();
const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: '2925',
@@ -172,3 +151,37 @@ return { getMailConfig };
injectSource: 'mail-2925',
});
});
test('getMailConfig uses icloud inbox config when host is com.cn and target mailbox is icloud inbox', () => {
const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
icloudHostPreference: 'icloud.com.cn',
icloudTargetMailboxType: 'icloud-inbox',
icloudForwardMailProvider: 'gmail',
}), {
source: 'icloud-mail',
url: 'https://www.icloud.com.cn/mail/',
label: 'iCloud 邮箱',
navigateOnReuse: true,
});
});
test('getMailConfig uses forward mailbox config when target mailbox type is forward', () => {
const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
icloudHostPreference: 'icloud.com.cn',
icloudTargetMailboxType: 'forward-mailbox',
icloudForwardMailProvider: 'gmail',
}), {
source: 'gmail-mail',
url: 'https://mail.google.com/mail/u/0/#inbox',
label: 'iCloud 转发(Gmail 邮箱)',
inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
injectSource: 'gmail-mail',
icloudForwarding: true,
});
});
+14 -1
View File
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const {
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = require('../mail-provider-utils.js');
const source = fs.readFileSync('background.js', 'utf8');
@@ -155,6 +159,8 @@ function findIcloudAliasByEmail(aliases, email) {
function getErrorMessage(error) {
return String(typeof error === 'string' ? error : error?.message || '');
}
const normalizeIcloudTargetMailboxType = overrides.normalizeIcloudTargetMailboxType;
const normalizeIcloudForwardMailProvider = overrides.normalizeIcloudForwardMailProvider;
${bundle}
@@ -175,9 +181,16 @@ test('normalizeEmailGenerator and label support icloud', () => {
});
test('normalizePersistentSettingValue handles icloud settings', () => {
const api = createApi();
const api = createApi({
normalizeIcloudTargetMailboxType,
normalizeIcloudForwardMailProvider,
});
assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'icloud.com'), 'icloud.com');
assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'bad-host'), 'auto');
assert.equal(api.normalizePersistentSettingValue('icloudTargetMailboxType', 'forward-mailbox'), 'forward-mailbox');
assert.equal(api.normalizePersistentSettingValue('icloudTargetMailboxType', 'wrong'), 'icloud-inbox');
assert.equal(api.normalizePersistentSettingValue('icloudForwardMailProvider', 'GMAIL'), 'gmail');
assert.equal(api.normalizePersistentSettingValue('icloudForwardMailProvider', 'unknown'), 'qq');
assert.equal(api.normalizePersistentSettingValue('autoDeleteUsedIcloudAlias', 1), true);
assert.equal(api.normalizePersistentSettingValue('verificationResendCount', '6'), 6);
assert.equal(api.normalizePersistentSettingValue('verificationResendCount', 99), 20);
+30
View File
@@ -3,7 +3,11 @@ const assert = require('node:assert/strict');
const {
HOTMAIL_PROVIDER,
getIcloudForwardMailConfig,
getIcloudForwardMailProviderOptions,
getMailProviderConfig,
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
normalizeMailProvider,
} = require('../mail-provider-utils.js');
@@ -33,3 +37,29 @@ test('getMailProviderConfig preserves the hotmail provider sentinel', () => {
}
);
});
test('iCloud forward mailbox helpers normalize and expose supported providers', () => {
assert.equal(normalizeIcloudTargetMailboxType('forward-mailbox'), 'forward-mailbox');
assert.equal(normalizeIcloudTargetMailboxType('unknown'), 'icloud-inbox');
assert.equal(normalizeIcloudForwardMailProvider('GMAIL'), 'gmail');
assert.equal(normalizeIcloudForwardMailProvider('unknown'), 'qq');
assert.deepEqual(
getIcloudForwardMailProviderOptions().map((option) => option.value),
['qq', '163', '163-vip', '126', 'gmail']
);
});
test('getIcloudForwardMailConfig reuses shared mailbox provider configs', () => {
assert.deepEqual(getIcloudForwardMailConfig('126'), {
source: 'mail-163',
url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D',
label: '126 邮箱',
});
assert.deepEqual(getIcloudForwardMailConfig('gmail'), {
source: 'gmail-mail',
url: 'https://mail.google.com/mail/u/0/#inbox',
label: 'Gmail 邮箱',
inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
injectSource: 'gmail-mail',
});
});
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const {
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = require('../mail-provider-utils');
const source = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -172,7 +176,7 @@ return {
test('updateMailProviderUI keeps the temp domain selector visible and updates the hint when random subdomain is enabled', () => {
const bundle = extractFunction('updateMailProviderUI');
const api = new Function(`
const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = {
cloudflareTempEmailDomains: ['mail.example.com'],
};
@@ -254,7 +258,7 @@ return {
autoHintText,
calls,
};
`)();
`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
api.updateMailProviderUI();
assert.equal(api.cloudflareTempEmailSection.style.display, '');
+6 -2
View File
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const {
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -174,7 +178,7 @@ return { renderStepStatuses, rows, statusEls, stepsProgress };
test('collectSettingsPayload omits custom password and local sync settings in contribution mode', () => {
const bundle = extractFunction('collectSettingsPayload');
const api = new Function(`
const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = { contributionMode: true };
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
@@ -241,7 +245,7 @@ return {
collectSettingsPayload,
setLatestState(nextState) { latestState = nextState; },
};
`)();
`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
const contributionPayload = api.collectSettingsPayload();
assert.equal('panelMode' in contributionPayload, false);
+294
View File
@@ -79,3 +79,297 @@ return { getSelectedIcloudHostPreference, getMailProviderLoginUrl };
assert.equal(api.getSelectedIcloudHostPreference(), 'icloud.com.cn');
assert.equal(api.getMailProviderLoginUrl(), 'https://www.icloud.com.cn/');
});
test('collectSettingsPayload persists icloud target mailbox settings', () => {
const bundle = extractFunction('collectSettingsPayload');
const api = new Function(`
let latestState = { contributionMode: false };
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
const selectCfDomain = { value: '' };
const selectTempEmailDomain = { value: '' };
const selectPanelMode = { value: 'cpa' };
const inputVpsUrl = { value: '' };
const inputVpsPassword = { value: '' };
const inputSub2ApiUrl = { value: '' };
const inputSub2ApiEmail = { value: '' };
const inputSub2ApiPassword = { value: '' };
const inputSub2ApiGroup = { value: '' };
const inputSub2ApiDefaultProxy = { value: '' };
const inputCodex2ApiUrl = { value: '' };
const inputCodex2ApiAdminKey = { value: '' };
const inputPassword = { value: '' };
const selectMailProvider = { value: 'icloud' };
const selectEmailGenerator = { value: 'duck' };
const checkboxAutoDeleteIcloud = { checked: false };
const selectIcloudHostPreference = { value: 'auto' };
const selectIcloudFetchMode = { value: 'reuse_existing' };
const selectIcloudTargetMailboxType = { value: 'forward-mailbox' };
const selectIcloudForwardMailProvider = { value: 'gmail' };
const inputPhoneVerificationEnabled = { checked: false };
const inputAccountRunHistoryTextEnabled = { checked: false };
const inputAccountRunHistoryHelperBaseUrl = { value: '' };
const inputInbucketHost = { value: '' };
const inputInbucketMailbox = { value: '' };
const inputHotmailRemoteBaseUrl = { value: '' };
const inputHotmailLocalBaseUrl = { value: '' };
const inputLuckmailApiKey = { value: '' };
const inputLuckmailBaseUrl = { value: '' };
const selectLuckmailEmailType = { value: 'ms_graph' };
const inputLuckmailDomain = { value: '' };
const inputTempEmailBaseUrl = { value: '' };
const inputTempEmailAdminAuth = { value: '' };
const inputTempEmailCustomAuth = { value: '' };
const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' };
const inputVerificationResendCount = { value: '4' };
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; }
function normalizeCloudflareDomainValue(value) { return String(value || '').trim(); }
function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; }
function normalizeCloudflareTempEmailDomainValue(value) { return String(value || '').trim(); }
function getSelectedLocalCpaStep9Mode() { return 'submit'; }
function getSelectedMail2925Mode() { return 'provide'; }
function buildManagedAliasBaseEmailPayload() { return { gmailBaseEmail: '', mail2925BaseEmail: '', emailPrefix: '' }; }
function getSelectedHotmailServiceMode() { return 'local'; }
function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
${bundle}
return { collectSettingsPayload };
`)();
const payload = api.collectSettingsPayload();
assert.equal(payload.icloudTargetMailboxType, 'forward-mailbox');
assert.equal(payload.icloudForwardMailProvider, 'gmail');
});
test('updateMailProviderUI toggles icloud forward mailbox controls and hint', () => {
const bundle = extractFunction('updateMailProviderUI');
const createRow = (display = 'none') => ({ style: { display } });
const api = new Function('createRow', `
let latestState = { icloudHostPreference: 'icloud.com.cn' };
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
const ICLOUD_PROVIDER = 'icloud';
const GMAIL_PROVIDER = 'gmail';
const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
const LUCKMAIL_PROVIDER = 'luckmail-api';
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
const HOTMAIL_SERVICE_MODE_REMOTE = 'remote';
const HOTMAIL_SERVICE_MODE_LOCAL = 'local';
const rowMail2925Mode = createRow();
const rowMail2925PoolSettings = createRow();
const rowEmailPrefix = createRow();
const rowCustomMailProviderPool = createRow();
const rowInbucketHost = createRow();
const rowInbucketMailbox = createRow();
const rowEmailGenerator = createRow();
const rowCfDomain = createRow();
const rowTempEmailBaseUrl = createRow();
const rowTempEmailAdminAuth = createRow();
const rowTempEmailCustomAuth = createRow();
const rowTempEmailReceiveMailbox = createRow();
const rowTempEmailRandomSubdomainToggle = createRow();
const rowTempEmailDomain = createRow();
const cloudflareTempEmailSection = createRow();
const hotmailSection = createRow();
const mail2925Section = createRow();
const luckmailSection = createRow();
const icloudSection = createRow();
const rowIcloudTargetMailboxType = createRow();
const rowIcloudForwardMailProvider = createRow();
const labelEmailPrefix = { textContent: '' };
const inputEmailPrefix = { placeholder: '', style: { display: '' }, readOnly: false };
const labelMail2925UseAccountPool = createRow();
const selectMail2925PoolAccount = { style: { display: 'none' }, disabled: false };
const btnFetchEmail = { hidden: false, disabled: false, textContent: '' };
const btnMailLogin = { disabled: false, textContent: '', title: '' };
const inputEmail = { readOnly: false, placeholder: '', value: '' };
const autoHintText = { textContent: '' };
const rowHotmailServiceMode = createRow();
const rowHotmailRemoteBaseUrl = createRow();
const rowHotmailLocalBaseUrl = createRow();
const inputMail2925UseAccountPool = { checked: false };
const selectMailProvider = { value: 'icloud' };
const selectEmailGenerator = { value: 'duck', disabled: false, options: [] };
const selectIcloudTargetMailboxType = { value: 'icloud-inbox' };
const selectIcloudForwardMailProvider = { value: 'gmail' };
const selectIcloudHostPreference = { value: 'icloud.com.cn' };
const inputTempEmailUseRandomSubdomain = { checked: false };
const inputRunCount = { disabled: false };
const currentAutoRun = { autoRunning: false };
const MAIL_PROVIDER_LOGIN_CONFIGS = { gmail: { label: 'Gmail 邮箱' } };
const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = { gmail: 'Gmail 邮箱' };
function normalizeIcloudHost(value) { return String(value || '').trim().toLowerCase(); }
function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
function getSelectedIcloudHostPreference() { return selectIcloudHostPreference.value; }
function isLuckmailProvider() { return false; }
function isCustomMailProvider() { return false; }
function isIcloudMailProvider() { return selectMailProvider.value === ICLOUD_PROVIDER; }
function usesCustomMailProviderPool() { return false; }
function usesGeneratedAliasMailProvider() { return false; }
function getSelectedMail2925Mode() { return 'provide'; }
function getManagedAliasProviderUiCopy() { return null; }
function getCurrentRegistrationEmailUiCopy() { return { buttonLabel: '获取邮箱', placeholder: '邮箱', label: '邮箱' }; }
function updateMailLoginButtonState() {}
function getSelectedHotmailServiceMode() { return 'local'; }
function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; }
function setCloudflareDomainEditMode() {}
function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; }
function setCloudflareTempEmailDomainEditMode() {}
function queueIcloudAliasRefresh() {}
function hideIcloudLoginHelp() {}
function syncMail2925PoolAccountOptions() {}
function getMail2925Accounts() { return []; }
function renderHotmailAccounts() {}
function renderMail2925Accounts() {}
function renderLuckmailPurchases() {}
function getSelectedEmailGenerator() { return selectEmailGenerator.value; }
function isAutoRunLockedPhase() { return false; }
function getCurrentHotmailEmail() { return ''; }
function getCurrentLuckmailEmail() { return ''; }
function getCustomEmailPoolSize() { return 0; }
function getCustomMailProviderPoolSize() { return 0; }
function syncRunCountFromCustomEmailPool() {}
function syncRunCountFromCustomMailProviderPool() {}
function shouldLockRunCountToEmailPool() { return false; }
${bundle}
return {
updateMailProviderUI,
rowIcloudTargetMailboxType,
rowIcloudForwardMailProvider,
selectIcloudTargetMailboxType,
autoHintText,
};
`)(createRow);
api.updateMailProviderUI();
assert.equal(api.rowIcloudTargetMailboxType.style.display, '');
assert.equal(api.rowIcloudForwardMailProvider.style.display, 'none');
api.selectIcloudTargetMailboxType.value = 'forward-mailbox';
api.updateMailProviderUI();
assert.equal(api.rowIcloudForwardMailProvider.style.display, '');
assert.match(api.autoHintText.textContent, /Gmail 邮箱/);
});
test('applySettingsState restores icloud forward mailbox settings before UI refresh', () => {
const bundle = extractFunction('applySettingsState');
const calls = [];
const api = new Function('calls', `
let latestState = {};
const inputEmail = { value: '' };
const inputVpsUrl = { value: '' };
const inputVpsPassword = { value: '' };
const selectPanelMode = { value: 'cpa' };
const inputSub2ApiUrl = { value: '' };
const inputSub2ApiEmail = { value: '' };
const inputSub2ApiPassword = { value: '' };
const inputSub2ApiGroup = { value: '' };
const inputSub2ApiDefaultProxy = { value: '' };
const inputCodex2ApiUrl = { value: '' };
const inputCodex2ApiAdminKey = { value: '' };
const ICLOUD_PROVIDER = 'icloud';
const GMAIL_PROVIDER = 'gmail';
const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
const selectMailProvider = { value: '163' };
const selectEmailGenerator = { value: 'duck' };
const selectIcloudHostPreference = { value: 'auto' };
const selectIcloudFetchMode = { value: 'reuse_existing' };
const selectIcloudTargetMailboxType = { value: 'icloud-inbox' };
const selectIcloudForwardMailProvider = { value: 'qq' };
const checkboxAutoDeleteIcloud = { checked: false };
const inputAccountRunHistoryHelperBaseUrl = { value: '' };
const inputContributionNickname = { value: '' };
const inputContributionQq = { value: '' };
const inputMail2925UseAccountPool = { checked: false };
const inputInbucketHost = { value: '' };
const inputInbucketMailbox = { value: '' };
const inputCustomMailProviderPool = { value: '' };
const inputCustomEmailPool = { value: '' };
const inputHotmailRemoteBaseUrl = { value: '' };
const inputHotmailLocalBaseUrl = { value: '' };
const inputLuckmailApiKey = { value: '' };
const inputLuckmailBaseUrl = { value: '' };
const selectLuckmailEmailType = { value: 'ms_graph' };
const inputLuckmailDomain = { value: '' };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '' };
const inputAutoStepDelaySeconds = { value: '' };
const inputVerificationResendCount = { value: '' };
const inputPhoneVerificationEnabled = { checked: false };
const DEFAULT_PHONE_VERIFICATION_ENABLED = false;
const inputHeroSmsApiKey = { value: '' };
const selectHeroSmsCountry = { value: '52', options: [{ value: '52' }] };
const inputRunCount = { value: '' };
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
function syncLatestState(state) { latestState = { ...latestState, ...state }; }
function syncAutoRunState() {}
function syncPasswordField() {}
function setLocalCpaStep9Mode() {}
function isCustomMailProvider() { return false; }
function setMail2925Mode() {}
function normalizeIcloudFetchMode(value) { return String(value || '') === 'always_new' ? 'always_new' : 'reuse_existing'; }
function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function setManagedAliasBaseEmailInputForProvider() {}
function normalizeCustomEmailPoolEntries(value) { return Array.isArray(value) ? value : []; }
function setHotmailServiceMode() {}
function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
function applyCloudflareTempEmailSettingsState() {}
function renderCloudflareDomainOptions() {}
function setCloudflareDomainEditMode() {}
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizeHeroSmsCountryId() { return 52; }
function getSelectedHeroSmsCountryOption() { return { label: 'Thailand' }; }
function updateHeroSmsPlatformDisplay() {}
function applyAutoRunStatus() {}
function markSettingsDirty() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {}
function updateAccountRunHistorySettingsUI() {}
function updatePhoneVerificationSettingsUI() {}
function updatePanelModeUI() {}
function updateMailProviderUI() { calls.push({ target: selectIcloudTargetMailboxType.value, provider: selectIcloudForwardMailProvider.value }); }
function isLuckmailProvider() { return false; }
function updateButtonStates() {}
${bundle}
return { applySettingsState, selectIcloudTargetMailboxType, selectIcloudForwardMailProvider };
`)(calls);
api.applySettingsState({
mailProvider: 'icloud',
icloudTargetMailboxType: 'forward-mailbox',
icloudForwardMailProvider: 'gmail',
});
assert.equal(api.selectIcloudTargetMailboxType.value, 'forward-mailbox');
assert.equal(api.selectIcloudForwardMailProvider.value, 'gmail');
assert.deepEqual(calls.at(-1), { target: 'forward-mailbox', provider: 'gmail' });
});
+6 -2
View File
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const {
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -148,7 +152,7 @@ test('collectSettingsPayload persists currentMail2925AccountId for 2925 account
extractFunction('collectSettingsPayload'),
].join('\n');
const api = new Function(`
const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = {
contributionMode: false,
mail2925UseAccountPool: true,
@@ -222,7 +226,7 @@ function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Num
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
${bundle}
return { collectSettingsPayload };
`)();
`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
const payload = api.collectSettingsPayload();
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const {
normalizeIcloudForwardMailProvider,
normalizeIcloudTargetMailboxType,
} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -90,7 +94,7 @@ return {
});
test('collectSettingsPayload keeps local helper sync enabled while persisting sms toggle state', () => {
const api = new Function(`
const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = {
contributionMode: false,
mail2925UseAccountPool: false,
@@ -168,7 +172,7 @@ ${extractFunction('normalizeHeroSmsCountryLabel')}
${extractFunction('getSelectedHeroSmsCountryOption')}
${extractFunction('collectSettingsPayload')}
return { collectSettingsPayload };
`)();
`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
const payload = api.collectSettingsPayload();
+16 -1
View File
@@ -169,7 +169,7 @@
- 2925 当前选中的号池账号 ID `currentMail2925AccountId`
- Cloudflare / Temp Email 设置
- 接码开关,以及 HeroSMS 的 API Key 与默认国家设置
- iCloud 相关偏好
- iCloud 相关偏好:Host、获取策略、成功后自动删除、目标邮箱类型与转发收码邮箱 provider
- LuckMail API 配置
- 自动运行默认配置
- 账号运行历史 `accountRunHistory`(以邮箱为主键,保存该邮箱最近一次状态:成功/失败/停止)
@@ -639,6 +639,7 @@ Plus 模式可见步骤:
- `163``163 VIP``126` 都走同一条“网易网页邮箱”验证码链路。
- sidepanel 只负责切换 provider 与展示登录入口;后台根据 provider 选择对应网页邮箱首页。
- 内容脚本来源统一归类到 `mail-163`,这样 Step 4 / Step 8 继续复用同一套验证码读取与邮件清理逻辑。
- `mail-provider-utils.js` 同时承接 iCloud 转发收码可选 provider 的归一化与入口配置,避免 background / sidepanel 重新复制 QQ、网易和 Gmail 的收码地址、label 与注入脚本。
- `manifest.json` 需要同时覆盖:
- `https://mail.163.com/*`
- `https://webmail.vip.163.com/*`
@@ -733,8 +734,22 @@ Plus 模式可见步骤:
组成:
- [icloud-utils.js](c:/Users/projectf/Downloads/codex注册扩展/icloud-utils.js)
- [mail-provider-utils.js](c:/Users/projectf/Downloads/codex注册扩展/mail-provider-utils.js)
- [content/icloud-mail.js](c:/Users/projectf/Downloads/codex注册扩展/content/icloud-mail.js)
配置:
- `icloudHostPreference` 只决定 iCloud 登录、别名管理和 iCloud Mail 收件箱 Host。
- `icloudFetchMode` 决定生成注册邮箱时复用已有 Hide My Email 别名,还是始终创建新别名。
- `icloudTargetMailboxType = icloud-inbox` 时,Step 4 / Step 8 直接打开 iCloud Mail 收件箱轮询验证码。
- `icloudTargetMailboxType = forward-mailbox` 时,注册邮箱仍由 iCloud Hide My Email 生成,但 Step 4 / Step 8 改为打开 `icloudForwardMailProvider` 指定的转发目标邮箱收码;当前支持 `qq / 163 / 163-vip / 126 / gmail`
维护约定:
- iCloud 转发目标邮箱 provider 的 label、URL、source、inject 配置统一由 `mail-provider-utils.js` 输出。
- `background.js` 只根据 `icloudTargetMailboxType` 选择 iCloud Mail 收件箱或共享转发邮箱配置,不再在主文件内硬编码各 provider 地址。
- `sidepanel/sidepanel.js` 只负责展示、保存和回显目标邮箱类型 / 转发邮箱 provider,不重复定义 provider 业务清单。
## 8. 自动运行完整链路
文件:
+27 -6
View File
@@ -26,7 +26,7 @@
- `hotmail-utils.js`:Hotmail 账号与验证码提取相关的纯工具函数,负责账号筛选、验证码匹配、第三方接口数据归一化。
- `icloud-utils.js`:iCloud 隐私邮箱相关的纯工具函数,负责 host、别名列表、保留状态、已用状态等归一化。
- `luckmail-utils.js`:LuckMail 相关的纯工具函数,负责邮箱购买记录、标签、邮件 cursor、验证码匹配等归一化。
- `mail-provider-utils.js`:网页邮箱 provider 配置纯工具,负责 `163 / 163 VIP / 126 / QQ / Inbucket / Hotmail` 的基础归一化与页面入口配置。
- `mail-provider-utils.js`:网页邮箱 provider 配置纯工具,负责 `163 / 163 VIP / 126 / QQ / Inbucket / Hotmail` 的基础归一化与页面入口配置,并统一承接 iCloud 转发收码目标邮箱 provider 的归一化、选项列表和收码入口配置
- `mail2925-utils.js`:2925 账号池相关的纯工具函数,负责账号归一化、冷却期判断、可用账号挑选、批量导入解析与列表更新。
- `managed-alias-utils.js`:共享 Gmail / 2925 的别名邮箱规则,负责解析基邮箱、校验“当前完整注册邮箱”是否仍与基邮箱兼容、生成 Gmail `+tag` 与 2925 随机后缀邮箱,并输出 sidepanel 可复用的 UI 文案;当前还统一承接 `mail2925Mode` 的归一化与“2925 仅在 provide 模式下参与别名生成”的共享判定。
- `manifest.json`:Chrome 扩展清单,声明权限、背景脚本、侧边栏、内容脚本与规则集。
@@ -132,8 +132,29 @@
- `sidepanel/contribution-content-update-service.js`:侧边栏贡献内容更新服务,负责拉取 `https://apikey.qzz.io/api/content-summary`、缓存公开公告/教程摘要,并输出可用于提示展示的 `promptVersion` 与最新更新时间。
- `sidepanel/contribution-mode.js`:侧边栏贡献模式管理器,负责顶部“贡献”按钮、确认弹窗、贡献模式显隐、复用主自动流程启动、侧栏内贡献状态轮询、上传页跳转,以及贡献模式下对来源选择、配置入口、记录入口和敏感配置行(包括 Codex2API 配置)的禁用与隐藏。
- `sidepanel/sidepanel.css`:侧边栏样式文件;当前额外提供 Hotmail / 2925 共用的号池表单容器、操作按钮行与统一的收起态列表高度样式。
- `sidepanel/sidepanel.html`:侧边栏页面结构;当前步骤列表已改为动态容器,日志区提供“记录”按钮并挂接邮箱记录覆盖层,顶部新增“贡献”按钮并在设置卡片中新增贡献模式主面板;贡献按钮下方额外挂接一个可关闭的轻提示气泡,用于提示公开公告 / 使用教程有更新;贡献面板内展示 `OAUTH / 回调 / 总状态` 三块真实运行态信息,同时把“接码”开关与“验证码重发”拆成同一行展示;只有开启接码后,下面的 HeroSMS 平台、国家与 API Key 行才会显示;页面继续加载 `managed-alias-utils.js`,并把旧的“邮箱前缀”字段语义改为“别名基邮箱”;当 provider 为 2925 时,会额外显示 `提供邮箱 / 接收邮箱` 模式切换,并把“2925 号池”从别名基邮箱行拆成独立配置行,避免 receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱` 时额外显示 `自定义号池` 文本框;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密钥配置行;设置卡片新增 `Plus 模式` 开关与 PayPal 账号/密码输入行;Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器。
- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 邮箱记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启动逻辑,而独立 manager 负责贡献运行态展示与轮询,同时把 Gmail / 2925 的基邮箱输入、完整注册邮箱输入、自动生成按钮与兼容性校验统一接到共享别名逻辑上;当 provider 为 2925 时,会根据 `mail2925Mode` 决定是否启用别名基邮箱链路,而 2925 号池开关与当前账号选择则独立显示并同时服务于 provide / receive 两种模式;`自定义邮箱池` 模式会按邮箱池长度锁定自动轮数,并把当前输入的邮箱池配置参与自动启动前保存;`邮箱服务 = 自定义邮箱` 时,如果配置了 `customMailProviderPool`,也会按号池长度锁定自动轮数并在 Auto 中按轮次分配注册邮箱,同时在普通失败时继续复用当前邮箱,只有成功或出现手机号验证时才切换下一个邮箱;Plus 模式开启后,步骤列表会切换为 12 步,并显示 PayPal 凭据配置;Step 8 的自定义邮箱确认弹窗当前额外提供“出现手机号验证”按钮,用于直接走与真实 add-phone 一致的 fatal 分支;HeroSMS 国家列表会在 sidepanel 初始化时拉取并恢复到本地保存的国家/API 设置,接码开关关闭时会直接隐藏相关配置;账号记录快照同步改为默认自动模式,只要本地 helper 可用就会自动落盘;新来源 Codex2API 在这里仅补充来源配置接线、表单显隐和 Step 10 按钮文案,不承接协议业务逻辑;Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;侧边栏初始化与点击“自动”前会刷新一次贡献站公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示。
- `sidepanel/sidepanel.html`:侧边栏页面结构;当前步骤列表已改为动态容器,日志区提供“记录”按钮并挂接邮箱记录覆盖层,顶部新增“贡
献”按钮并在设置卡片中新增贡献模式主面板;贡献按钮下方额外挂接一个可关闭的轻提示气泡,用于提示公开公告 / 使用教程有更新;贡献面板内
展示 `OAUTH / 回调 / 总状态` 三块真实运行态信息,同时把“接码”开关与“验证码重发”拆成同一行展示;只有开启接码后,下面的 HeroSMS 平
台、国家与 API Key 行才会显示;页面继续加载 `managed-alias-utils.js``mail-provider-utils.js`,并把旧的“邮箱前缀”字段语义改为“别
名基邮箱”;当 provider 为 2925 时,会额外显示 `提供邮箱 / 接收邮箱` 模式切换,并把“2925 号池”从别名基邮箱行拆成独立配置行,避免
receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱`
额外显示 `自定义号池` 文本框;当邮箱服务为 iCloud 时,额外提供目标邮箱类型与转发邮箱 provider 配置,用于选择直接从 iCloud 收件箱收
码或从 QQ / 网易 / Gmail 转发目标邮箱收码;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密
钥配置行;设置卡片新增 `Plus 模式` 开关与 PayPal 账号/密码输入行;Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添
加”按钮和共享表单容器。
- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装
配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 邮箱记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启
动逻辑,而独立 manager 负责贡献运行态展示与轮询,同时把 Gmail / 2925 的基邮箱输入、完整注册邮箱输入、自动生成按钮与兼容性校验统一接
到共享别名逻辑上;当 provider 为 2925 时,会根据 `mail2925Mode` 决定是否启用别名基邮箱链路,而 2925 号池开关与当前账号选择则独立显
示并同时服务于 provide / receive 两种模式;当 provider 为 iCloud 时,会保存并回显目标邮箱类型与转发邮箱 provider,相关 provider 选
项和 label 复用 `mail-provider-utils.js``自定义邮箱池` 模式会按邮箱池长度锁定自动轮数,并把当前输入的邮箱池配置参与自动启动前保
存;`邮箱服务 = 自定义邮箱` 时,如果配置了 `customMailProviderPool`,也会按号池长度锁定自动轮数并在 Auto 中按轮次分配注册邮箱,同时
在普通失败时继续复用当前邮箱,只有成功或出现手机号验证时才切换下一个邮箱;Plus 模式开启后,步骤列表会切换为 12 步,并显示 PayPal 凭
据配置;Step 8 的自定义邮箱确认弹窗当前额外提供“出现手机号验证”按钮,用于直接走与真实 add-phone 一致的 fatal 分支;HeroSMS 国家列表
会在 sidepanel 初始化时拉取并恢复到本地保存的国家/API 设置,接码开关关闭时会直接隐藏相关配置;账号记录快照同步改为默认自动模式,只
要本地 helper 可用就会自动落盘;新来源 Codex2API 在这里仅补充来源配置接线、表单显隐和 Step 10 按钮文案,不承接协议业务逻辑;
Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;侧边栏初始化与点击“自动”前会刷新一次贡献站
公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示。
- `sidepanel/update-service.js`:侧边栏更新检查服务,负责 GitHub Releases 查询、`Pro` / `v` 双版本族排序、缓存读取与版本展示。
## `tests/`
@@ -154,7 +175,7 @@
- `tests/background-custom-email-pool.test.js`:测试后台对自定义邮箱池生成方式,以及自定义邮箱服务号池的归一化、标签文案和按轮次取邮箱逻辑。
- `tests/background-generated-email-module.test.js`:测试生成邮箱辅助模块已接入且导出工厂,并覆盖 2925 receive 模式回退普通邮箱生成、自定义邮箱池按轮次取值、2925 provide 模式账号池预热,以及 iCloud `always_new` 传参。
- `tests/background-icloud.test.js`:测试 iCloud 相关后台纯函数与别名收尾逻辑。
- `tests/background-icloud-mail-provider.test.js`:测试 iCloud 邮箱 provider 配置解析。
- `tests/background-icloud-mail-provider.test.js`:测试 iCloud 邮箱 provider 配置解析,并覆盖转发收码模式复用共享 provider 配置
- `tests/background-logging-status-module.test.js`:测试日志 / 状态模块已接入且导出工厂。
- `tests/background-luckmail.test.js`:测试 LuckMail 相关后台逻辑,如购买、复用、标记已用与重置。
- `tests/background-mail2925-session-module.test.js`:测试 2925 会话模块已接入且导出工厂,并覆盖命中上限后“禁用 24 小时 + 切下一个号 + 自动登录”的核心链路。
@@ -185,7 +206,7 @@
- `tests/icloud-mail-content.test.js`:测试 iCloud Mail 内容脚本读取邮件正文和选中状态逻辑。
- `tests/icloud-utils.test.js`:测试 iCloud 工具层的 host、别名列表、保留状态与筛选逻辑。
- `tests/luckmail-utils.test.js`:测试 LuckMail 工具层的购买记录、邮件、游标和验证码匹配逻辑。
- `tests/mail-provider-utils.test.js`:测试网页邮箱 provider 配置工具对 `126` 等 provider 的归一化与入口配置解析。
- `tests/mail-provider-utils.test.js`:测试网页邮箱 provider 配置工具对 `126` 等 provider 的归一化与入口配置解析,以及 iCloud 转发收码 provider 选项、归一化和配置复用
- `tests/mail-2925-content.test.js`:测试 2925 邮箱内容脚本的轮询快照、忽略邮箱对比、按步骤会话隔离已试验证码、单封邮件删除与整箱删除逻辑。
- `tests/mail2925-utils.test.js`:测试 2925 账号池工具层的账号归一化、冷却期判断、可用账号挑选与批量导入解析。
- `tests/managed-alias-utils.test.js`:覆盖 Gmail / 2925 共享别名工具的解析、生成、兼容性判断。
@@ -198,7 +219,7 @@
- `tests/sidepanel-contribution-mode.test.js`:测试侧边栏贡献模式的 HTML 接线、runtime-only 设置保护,以及贡献模式 manager 复用主自动流启动、状态轮询和退出清理逻辑。
- `tests/sidepanel-auto-run-content-refresh.test.js`:测试点击“自动”时会先刷新贡献内容更新摘要,且刷新失败不会阻塞自动流程启动。
- `tests/sidepanel-icloud-manager.test.js`:测试侧边栏 iCloud 管理器模块接线与空态渲染。
- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析逻辑
- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析、目标邮箱类型 / 转发邮箱 provider 保存、回显和显隐联动
- `tests/sidepanel-luckmail-manager.test.js`:测试侧边栏 LuckMail 管理器模块接线与空态渲染。
- `tests/sidepanel-mail2925-manager.test.js`:测试侧边栏 2925 管理器模块接线、共享号池表单脚本加载顺序、显隐交互与空态渲染。
- `tests/sidepanel-mail2925-mode.test.js`:测试侧边栏保留 `2925 provide / receive` 模式行与独立的号池配置行,并验证 sidepanel 只在 provide 模式下把 2925 视为别名邮箱 provider。