Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
@@ -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 邮箱验证码轮询
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -114,6 +114,9 @@ const VERIFICATION_CODE_INPUT_SELECTOR = [
|
||||
].join(', ');
|
||||
|
||||
const ONE_TIME_CODE_LOGIN_PATTERN = /使用一次性验证码登录|改用(?:一次性)?验证码(?:登录)?|使用验证码登录|一次性验证码|验证码登录|one[-\s]*time\s*(?:passcode|password|code)|use\s+(?:a\s+)?one[-\s]*time\s*(?:passcode|password|code)(?:\s+instead)?|use\s+(?:a\s+)?code(?:\s+instead)?|sign\s+in\s+with\s+(?:email|code)|email\s+(?:me\s+)?(?:a\s+)?code/i;
|
||||
const LOGIN_ENTRY_ACTION_PATTERN = /(?:^|\b)(?:log\s*in|sign\s*in|continue\s+(?:with|using)\s+(?:email|chatgpt)|use\s+(?:an?\s+)?email|email\s+address)(?:\b|$)|登录|登陆|邮箱|电子邮件/i;
|
||||
const LOGIN_EXTERNAL_IDP_PATTERN = /google|microsoft|apple|sso|single\s+sign[-\s]*on|企业|工作区|workspace/i;
|
||||
const LOGIN_CODE_ONLY_ACTION_PATTERN = /one[-\s]*time|passcode|use\s+(?:a\s+)?code|验证码|一次性/i;
|
||||
|
||||
const RESEND_VERIFICATION_CODE_PATTERN = /重新发送(?:验证码)?|再次发送(?:验证码)?|重发(?:验证码)?|未收到(?:验证码|邮件)|resend(?:\s+code)?|send\s+(?:a\s+)?new\s+code|send\s+(?:it\s+)?again|request\s+(?:a\s+)?new\s+code|didn'?t\s+receive/i;
|
||||
|
||||
@@ -1719,12 +1722,32 @@ function getLoginSubmitButton({ allowDisabled = false } = {}) {
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function findLoginEntryTrigger() {
|
||||
const candidates = Array.from(document.querySelectorAll(
|
||||
'button, a, [role="button"], [role="link"], input[type="button"], input[type="submit"]'
|
||||
)).filter((el) => isVisibleElement(el) && isActionEnabled(el));
|
||||
|
||||
const preferred = candidates.find((el) => {
|
||||
const text = getActionText(el);
|
||||
if (!text || LOGIN_CODE_ONLY_ACTION_PATTERN.test(text) || LOGIN_EXTERNAL_IDP_PATTERN.test(text)) return false;
|
||||
return /continue\s+(?:with|using)\s+email|use\s+(?:an?\s+)?email|email\s+address|邮箱|电子邮件/i.test(text);
|
||||
});
|
||||
if (preferred) return preferred;
|
||||
|
||||
return candidates.find((el) => {
|
||||
const text = getActionText(el);
|
||||
if (!text || LOGIN_CODE_ONLY_ACTION_PATTERN.test(text) || LOGIN_EXTERNAL_IDP_PATTERN.test(text)) return false;
|
||||
return LOGIN_ENTRY_ACTION_PATTERN.test(text);
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function inspectLoginAuthState() {
|
||||
const retryState = getLoginTimeoutErrorPageState();
|
||||
const verificationTarget = getVerificationCodeTarget();
|
||||
const passwordInput = getLoginPasswordInput();
|
||||
const emailInput = getLoginEmailInput();
|
||||
const switchTrigger = findOneTimeCodeLoginTrigger();
|
||||
const loginEntryTrigger = findLoginEntryTrigger();
|
||||
const submitButton = getLoginSubmitButton({ allowDisabled: true });
|
||||
const verificationVisible = isVerificationPageStillVisible();
|
||||
const addPhonePage = isAddPhonePageReady();
|
||||
@@ -1746,6 +1769,7 @@ function inspectLoginAuthState() {
|
||||
emailInput,
|
||||
submitButton,
|
||||
switchTrigger,
|
||||
loginEntryTrigger,
|
||||
verificationVisible,
|
||||
addPhonePage,
|
||||
phoneVerificationPage,
|
||||
@@ -1810,6 +1834,13 @@ function inspectLoginAuthState() {
|
||||
};
|
||||
}
|
||||
|
||||
if (loginEntryTrigger) {
|
||||
return {
|
||||
...baseState,
|
||||
state: 'entry_page',
|
||||
};
|
||||
}
|
||||
|
||||
return baseState;
|
||||
}
|
||||
|
||||
@@ -1828,6 +1859,7 @@ function serializeLoginAuthState(snapshot) {
|
||||
hasEmailInput: Boolean(snapshot?.emailInput),
|
||||
hasSubmitButton: Boolean(snapshot?.submitButton),
|
||||
hasSwitchTrigger: Boolean(snapshot?.switchTrigger),
|
||||
hasLoginEntryTrigger: Boolean(snapshot?.loginEntryTrigger),
|
||||
verificationVisible: Boolean(snapshot?.verificationVisible),
|
||||
addPhonePage: Boolean(snapshot?.addPhonePage),
|
||||
phoneVerificationPage: Boolean(snapshot?.phoneVerificationPage),
|
||||
@@ -1849,6 +1881,8 @@ function getLoginAuthStateLabel(snapshot) {
|
||||
return '登录超时报错页';
|
||||
case 'oauth_consent_page':
|
||||
return 'OAuth 授权页';
|
||||
case 'entry_page':
|
||||
return '登录入口页';
|
||||
case 'add_phone_page':
|
||||
return '手机号页';
|
||||
default:
|
||||
@@ -2816,6 +2850,71 @@ async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeou
|
||||
return transition;
|
||||
}
|
||||
|
||||
async function waitForLoginEntryOpenTransition(timeout = 10000) {
|
||||
const start = Date.now();
|
||||
let snapshot = normalizeStep6Snapshot(inspectLoginAuthState());
|
||||
|
||||
while (Date.now() - start < timeout) {
|
||||
throwIfStopped();
|
||||
snapshot = normalizeStep6Snapshot(inspectLoginAuthState());
|
||||
if (snapshot.state !== 'unknown' && snapshot.state !== 'entry_page') {
|
||||
return snapshot;
|
||||
}
|
||||
await sleep(250);
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
async function step6OpenLoginEntry(payload, snapshot) {
|
||||
const currentSnapshot = normalizeStep6Snapshot(snapshot || inspectLoginAuthState());
|
||||
const trigger = currentSnapshot.loginEntryTrigger || findLoginEntryTrigger();
|
||||
if (!trigger || !isActionEnabled(trigger)) {
|
||||
return createStep6RecoverableResult('missing_login_entry_trigger', currentSnapshot, {
|
||||
message: '当前登录入口页没有可点击的邮箱登录入口。',
|
||||
});
|
||||
}
|
||||
|
||||
log(`步骤 7:检测到登录入口页,正在点击 "${getActionText(trigger).slice(0, 80)}"...`);
|
||||
await humanPause(350, 900);
|
||||
simulateClick(trigger);
|
||||
const nextSnapshot = await waitForLoginEntryOpenTransition();
|
||||
|
||||
if (nextSnapshot.state === 'email_page') {
|
||||
return step6LoginFromEmailPage(payload, nextSnapshot);
|
||||
}
|
||||
if (nextSnapshot.state === 'password_page') {
|
||||
return step6LoginFromPasswordPage(payload, nextSnapshot);
|
||||
}
|
||||
if (nextSnapshot.state === 'verification_page') {
|
||||
return finalizeStep6VerificationReady({
|
||||
logLabel: '步骤 7 收尾',
|
||||
loginVerificationRequestedAt: null,
|
||||
via: 'entry_open_verification_page',
|
||||
});
|
||||
}
|
||||
if (nextSnapshot.state === 'oauth_consent_page') {
|
||||
return createStep6OAuthConsentSuccessResult(nextSnapshot, {
|
||||
via: 'entry_open_oauth_consent_page',
|
||||
});
|
||||
}
|
||||
if (nextSnapshot.state === 'login_timeout_error_page') {
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_after_entry_open',
|
||||
nextSnapshot,
|
||||
'点击登录入口后进入登录超时报错页。'
|
||||
);
|
||||
if (transition.action === 'done') return transition.result;
|
||||
if (transition.action === 'email') return step6LoginFromEmailPage(payload, transition.snapshot);
|
||||
if (transition.action === 'password') return step6LoginFromPasswordPage(payload, transition.snapshot);
|
||||
return transition.result;
|
||||
}
|
||||
|
||||
return createStep6RecoverableResult('login_entry_open_stalled', nextSnapshot, {
|
||||
message: '点击登录入口后仍未进入邮箱/密码/验证码页。',
|
||||
});
|
||||
}
|
||||
|
||||
async function step6SwitchToOneTimeCodeLogin(payload, snapshot) {
|
||||
const switchTrigger = snapshot?.switchTrigger || findOneTimeCodeLoginTrigger();
|
||||
if (!switchTrigger || !isActionEnabled(switchTrigger)) {
|
||||
@@ -3024,6 +3123,10 @@ async function step6_login(payload) {
|
||||
return step6LoginFromPasswordPage(payload, snapshot);
|
||||
}
|
||||
|
||||
if (snapshot.state === 'entry_page') {
|
||||
return step6OpenLoginEntry(payload, snapshot);
|
||||
}
|
||||
|
||||
throwForStep6FatalState(snapshot);
|
||||
throw new Error(`无法识别当前登录页面状态。URL: ${snapshot?.url || location.href}`);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -3353,6 +3395,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';
|
||||
@@ -3414,6 +3471,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) {
|
||||
@@ -3435,6 +3501,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) {
|
||||
@@ -3550,6 +3622,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) {
|
||||
@@ -3707,6 +3786,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}"]`);
|
||||
@@ -3756,6 +3838,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()
|
||||
@@ -5005,6 +5096,7 @@ selectEmailGenerator.addEventListener('change', () => {
|
||||
});
|
||||
|
||||
selectIcloudHostPreference?.addEventListener('change', () => {
|
||||
updateMailProviderUI();
|
||||
markSettingsDirty(true);
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
if (getSelectedEmailGenerator() === 'icloud') {
|
||||
@@ -5012,6 +5104,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(() => { });
|
||||
@@ -5596,6 +5700,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);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, '');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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' });
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -96,6 +96,10 @@ function findOneTimeCodeLoginTrigger() {
|
||||
return ${JSON.stringify(overrides.switchTrigger || null)};
|
||||
}
|
||||
|
||||
function findLoginEntryTrigger() {
|
||||
return ${JSON.stringify(overrides.loginEntryTrigger || null)};
|
||||
}
|
||||
|
||||
function getLoginSubmitButton() {
|
||||
return ${JSON.stringify(overrides.submitButton || null)};
|
||||
}
|
||||
@@ -226,6 +230,15 @@ return {
|
||||
assert.strictEqual(snapshot.state, 'oauth_consent_page', '第六步应保留 oauth_consent_page 状态');
|
||||
}
|
||||
|
||||
{
|
||||
const api = createApi({
|
||||
loginEntryTrigger: { id: 'continue-email' },
|
||||
});
|
||||
|
||||
const snapshot = api.inspectLoginAuthState();
|
||||
assert.strictEqual(snapshot.state, 'entry_page');
|
||||
}
|
||||
|
||||
assert.ok(
|
||||
extractFunction('inspectLoginAuthState').includes("state: 'oauth_consent_page'"),
|
||||
'inspectLoginAuthState 应产出 oauth_consent_page 状态'
|
||||
|
||||
+16
-1
@@ -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
@@ -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。
|
||||
|
||||
Reference in New Issue
Block a user