feat: 新增 YYDS Mail 邮箱服务端点
This commit is contained in:
+120
@@ -58,6 +58,8 @@ importScripts(
|
|||||||
'cloudflare-temp-email-utils.js',
|
'cloudflare-temp-email-utils.js',
|
||||||
'cloudmail-utils.js',
|
'cloudmail-utils.js',
|
||||||
'background/cloudmail-provider.js',
|
'background/cloudmail-provider.js',
|
||||||
|
'yyds-mail-utils.js',
|
||||||
|
'background/yyds-mail-provider.js',
|
||||||
'icloud-utils.js',
|
'icloud-utils.js',
|
||||||
'mail-provider-utils.js',
|
'mail-provider-utils.js',
|
||||||
'content/activation-utils.js'
|
'content/activation-utils.js'
|
||||||
@@ -261,6 +263,19 @@ const {
|
|||||||
normalizeCloudMailDomains,
|
normalizeCloudMailDomains,
|
||||||
normalizeCloudMailMailApiMessages,
|
normalizeCloudMailMailApiMessages,
|
||||||
} = self.CloudMailUtils;
|
} = self.CloudMailUtils;
|
||||||
|
const {
|
||||||
|
DEFAULT_YYDS_MAIL_BASE_URL,
|
||||||
|
YYDS_MAIL_PROVIDER,
|
||||||
|
buildYydsMailHeaders,
|
||||||
|
joinYydsMailUrl,
|
||||||
|
normalizeYydsMailAddress,
|
||||||
|
normalizeYydsMailApiKey,
|
||||||
|
normalizeYydsMailBaseUrl,
|
||||||
|
normalizeYydsMailCurrentInbox,
|
||||||
|
normalizeYydsMailInbox,
|
||||||
|
normalizeYydsMailMessageDetail,
|
||||||
|
normalizeYydsMailMessages,
|
||||||
|
} = self.YydsMailUtils;
|
||||||
const {
|
const {
|
||||||
findIcloudAliasByEmail,
|
findIcloudAliasByEmail,
|
||||||
getConfiguredIcloudHostPreference,
|
getConfiguredIcloudHostPreference,
|
||||||
@@ -405,6 +420,7 @@ const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
|
|||||||
const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email';
|
const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email';
|
||||||
const CLOUD_MAIL_PROVIDER = 'cloudmail';
|
const CLOUD_MAIL_PROVIDER = 'cloudmail';
|
||||||
const CLOUD_MAIL_GENERATOR = 'cloudmail';
|
const CLOUD_MAIL_GENERATOR = 'cloudmail';
|
||||||
|
const YYDS_MAIL_GENERATOR = YYDS_MAIL_PROVIDER;
|
||||||
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
|
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
|
||||||
const HOTMAIL_MAILBOXES = ['INBOX', 'Junk'];
|
const HOTMAIL_MAILBOXES = ['INBOX', 'Junk'];
|
||||||
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
|
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
|
||||||
@@ -1003,6 +1019,8 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|||||||
cloudMailReceiveMailbox: '',
|
cloudMailReceiveMailbox: '',
|
||||||
cloudMailDomain: '',
|
cloudMailDomain: '',
|
||||||
cloudMailDomains: [],
|
cloudMailDomains: [],
|
||||||
|
yydsMailApiKey: '',
|
||||||
|
yydsMailBaseUrl: DEFAULT_YYDS_MAIL_BASE_URL,
|
||||||
hotmailAccounts: [],
|
hotmailAccounts: [],
|
||||||
mail2925Accounts: [],
|
mail2925Accounts: [],
|
||||||
paypalAccounts: [],
|
paypalAccounts: [],
|
||||||
@@ -1139,6 +1157,7 @@ const DEFAULT_STATE = {
|
|||||||
luckmailPreserveTagName: DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME,
|
luckmailPreserveTagName: DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME,
|
||||||
currentLuckmailPurchase: null,
|
currentLuckmailPurchase: null,
|
||||||
currentLuckmailMailCursor: null,
|
currentLuckmailMailCursor: null,
|
||||||
|
currentYydsMailInbox: null,
|
||||||
currentPhoneActivation: null,
|
currentPhoneActivation: null,
|
||||||
phoneNumber: '',
|
phoneNumber: '',
|
||||||
currentPhoneVerificationCode: '',
|
currentPhoneVerificationCode: '',
|
||||||
@@ -2114,6 +2133,9 @@ function normalizeEmailGenerator(value = '') {
|
|||||||
const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string'
|
const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string'
|
||||||
? GMAIL_ALIAS_GENERATOR
|
? GMAIL_ALIAS_GENERATOR
|
||||||
: 'gmail-alias';
|
: 'gmail-alias';
|
||||||
|
const yydsMailGenerator = typeof YYDS_MAIL_GENERATOR === 'string'
|
||||||
|
? YYDS_MAIL_GENERATOR
|
||||||
|
: 'yyds-mail';
|
||||||
if (normalized === 'custom' || normalized === 'manual') {
|
if (normalized === 'custom' || normalized === 'manual') {
|
||||||
return 'custom';
|
return 'custom';
|
||||||
}
|
}
|
||||||
@@ -2129,6 +2151,7 @@ function normalizeEmailGenerator(value = '') {
|
|||||||
if (normalized === 'cloudflare') return 'cloudflare';
|
if (normalized === 'cloudflare') return 'cloudflare';
|
||||||
if (normalized === CLOUDFLARE_TEMP_EMAIL_GENERATOR) return CLOUDFLARE_TEMP_EMAIL_GENERATOR;
|
if (normalized === CLOUDFLARE_TEMP_EMAIL_GENERATOR) return CLOUDFLARE_TEMP_EMAIL_GENERATOR;
|
||||||
if (normalized === 'cloudmail') return 'cloudmail';
|
if (normalized === 'cloudmail') return 'cloudmail';
|
||||||
|
if (normalized === yydsMailGenerator) return yydsMailGenerator;
|
||||||
return 'duck';
|
return 'duck';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2303,6 +2326,15 @@ async function markCurrentRegistrationAccountUsed(state = {}, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof isYydsMailProvider === 'function' && isYydsMailProvider(latestState)) {
|
||||||
|
const currentInbox = normalizeYydsMailCurrentInbox(latestState.currentYydsMailInbox);
|
||||||
|
if (currentInbox?.address) {
|
||||||
|
await clearYydsMailRuntimeState({ clearEmail: true });
|
||||||
|
await addLog(`${reasonPrefix}:YYDS Mail 邮箱 ${currentInbox.address} 运行态已清空。`, options.level || 'warn');
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) {
|
if (String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) {
|
||||||
await patchMail2925Account(latestState.currentMail2925AccountId, {
|
await patchMail2925Account(latestState.currentMail2925AccountId, {
|
||||||
lastUsedAt: Date.now(),
|
lastUsedAt: Date.now(),
|
||||||
@@ -2355,6 +2387,9 @@ function normalizePanelMode(value = '') {
|
|||||||
|
|
||||||
function normalizeMailProvider(value = '') {
|
function normalizeMailProvider(value = '') {
|
||||||
const normalized = String(value || '').trim().toLowerCase();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
|
const yydsMailProvider = typeof YYDS_MAIL_PROVIDER === 'string'
|
||||||
|
? YYDS_MAIL_PROVIDER
|
||||||
|
: 'yyds-mail';
|
||||||
switch (normalized) {
|
switch (normalized) {
|
||||||
case 'custom':
|
case 'custom':
|
||||||
case ICLOUD_PROVIDER:
|
case ICLOUD_PROVIDER:
|
||||||
@@ -2363,6 +2398,7 @@ function normalizeMailProvider(value = '') {
|
|||||||
case LUCKMAIL_PROVIDER:
|
case LUCKMAIL_PROVIDER:
|
||||||
case CLOUDFLARE_TEMP_EMAIL_PROVIDER:
|
case CLOUDFLARE_TEMP_EMAIL_PROVIDER:
|
||||||
case CLOUD_MAIL_PROVIDER:
|
case CLOUD_MAIL_PROVIDER:
|
||||||
|
case yydsMailProvider:
|
||||||
case '163':
|
case '163':
|
||||||
case '163-vip':
|
case '163-vip':
|
||||||
case '126':
|
case '126':
|
||||||
@@ -2600,6 +2636,32 @@ const {
|
|||||||
pollCloudMailVerificationCode,
|
pollCloudMailVerificationCode,
|
||||||
resolveCloudMailPollTargetEmail,
|
resolveCloudMailPollTargetEmail,
|
||||||
} = cloudMailProvider;
|
} = cloudMailProvider;
|
||||||
|
const yydsMailProvider = self.MultiPageBackgroundYydsMailProvider.createYydsMailProvider({
|
||||||
|
addLog,
|
||||||
|
buildYydsMailHeaders,
|
||||||
|
DEFAULT_YYDS_MAIL_BASE_URL,
|
||||||
|
getState,
|
||||||
|
joinYydsMailUrl,
|
||||||
|
normalizeYydsMailAddress,
|
||||||
|
normalizeYydsMailApiKey,
|
||||||
|
normalizeYydsMailBaseUrl,
|
||||||
|
normalizeYydsMailCurrentInbox,
|
||||||
|
normalizeYydsMailInbox,
|
||||||
|
normalizeYydsMailMessageDetail,
|
||||||
|
normalizeYydsMailMessages,
|
||||||
|
persistRegistrationEmailState,
|
||||||
|
pickVerificationMessageWithTimeFallback,
|
||||||
|
setEmailState,
|
||||||
|
setState,
|
||||||
|
sleepWithStop,
|
||||||
|
throwIfStopped,
|
||||||
|
YYDS_MAIL_PROVIDER,
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
clearYydsMailRuntimeState,
|
||||||
|
fetchYydsMailAddress,
|
||||||
|
pollYydsMailVerificationCode,
|
||||||
|
} = yydsMailProvider;
|
||||||
|
|
||||||
function normalizeSub2ApiGroupNames(value = '') {
|
function normalizeSub2ApiGroupNames(value = '') {
|
||||||
const source = Array.isArray(value)
|
const source = Array.isArray(value)
|
||||||
@@ -2964,6 +3026,10 @@ function normalizePersistentSettingValue(key, value) {
|
|||||||
return normalizeCloudMailDomain(value);
|
return normalizeCloudMailDomain(value);
|
||||||
case 'cloudMailDomains':
|
case 'cloudMailDomains':
|
||||||
return normalizeCloudMailDomains(value);
|
return normalizeCloudMailDomains(value);
|
||||||
|
case 'yydsMailApiKey':
|
||||||
|
return normalizeYydsMailApiKey(value);
|
||||||
|
case 'yydsMailBaseUrl':
|
||||||
|
return normalizeYydsMailBaseUrl(value);
|
||||||
case 'hotmailAccounts':
|
case 'hotmailAccounts':
|
||||||
return normalizeHotmailAccounts(value);
|
return normalizeHotmailAccounts(value);
|
||||||
case 'mail2925Accounts':
|
case 'mail2925Accounts':
|
||||||
@@ -3814,6 +3880,8 @@ async function resetState() {
|
|||||||
'luckmailUsedPurchases',
|
'luckmailUsedPurchases',
|
||||||
'luckmailPreserveTagId',
|
'luckmailPreserveTagId',
|
||||||
'luckmailPreserveTagName',
|
'luckmailPreserveTagName',
|
||||||
|
'yydsMailApiKey',
|
||||||
|
'yydsMailBaseUrl',
|
||||||
'preferredIcloudHost',
|
'preferredIcloudHost',
|
||||||
'automationWindowId',
|
'automationWindowId',
|
||||||
...CONTRIBUTION_RUNTIME_KEYS,
|
...CONTRIBUTION_RUNTIME_KEYS,
|
||||||
@@ -3879,6 +3947,9 @@ async function resetState() {
|
|||||||
luckmailPreserveTagName: String(prev.luckmailPreserveTagName || '').trim() || DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME,
|
luckmailPreserveTagName: String(prev.luckmailPreserveTagName || '').trim() || DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME,
|
||||||
currentLuckmailPurchase: null,
|
currentLuckmailPurchase: null,
|
||||||
currentLuckmailMailCursor: null,
|
currentLuckmailMailCursor: null,
|
||||||
|
yydsMailApiKey: normalizeYydsMailApiKey(prev.yydsMailApiKey ?? persistedSettings.yydsMailApiKey),
|
||||||
|
yydsMailBaseUrl: normalizeYydsMailBaseUrl(prev.yydsMailBaseUrl ?? persistedSettings.yydsMailBaseUrl),
|
||||||
|
currentYydsMailInbox: null,
|
||||||
// Keep reusable phone activation across round resets so the same number can be reactivated up to maxUses.
|
// Keep reusable phone activation across round resets so the same number can be reactivated up to maxUses.
|
||||||
reusablePhoneActivation,
|
reusablePhoneActivation,
|
||||||
// Keep free reuse phone activation until the user clears or the flow retires it.
|
// Keep free reuse phone activation until the user clears or the flow retires it.
|
||||||
@@ -4014,6 +4085,16 @@ function isLuckmailProvider(stateOrProvider) {
|
|||||||
return provider === LUCKMAIL_PROVIDER;
|
return provider === LUCKMAIL_PROVIDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isYydsMailProvider(stateOrProvider) {
|
||||||
|
const provider = typeof stateOrProvider === 'string'
|
||||||
|
? stateOrProvider
|
||||||
|
: stateOrProvider?.mailProvider;
|
||||||
|
const yydsMailProvider = typeof YYDS_MAIL_PROVIDER === 'string'
|
||||||
|
? YYDS_MAIL_PROVIDER
|
||||||
|
: 'yyds-mail';
|
||||||
|
return provider === yydsMailProvider;
|
||||||
|
}
|
||||||
|
|
||||||
function isCustomMailProvider(stateOrProvider) {
|
function isCustomMailProvider(stateOrProvider) {
|
||||||
const provider = typeof stateOrProvider === 'string'
|
const provider = typeof stateOrProvider === 'string'
|
||||||
? stateOrProvider
|
? stateOrProvider
|
||||||
@@ -10545,6 +10626,9 @@ function getEmailGeneratorLabel(generator) {
|
|||||||
const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string'
|
const gmailAliasGenerator = typeof GMAIL_ALIAS_GENERATOR === 'string'
|
||||||
? GMAIL_ALIAS_GENERATOR
|
? GMAIL_ALIAS_GENERATOR
|
||||||
: 'gmail-alias';
|
: 'gmail-alias';
|
||||||
|
const yydsMailGenerator = typeof YYDS_MAIL_GENERATOR === 'string'
|
||||||
|
? YYDS_MAIL_GENERATOR
|
||||||
|
: 'yyds-mail';
|
||||||
if (generator === 'custom') {
|
if (generator === 'custom') {
|
||||||
return '自定义邮箱';
|
return '自定义邮箱';
|
||||||
}
|
}
|
||||||
@@ -10560,6 +10644,7 @@ function getEmailGeneratorLabel(generator) {
|
|||||||
if (generator === 'cloudflare') return 'Cloudflare 邮箱';
|
if (generator === 'cloudflare') return 'Cloudflare 邮箱';
|
||||||
if (generator === CLOUDFLARE_TEMP_EMAIL_GENERATOR) return 'Cloudflare Temp Email';
|
if (generator === CLOUDFLARE_TEMP_EMAIL_GENERATOR) return 'Cloudflare Temp Email';
|
||||||
if (generator === CLOUD_MAIL_GENERATOR) return 'Cloud Mail';
|
if (generator === CLOUD_MAIL_GENERATOR) return 'Cloud Mail';
|
||||||
|
if (generator === yydsMailGenerator) return 'YYDS Mail';
|
||||||
return 'Duck 邮箱';
|
return 'Duck 邮箱';
|
||||||
}
|
}
|
||||||
const mail2925SessionManager = self.MultiPageBackgroundMail2925Session?.createMail2925SessionManager({
|
const mail2925SessionManager = self.MultiPageBackgroundMail2925Session?.createMail2925SessionManager({
|
||||||
@@ -10736,7 +10821,20 @@ async function fetchDuckEmail(options = {}) {
|
|||||||
|
|
||||||
async function fetchGeneratedEmail(state, options = {}) {
|
async function fetchGeneratedEmail(state, options = {}) {
|
||||||
const currentState = state || await getState();
|
const currentState = state || await getState();
|
||||||
|
const yydsMailProvider = typeof YYDS_MAIL_PROVIDER === 'string'
|
||||||
|
? YYDS_MAIL_PROVIDER
|
||||||
|
: 'yyds-mail';
|
||||||
|
const yydsMailGenerator = typeof YYDS_MAIL_GENERATOR === 'string'
|
||||||
|
? YYDS_MAIL_GENERATOR
|
||||||
|
: 'yyds-mail';
|
||||||
|
const requestedMailProvider = normalizeMailProvider(options.mailProvider ?? currentState.mailProvider);
|
||||||
|
if (requestedMailProvider === yydsMailProvider) {
|
||||||
|
return fetchYydsMailAddress(currentState, options);
|
||||||
|
}
|
||||||
const generator = normalizeEmailGenerator(options.generator ?? currentState.emailGenerator);
|
const generator = normalizeEmailGenerator(options.generator ?? currentState.emailGenerator);
|
||||||
|
if (generator === yydsMailGenerator) {
|
||||||
|
return fetchYydsMailAddress(currentState, options);
|
||||||
|
}
|
||||||
if (generator === CLOUD_MAIL_GENERATOR) {
|
if (generator === CLOUD_MAIL_GENERATOR) {
|
||||||
return fetchCloudMailAddress(currentState, options);
|
return fetchCloudMailAddress(currentState, options);
|
||||||
}
|
}
|
||||||
@@ -11294,6 +11392,12 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
|||||||
return purchase.email_address;
|
return purchase.email_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isYydsMailProvider(currentState)) {
|
||||||
|
const email = await fetchYydsMailAddress(currentState, { generateNew: true });
|
||||||
|
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:YYDS Mail 邮箱已就绪:${email}(第 ${attemptRuns} 次尝试)===`, 'ok');
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
if (isGeneratedAliasProvider(currentState)) {
|
if (isGeneratedAliasProvider(currentState)) {
|
||||||
if (currentState.mailProvider === GMAIL_PROVIDER) {
|
if (currentState.mailProvider === GMAIL_PROVIDER) {
|
||||||
if (!currentState.emailPrefix) {
|
if (!currentState.emailPrefix) {
|
||||||
@@ -11425,6 +11529,12 @@ async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
|||||||
return purchase.email_address;
|
return purchase.email_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isYydsMailProvider(currentState)) {
|
||||||
|
const email = await fetchYydsMailAddress(currentState, { generateNew: true });
|
||||||
|
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:YYDS Mail 邮箱已就绪:${email}(第 ${attemptRuns} 次尝试)===`, 'ok');
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
if (isGeneratedAliasProvider(currentState)) {
|
if (isGeneratedAliasProvider(currentState)) {
|
||||||
if (isReusableGeneratedAliasEmail(currentState)) {
|
if (isReusableGeneratedAliasEmail(currentState)) {
|
||||||
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:当前已复用 ${currentState.email},将直接继续执行(第 ${attemptRuns} 次尝试)===`, 'info');
|
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:当前已复用 ${currentState.email},将直接继续执行(第 ${attemptRuns} 次尝试)===`, 'info');
|
||||||
@@ -12168,12 +12278,14 @@ const verificationFlowHelpers = self.MultiPageBackgroundVerificationFlow?.create
|
|||||||
isRetryableContentScriptTransportError,
|
isRetryableContentScriptTransportError,
|
||||||
isStopError,
|
isStopError,
|
||||||
LUCKMAIL_PROVIDER,
|
LUCKMAIL_PROVIDER,
|
||||||
|
YYDS_MAIL_PROVIDER,
|
||||||
MAIL_2925_VERIFICATION_INTERVAL_MS,
|
MAIL_2925_VERIFICATION_INTERVAL_MS,
|
||||||
MAIL_2925_VERIFICATION_MAX_ATTEMPTS,
|
MAIL_2925_VERIFICATION_MAX_ATTEMPTS,
|
||||||
pollCloudflareTempEmailVerificationCode,
|
pollCloudflareTempEmailVerificationCode,
|
||||||
pollCloudMailVerificationCode,
|
pollCloudMailVerificationCode,
|
||||||
pollHotmailVerificationCode,
|
pollHotmailVerificationCode,
|
||||||
pollLuckmailVerificationCode,
|
pollLuckmailVerificationCode,
|
||||||
|
pollYydsMailVerificationCode,
|
||||||
sendToContentScript,
|
sendToContentScript,
|
||||||
sendToContentScriptResilient,
|
sendToContentScriptResilient,
|
||||||
sendToMailContentScriptResilient,
|
sendToMailContentScriptResilient,
|
||||||
@@ -12565,6 +12677,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
|
|||||||
clearAutoRunTimerAlarm,
|
clearAutoRunTimerAlarm,
|
||||||
clearFreeReusablePhoneActivation,
|
clearFreeReusablePhoneActivation,
|
||||||
clearLuckmailRuntimeState,
|
clearLuckmailRuntimeState,
|
||||||
|
clearYydsMailRuntimeState,
|
||||||
clearStopRequest,
|
clearStopRequest,
|
||||||
closeLocalhostCallbackTabs,
|
closeLocalhostCallbackTabs,
|
||||||
closeTabsByUrlPrefix,
|
closeTabsByUrlPrefix,
|
||||||
@@ -12621,6 +12734,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
|
|||||||
isHotmailProvider,
|
isHotmailProvider,
|
||||||
isLocalhostOAuthCallbackUrl,
|
isLocalhostOAuthCallbackUrl,
|
||||||
isLuckmailProvider,
|
isLuckmailProvider,
|
||||||
|
isYydsMailProvider,
|
||||||
isStopError,
|
isStopError,
|
||||||
isTabAlive,
|
isTabAlive,
|
||||||
launchAutoRunTimerPlan,
|
launchAutoRunTimerPlan,
|
||||||
@@ -12833,6 +12947,9 @@ async function executeStep3(state) {
|
|||||||
|
|
||||||
function getMailConfig(state) {
|
function getMailConfig(state) {
|
||||||
const provider = state.mailProvider || 'qq';
|
const provider = state.mailProvider || 'qq';
|
||||||
|
const yydsMailProvider = typeof YYDS_MAIL_PROVIDER === 'string'
|
||||||
|
? YYDS_MAIL_PROVIDER
|
||||||
|
: 'yyds-mail';
|
||||||
if (provider === 'custom') {
|
if (provider === 'custom') {
|
||||||
return { provider: 'custom', label: '自定义邮箱' };
|
return { provider: 'custom', label: '自定义邮箱' };
|
||||||
}
|
}
|
||||||
@@ -12881,6 +12998,9 @@ function getMailConfig(state) {
|
|||||||
if (provider === 'cloudmail') {
|
if (provider === 'cloudmail') {
|
||||||
return { provider: 'cloudmail', label: 'Cloud Mail' };
|
return { provider: 'cloudmail', label: 'Cloud Mail' };
|
||||||
}
|
}
|
||||||
|
if (provider === yydsMailProvider) {
|
||||||
|
return { provider: yydsMailProvider, label: 'YYDS Mail' };
|
||||||
|
}
|
||||||
if (provider === '163') {
|
if (provider === '163') {
|
||||||
return { source: 'mail-163', url: 'https://mail.163.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: '163 邮箱' };
|
return { source: 'mail-163', url: 'https://mail.163.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: '163 邮箱' };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
clearAutoRunTimerAlarm,
|
clearAutoRunTimerAlarm,
|
||||||
clearFreeReusablePhoneActivation,
|
clearFreeReusablePhoneActivation,
|
||||||
clearLuckmailRuntimeState,
|
clearLuckmailRuntimeState,
|
||||||
|
clearYydsMailRuntimeState,
|
||||||
clearStopRequest,
|
clearStopRequest,
|
||||||
closeLocalhostCallbackTabs,
|
closeLocalhostCallbackTabs,
|
||||||
closeTabsByUrlPrefix,
|
closeTabsByUrlPrefix,
|
||||||
@@ -125,6 +126,7 @@
|
|||||||
isHotmailProvider,
|
isHotmailProvider,
|
||||||
isLocalhostOAuthCallbackUrl,
|
isLocalhostOAuthCallbackUrl,
|
||||||
isLuckmailProvider,
|
isLuckmailProvider,
|
||||||
|
isYydsMailProvider = () => false,
|
||||||
isStopError,
|
isStopError,
|
||||||
isTabAlive,
|
isTabAlive,
|
||||||
launchAutoRunTimerPlan,
|
launchAutoRunTimerPlan,
|
||||||
@@ -572,6 +574,14 @@
|
|||||||
await clearLuckmailRuntimeState({ clearEmail: true });
|
await clearLuckmailRuntimeState({ clearEmail: true });
|
||||||
await addLog('当前 LuckMail 邮箱运行态已清空,下轮将优先复用未用邮箱或重新购买邮箱。', 'ok');
|
await addLog('当前 LuckMail 邮箱运行态已清空,下轮将优先复用未用邮箱或重新购买邮箱。', 'ok');
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
typeof markCurrentRegistrationAccountUsed !== 'function'
|
||||||
|
&& isYydsMailProvider(latestState)
|
||||||
|
&& typeof clearYydsMailRuntimeState === 'function'
|
||||||
|
) {
|
||||||
|
await clearYydsMailRuntimeState({ clearEmail: true });
|
||||||
|
await addLog('当前 YYDS Mail 邮箱运行态已清空,下轮将重新创建邮箱。', 'ok');
|
||||||
|
}
|
||||||
const localhostPrefix = buildLocalhostCleanupPrefix(payload.localhostUrl);
|
const localhostPrefix = buildLocalhostCleanupPrefix(payload.localhostUrl);
|
||||||
if (localhostPrefix) {
|
if (localhostPrefix) {
|
||||||
await closeTabsByUrlPrefix(localhostPrefix, {
|
await closeTabsByUrlPrefix(localhostPrefix, {
|
||||||
|
|||||||
@@ -105,6 +105,7 @@
|
|||||||
luckmail: Object.freeze([
|
luckmail: Object.freeze([
|
||||||
'currentLuckmailPurchase',
|
'currentLuckmailPurchase',
|
||||||
'currentLuckmailMailCursor',
|
'currentLuckmailMailCursor',
|
||||||
|
'currentYydsMailInbox',
|
||||||
]),
|
]),
|
||||||
identity: Object.freeze([
|
identity: Object.freeze([
|
||||||
'resolvedSignupMethod',
|
'resolvedSignupMethod',
|
||||||
|
|||||||
@@ -24,12 +24,14 @@
|
|||||||
isMail2925LimitReachedError,
|
isMail2925LimitReachedError,
|
||||||
isStopError,
|
isStopError,
|
||||||
LUCKMAIL_PROVIDER,
|
LUCKMAIL_PROVIDER,
|
||||||
|
YYDS_MAIL_PROVIDER = 'yyds-mail',
|
||||||
MAIL_2925_VERIFICATION_INTERVAL_MS,
|
MAIL_2925_VERIFICATION_INTERVAL_MS,
|
||||||
MAIL_2925_VERIFICATION_MAX_ATTEMPTS,
|
MAIL_2925_VERIFICATION_MAX_ATTEMPTS,
|
||||||
pollCloudflareTempEmailVerificationCode,
|
pollCloudflareTempEmailVerificationCode,
|
||||||
pollCloudMailVerificationCode,
|
pollCloudMailVerificationCode,
|
||||||
pollHotmailVerificationCode,
|
pollHotmailVerificationCode,
|
||||||
pollLuckmailVerificationCode,
|
pollLuckmailVerificationCode,
|
||||||
|
pollYydsMailVerificationCode,
|
||||||
sendToContentScript,
|
sendToContentScript,
|
||||||
sendToContentScriptResilient,
|
sendToContentScriptResilient,
|
||||||
sendToMailContentScriptResilient,
|
sendToMailContentScriptResilient,
|
||||||
@@ -985,6 +987,13 @@
|
|||||||
}, cleanPollOverrides, `轮询${getVerificationCodeLabel(step)}验证码邮箱`);
|
}, cleanPollOverrides, `轮询${getVerificationCodeLabel(step)}验证码邮箱`);
|
||||||
return pollCloudMailVerificationCode(step, state, timedPoll.payload);
|
return pollCloudMailVerificationCode(step, state, timedPoll.payload);
|
||||||
}
|
}
|
||||||
|
if (mail.provider === YYDS_MAIL_PROVIDER) {
|
||||||
|
const timedPoll = await applyMailPollingTimeBudget(step, {
|
||||||
|
...getVerificationPollPayload(step, state),
|
||||||
|
...cleanPollOverrides,
|
||||||
|
}, cleanPollOverrides, `轮询${getVerificationCodeLabel(step)}验证码邮箱`);
|
||||||
|
return pollYydsMailVerificationCode(step, state, timedPoll.payload);
|
||||||
|
}
|
||||||
|
|
||||||
if (Number(pollOverrides.resendIntervalMs) > 0) {
|
if (Number(pollOverrides.resendIntervalMs) > 0) {
|
||||||
return pollFreshVerificationCodeWithResendInterval(step, state, mail, pollOverrides);
|
return pollFreshVerificationCodeWithResendInterval(step, state, mail, pollOverrides);
|
||||||
|
|||||||
@@ -0,0 +1,330 @@
|
|||||||
|
(function yydsMailProviderModule(root, factory) {
|
||||||
|
root.MultiPageBackgroundYydsMailProvider = factory();
|
||||||
|
})(typeof self !== 'undefined' ? self : globalThis, function createYydsMailProviderModule() {
|
||||||
|
function createYydsMailProvider(deps = {}) {
|
||||||
|
const {
|
||||||
|
addLog = async () => {},
|
||||||
|
buildYydsMailHeaders,
|
||||||
|
DEFAULT_YYDS_MAIL_BASE_URL = 'https://maliapi.215.im/v1',
|
||||||
|
fetchImpl = typeof fetch === 'function' ? fetch.bind(globalThis) : null,
|
||||||
|
getState = async () => ({}),
|
||||||
|
joinYydsMailUrl,
|
||||||
|
normalizeYydsMailAddress,
|
||||||
|
normalizeYydsMailApiKey,
|
||||||
|
normalizeYydsMailBaseUrl,
|
||||||
|
normalizeYydsMailCurrentInbox,
|
||||||
|
normalizeYydsMailInbox,
|
||||||
|
normalizeYydsMailMessageDetail,
|
||||||
|
normalizeYydsMailMessages,
|
||||||
|
persistRegistrationEmailState = null,
|
||||||
|
pickVerificationMessageWithTimeFallback,
|
||||||
|
setEmailState = async () => {},
|
||||||
|
setState = async () => {},
|
||||||
|
sleepWithStop = async () => {},
|
||||||
|
throwIfStopped = () => {},
|
||||||
|
YYDS_MAIL_PROVIDER = 'yyds-mail',
|
||||||
|
} = deps;
|
||||||
|
|
||||||
|
async function persistResolvedEmailState(state = null, email, options = {}) {
|
||||||
|
if (typeof persistRegistrationEmailState === 'function') {
|
||||||
|
await persistRegistrationEmailState(state, email, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await setEmailState(email, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getYydsMailConfig(state = {}) {
|
||||||
|
return {
|
||||||
|
apiKey: normalizeYydsMailApiKey(state.yydsMailApiKey),
|
||||||
|
baseUrl: normalizeYydsMailBaseUrl(state.yydsMailBaseUrl || DEFAULT_YYDS_MAIL_BASE_URL),
|
||||||
|
currentInbox: normalizeYydsMailCurrentInbox(state.currentYydsMailInbox),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureYydsMailConfig(state = {}, options = {}) {
|
||||||
|
const { requireApiKey = false, requireInbox = false } = options;
|
||||||
|
const config = getYydsMailConfig(state);
|
||||||
|
if (!config.baseUrl) {
|
||||||
|
throw new Error('YYDS Mail API 地址为空或格式无效。');
|
||||||
|
}
|
||||||
|
if (requireApiKey && !config.apiKey) {
|
||||||
|
throw new Error('YYDS Mail API Key 为空,请先在侧边栏填写。');
|
||||||
|
}
|
||||||
|
if (requireInbox && (!config.currentInbox?.address || !config.currentInbox?.token)) {
|
||||||
|
throw new Error('YYDS Mail 当前没有可用邮箱,请先获取邮箱。');
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestYydsMailJson(config, path, options = {}) {
|
||||||
|
if (!fetchImpl) {
|
||||||
|
throw new Error('YYDS Mail 当前运行环境不支持 fetch。');
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
method = 'GET',
|
||||||
|
payload,
|
||||||
|
params,
|
||||||
|
timeoutMs = 20000,
|
||||||
|
auth = 'temp',
|
||||||
|
} = options;
|
||||||
|
const url = joinYydsMailUrl(config.baseUrl, path, params);
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(new Error('timeout')), timeoutMs);
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await fetchImpl(url, {
|
||||||
|
method,
|
||||||
|
headers: buildYydsMailHeaders(config, {
|
||||||
|
apiKey: auth === 'apiKey' ? config.apiKey : '',
|
||||||
|
tempToken: auth === 'temp' ? config.currentInbox?.token : '',
|
||||||
|
includeConfigApiKey: auth === 'apiKey',
|
||||||
|
json: payload !== undefined,
|
||||||
|
}),
|
||||||
|
body: payload !== undefined ? JSON.stringify(payload || {}) : undefined,
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errorMessage = err?.name === 'AbortError'
|
||||||
|
? `YYDS Mail 请求超时(>${Math.round(timeoutMs / 1000)} 秒)`
|
||||||
|
: `YYDS Mail 请求失败:${err.message}`;
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = await response.text();
|
||||||
|
let parsed = {};
|
||||||
|
try {
|
||||||
|
parsed = text ? JSON.parse(text) : {};
|
||||||
|
} catch {
|
||||||
|
parsed = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const payloadError = parsed && typeof parsed === 'object'
|
||||||
|
? (parsed.error || parsed.message || parsed.msg || parsed.errorCode)
|
||||||
|
: '';
|
||||||
|
throw new Error(`YYDS Mail 请求失败:${payloadError || text || `HTTP ${response.status}`}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed && typeof parsed === 'object' && parsed.success === false) {
|
||||||
|
throw new Error(`YYDS Mail 业务错误:${parsed.error || parsed.message || parsed.errorCode || 'unknown_error'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed && typeof parsed === 'object' && Object.prototype.hasOwnProperty.call(parsed, 'data')) {
|
||||||
|
return parsed.data;
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateYydsMailLocalPart() {
|
||||||
|
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const digits = '0123456789';
|
||||||
|
const chars = [];
|
||||||
|
for (let i = 0; i < 6; i += 1) chars.push(letters[Math.floor(Math.random() * letters.length)]);
|
||||||
|
for (let i = 0; i < 4; i += 1) chars.push(digits[Math.floor(Math.random() * digits.length)]);
|
||||||
|
for (let i = chars.length - 1; i > 0; i -= 1) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[chars[i], chars[j]] = [chars[j], chars[i]];
|
||||||
|
}
|
||||||
|
return chars.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchYydsMailAddress(state, options = {}) {
|
||||||
|
throwIfStopped();
|
||||||
|
const latestState = state || await getState();
|
||||||
|
const config = ensureYydsMailConfig(latestState, { requireApiKey: true });
|
||||||
|
const localPart = String(options.localPart || options.name || '').trim().toLowerCase()
|
||||||
|
|| generateYydsMailLocalPart();
|
||||||
|
const data = await requestYydsMailJson(config, '/accounts', {
|
||||||
|
method: 'POST',
|
||||||
|
auth: 'apiKey',
|
||||||
|
payload: { localPart },
|
||||||
|
});
|
||||||
|
const inbox = normalizeYydsMailInbox(data);
|
||||||
|
if (!inbox.address || !inbox.token) {
|
||||||
|
throw new Error('YYDS Mail 创建邮箱成功,但未返回可用 address/token。');
|
||||||
|
}
|
||||||
|
|
||||||
|
await setState({ currentYydsMailInbox: inbox });
|
||||||
|
await persistResolvedEmailState(latestState, inbox.address, {
|
||||||
|
source: `generated:${YYDS_MAIL_PROVIDER}`,
|
||||||
|
preserveAccountIdentity: Boolean(options?.preserveAccountIdentity),
|
||||||
|
});
|
||||||
|
await addLog(`YYDS Mail:已创建邮箱 ${inbox.address}`, 'ok');
|
||||||
|
return inbox.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveYydsMailInbox(state = {}) {
|
||||||
|
const config = getYydsMailConfig(state);
|
||||||
|
if (config.currentInbox?.address && config.currentInbox?.token) {
|
||||||
|
return config.currentInbox;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveYydsMailPollTargetEmail(state = {}, pollPayload = {}) {
|
||||||
|
return normalizeYydsMailAddress(pollPayload.targetEmail)
|
||||||
|
|| resolveYydsMailInbox(state)?.address
|
||||||
|
|| normalizeYydsMailAddress(state.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listYydsMailMessages(state, options = {}) {
|
||||||
|
const latestState = state || await getState();
|
||||||
|
const inbox = resolveYydsMailInbox(latestState);
|
||||||
|
const config = {
|
||||||
|
...ensureYydsMailConfig(latestState, { requireInbox: true }),
|
||||||
|
currentInbox: inbox,
|
||||||
|
};
|
||||||
|
const address = normalizeYydsMailAddress(options.address) || inbox.address;
|
||||||
|
const payload = await requestYydsMailJson(config, '/messages', {
|
||||||
|
method: 'GET',
|
||||||
|
auth: 'temp',
|
||||||
|
params: {
|
||||||
|
address,
|
||||||
|
limit: Number(options.limit) || 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
config,
|
||||||
|
messages: normalizeYydsMailMessages(payload),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getYydsMailMessageDetail(state, messageId, options = {}) {
|
||||||
|
const latestState = state || await getState();
|
||||||
|
const inbox = resolveYydsMailInbox(latestState);
|
||||||
|
const config = {
|
||||||
|
...ensureYydsMailConfig(latestState, { requireInbox: true }),
|
||||||
|
currentInbox: inbox,
|
||||||
|
};
|
||||||
|
const address = normalizeYydsMailAddress(options.address) || inbox.address;
|
||||||
|
const payload = await requestYydsMailJson(config, `/messages/${encodeURIComponent(messageId)}`, {
|
||||||
|
method: 'GET',
|
||||||
|
auth: 'temp',
|
||||||
|
params: { address },
|
||||||
|
});
|
||||||
|
return normalizeYydsMailMessageDetail(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeYydsMailMessagesForLog(messages) {
|
||||||
|
return (messages || [])
|
||||||
|
.slice()
|
||||||
|
.sort((left, right) => {
|
||||||
|
const leftTime = Date.parse(left.receivedDateTime || '') || 0;
|
||||||
|
const rightTime = Date.parse(right.receivedDateTime || '') || 0;
|
||||||
|
return rightTime - leftTime;
|
||||||
|
})
|
||||||
|
.slice(0, 3)
|
||||||
|
.map((message) => {
|
||||||
|
const receivedAt = message?.receivedDateTime || '未知时间';
|
||||||
|
const sender = message?.from?.emailAddress?.address || '未知发件人';
|
||||||
|
const subject = message?.subject || '(无主题)';
|
||||||
|
const preview = String(message?.bodyPreview || '').replace(/\s+/g, ' ').trim().slice(0, 80);
|
||||||
|
return `${receivedAt} | ${sender} | ${subject} | ${preview}`;
|
||||||
|
})
|
||||||
|
.join(' || ');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hydrateYydsMailMessageDetails(state, messages, address) {
|
||||||
|
const details = [];
|
||||||
|
for (const message of (messages || []).slice(0, 8)) {
|
||||||
|
throwIfStopped();
|
||||||
|
if (!message?.id) {
|
||||||
|
details.push(message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
details.push(await getYydsMailMessageDetail(state, message.id, { address }));
|
||||||
|
} catch (err) {
|
||||||
|
await addLog(`YYDS Mail:读取邮件详情 ${message.id} 失败:${err.message}`, 'warn');
|
||||||
|
details.push(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return details.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollYydsMailVerificationCode(step, state, pollPayload = {}) {
|
||||||
|
const latestState = state || await getState();
|
||||||
|
const targetEmail = resolveYydsMailPollTargetEmail(latestState, pollPayload);
|
||||||
|
if (!targetEmail) {
|
||||||
|
throw new Error('YYDS Mail 轮询前缺少目标邮箱地址,请先获取邮箱。');
|
||||||
|
}
|
||||||
|
|
||||||
|
await addLog(`步骤 ${step}:正在轮询 YYDS Mail 邮件(${targetEmail})...`, 'info');
|
||||||
|
const maxAttempts = Number(pollPayload.maxAttempts) || 5;
|
||||||
|
const intervalMs = Number(pollPayload.intervalMs) || 3000;
|
||||||
|
let lastError = null;
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||||
|
throwIfStopped();
|
||||||
|
try {
|
||||||
|
const { messages } = await listYydsMailMessages(latestState, {
|
||||||
|
address: targetEmail,
|
||||||
|
limit: pollPayload.limit || 20,
|
||||||
|
});
|
||||||
|
const detailedMessages = await hydrateYydsMailMessageDetails(latestState, messages, targetEmail);
|
||||||
|
const matchResult = pickVerificationMessageWithTimeFallback(detailedMessages, {
|
||||||
|
afterTimestamp: pollPayload.filterAfterTimestamp || 0,
|
||||||
|
senderFilters: pollPayload.senderFilters || [],
|
||||||
|
subjectFilters: pollPayload.subjectFilters || [],
|
||||||
|
requiredKeywords: pollPayload.requiredKeywords || [],
|
||||||
|
codePatterns: pollPayload.codePatterns || [],
|
||||||
|
excludeCodes: pollPayload.excludeCodes || [],
|
||||||
|
});
|
||||||
|
const match = matchResult.match;
|
||||||
|
if (match?.code) {
|
||||||
|
if (matchResult.usedRelaxedFilters) {
|
||||||
|
const fallbackLabel = matchResult.usedTimeFallback ? '宽松匹配 + 时间回退' : '宽松匹配';
|
||||||
|
await addLog(`步骤 ${step}:严格规则未命中,已改用 ${fallbackLabel} 并命中 YYDS Mail 验证码。`, 'warn');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
code: match.code,
|
||||||
|
emailTimestamp: match.receivedAt || Date.now(),
|
||||||
|
mailId: match.message?.id || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
lastError = new Error(`步骤 ${step}:暂未在 YYDS Mail 中找到匹配验证码(${attempt}/${maxAttempts})。`);
|
||||||
|
await addLog(lastError.message, attempt === maxAttempts ? 'warn' : 'info');
|
||||||
|
const sample = summarizeYydsMailMessagesForLog(detailedMessages.length ? detailedMessages : messages);
|
||||||
|
if (sample) {
|
||||||
|
await addLog(`步骤 ${step}:最近邮件样本:${sample}`, 'info');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
lastError = err;
|
||||||
|
await addLog(`步骤 ${step}:YYDS Mail 轮询失败:${err.message}`, 'warn');
|
||||||
|
}
|
||||||
|
if (attempt < maxAttempts) {
|
||||||
|
await sleepWithStop(intervalMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError || new Error(`步骤 ${step}:未在 YYDS Mail 中找到新的匹配验证码。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearYydsMailRuntimeState(options = {}) {
|
||||||
|
await setState({
|
||||||
|
currentYydsMailInbox: null,
|
||||||
|
...(options.clearEmail ? { email: null } : {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
clearYydsMailRuntimeState,
|
||||||
|
ensureYydsMailConfig,
|
||||||
|
fetchYydsMailAddress,
|
||||||
|
getYydsMailConfig,
|
||||||
|
getYydsMailMessageDetail,
|
||||||
|
listYydsMailMessages,
|
||||||
|
pollYydsMailVerificationCode,
|
||||||
|
requestYydsMailJson,
|
||||||
|
resolveYydsMailPollTargetEmail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
createYydsMailProvider,
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
})(typeof self !== 'undefined' ? self : globalThis, function createMailProviderUtils() {
|
})(typeof self !== 'undefined' ? self : globalThis, function createMailProviderUtils() {
|
||||||
const HOTMAIL_PROVIDER = 'hotmail-api';
|
const HOTMAIL_PROVIDER = 'hotmail-api';
|
||||||
const GMAIL_PROVIDER = 'gmail';
|
const GMAIL_PROVIDER = 'gmail';
|
||||||
|
const YYDS_MAIL_PROVIDER = 'yyds-mail';
|
||||||
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 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_INBOX = 'icloud-inbox';
|
||||||
const ICLOUD_TARGET_MAILBOX_TYPE_FORWARD = 'forward-mailbox';
|
const ICLOUD_TARGET_MAILBOX_TYPE_FORWARD = 'forward-mailbox';
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
const normalized = String(value || '').trim().toLowerCase();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
switch (normalized) {
|
switch (normalized) {
|
||||||
case HOTMAIL_PROVIDER:
|
case HOTMAIL_PROVIDER:
|
||||||
|
case YYDS_MAIL_PROVIDER:
|
||||||
case '163':
|
case '163':
|
||||||
case '163-vip':
|
case '163-vip':
|
||||||
case '126':
|
case '126':
|
||||||
@@ -76,6 +78,9 @@
|
|||||||
if (provider === HOTMAIL_PROVIDER) {
|
if (provider === HOTMAIL_PROVIDER) {
|
||||||
return { provider: HOTMAIL_PROVIDER, label: 'Hotmail(微软 Graph)' };
|
return { provider: HOTMAIL_PROVIDER, label: 'Hotmail(微软 Graph)' };
|
||||||
}
|
}
|
||||||
|
if (provider === YYDS_MAIL_PROVIDER) {
|
||||||
|
return { provider: YYDS_MAIL_PROVIDER, label: 'YYDS Mail' };
|
||||||
|
}
|
||||||
if (provider === '163') {
|
if (provider === '163') {
|
||||||
return {
|
return {
|
||||||
source: 'mail-163',
|
source: 'mail-163',
|
||||||
@@ -121,6 +126,7 @@
|
|||||||
return {
|
return {
|
||||||
GMAIL_PROVIDER,
|
GMAIL_PROVIDER,
|
||||||
HOTMAIL_PROVIDER,
|
HOTMAIL_PROVIDER,
|
||||||
|
YYDS_MAIL_PROVIDER,
|
||||||
getIcloudForwardMailConfig,
|
getIcloudForwardMailConfig,
|
||||||
getIcloudForwardMailProviderOptions,
|
getIcloudForwardMailProviderOptions,
|
||||||
getMailProviderConfig,
|
getMailProviderConfig,
|
||||||
|
|||||||
@@ -420,6 +420,7 @@
|
|||||||
<option value="custom">自定义邮箱</option>
|
<option value="custom">自定义邮箱</option>
|
||||||
<option value="hotmail-api">Hotmail(账号池)</option>
|
<option value="hotmail-api">Hotmail(账号池)</option>
|
||||||
<option value="luckmail-api">LuckMail(API 购邮)</option>
|
<option value="luckmail-api">LuckMail(API 购邮)</option>
|
||||||
|
<option value="yyds-mail">YYDS Mail</option>
|
||||||
<option value="icloud">iCloud 邮箱</option>
|
<option value="icloud">iCloud 邮箱</option>
|
||||||
<option value="163">163 邮箱 (mail.163.com) 版本老旧,请自行维护使用</option>
|
<option value="163">163 邮箱 (mail.163.com) 版本老旧,请自行维护使用</option>
|
||||||
<option value="163-vip">163 VIP 邮箱 (vip.163.com) 版本老旧,请自行维护使用</option>
|
<option value="163-vip">163 VIP 邮箱 (vip.163.com) 版本老旧,请自行维护使用</option>
|
||||||
@@ -778,6 +779,29 @@
|
|||||||
<input type="text" id="input-cloud-mail-domain" class="data-input" placeholder="例如 mail.example.com" />
|
<input type="text" id="input-cloud-mail-domain" class="data-input" placeholder="例如 mail.example.com" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="yyds-mail-section" class="data-card hotmail-card" style="display:none;">
|
||||||
|
<div class="section-mini-header">
|
||||||
|
<div class="section-mini-copy">
|
||||||
|
<span class="section-label">YYDS Mail</span>
|
||||||
|
<span class="data-value">自动创建临时邮箱并通过 API 收取验证码</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="data-row">
|
||||||
|
<span class="data-label">API Key</span>
|
||||||
|
<div class="input-with-icon">
|
||||||
|
<input type="password" id="input-yyds-mail-api-key" class="data-input data-input-with-icon mono"
|
||||||
|
placeholder="请输入 YYDS Mail API Key(AC-...)" />
|
||||||
|
<button id="btn-toggle-yyds-mail-api-key" class="input-icon-btn" type="button"
|
||||||
|
data-password-toggle="input-yyds-mail-api-key" data-show-label="显示 YYDS Mail API Key"
|
||||||
|
data-hide-label="隐藏 YYDS Mail API Key" aria-label="显示 YYDS Mail API Key" title="显示 YYDS Mail API Key"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="data-row">
|
||||||
|
<span class="data-label">Base URL</span>
|
||||||
|
<input type="text" id="input-yyds-mail-base-url" class="data-input mono"
|
||||||
|
placeholder="https://maliapi.215.im/v1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="hotmail-section" class="data-card hotmail-card" style="display:none;">
|
<div id="hotmail-section" class="data-card hotmail-card" style="display:none;">
|
||||||
<div class="section-mini-header">
|
<div class="section-mini-header">
|
||||||
<div class="section-mini-copy">
|
<div class="section-mini-copy">
|
||||||
@@ -1764,6 +1788,7 @@
|
|||||||
<script src="../mail-provider-utils.js"></script>
|
<script src="../mail-provider-utils.js"></script>
|
||||||
<script src="../hotmail-utils.js"></script>
|
<script src="../hotmail-utils.js"></script>
|
||||||
<script src="../luckmail-utils.js"></script>
|
<script src="../luckmail-utils.js"></script>
|
||||||
|
<script src="../yyds-mail-utils.js"></script>
|
||||||
<script src="../shared/flow-capabilities.js"></script>
|
<script src="../shared/flow-capabilities.js"></script>
|
||||||
<script src="../data/step-definitions.js"></script>
|
<script src="../data/step-definitions.js"></script>
|
||||||
<script src="update-service.js"></script>
|
<script src="update-service.js"></script>
|
||||||
|
|||||||
+97
-3
@@ -274,6 +274,9 @@ const inputCloudMailAdminEmail = document.getElementById('input-cloud-mail-admin
|
|||||||
const inputCloudMailAdminPassword = document.getElementById('input-cloud-mail-admin-password');
|
const inputCloudMailAdminPassword = document.getElementById('input-cloud-mail-admin-password');
|
||||||
const inputCloudMailReceiveMailbox = document.getElementById('input-cloud-mail-receive-mailbox');
|
const inputCloudMailReceiveMailbox = document.getElementById('input-cloud-mail-receive-mailbox');
|
||||||
const inputCloudMailDomain = document.getElementById('input-cloud-mail-domain');
|
const inputCloudMailDomain = document.getElementById('input-cloud-mail-domain');
|
||||||
|
const yydsMailSection = document.getElementById('yyds-mail-section');
|
||||||
|
const inputYydsMailApiKey = document.getElementById('input-yyds-mail-api-key');
|
||||||
|
const inputYydsMailBaseUrl = document.getElementById('input-yyds-mail-base-url');
|
||||||
const hotmailSection = document.getElementById('hotmail-section');
|
const hotmailSection = document.getElementById('hotmail-section');
|
||||||
const mail2925Section = document.getElementById('mail2925-section');
|
const mail2925Section = document.getElementById('mail2925-section');
|
||||||
const luckmailSection = document.getElementById('luckmail-section');
|
const luckmailSection = document.getElementById('luckmail-section');
|
||||||
@@ -971,9 +974,11 @@ const ICLOUD_PROVIDER = 'icloud';
|
|||||||
const GMAIL_PROVIDER = 'gmail';
|
const GMAIL_PROVIDER = 'gmail';
|
||||||
const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
|
const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
|
||||||
const LUCKMAIL_PROVIDER = 'luckmail-api';
|
const LUCKMAIL_PROVIDER = 'luckmail-api';
|
||||||
|
const YYDS_MAIL_PROVIDER = 'yyds-mail';
|
||||||
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
|
const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
|
||||||
const DEFAULT_LUCKMAIL_BASE_URL = 'https://mails.luckyous.com';
|
const DEFAULT_LUCKMAIL_BASE_URL = 'https://mails.luckyous.com';
|
||||||
const DEFAULT_LUCKMAIL_EMAIL_TYPE = 'ms_graph';
|
const DEFAULT_LUCKMAIL_EMAIL_TYPE = 'ms_graph';
|
||||||
|
const DEFAULT_YYDS_MAIL_BASE_URL = window.YydsMailUtils?.DEFAULT_YYDS_MAIL_BASE_URL || 'https://maliapi.215.im/v1';
|
||||||
const DISPLAY_TIMEZONE = 'Asia/Shanghai';
|
const DISPLAY_TIMEZONE = 'Asia/Shanghai';
|
||||||
const DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL = 'http://127.0.0.1:17373';
|
const DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL = 'http://127.0.0.1:17373';
|
||||||
const CONTRIBUTION_UPLOAD_URL = 'https://flowpilot.qlhazycoder.top/';
|
const CONTRIBUTION_UPLOAD_URL = 'https://flowpilot.qlhazycoder.top/';
|
||||||
@@ -1210,6 +1215,17 @@ function getCurrentRegistrationEmailUiCopy() {
|
|||||||
if (isCustomMailProvider()) {
|
if (isCustomMailProvider()) {
|
||||||
return getCustomMailProviderUiCopy();
|
return getCustomMailProviderUiCopy();
|
||||||
}
|
}
|
||||||
|
const useYydsMail = typeof isYydsMailProvider === 'function'
|
||||||
|
? isYydsMailProvider()
|
||||||
|
: String(selectMailProvider.value || '').trim().toLowerCase() === 'yyds-mail';
|
||||||
|
if (useYydsMail) {
|
||||||
|
return {
|
||||||
|
buttonLabel: '获取',
|
||||||
|
placeholder: '点击获取 YYDS Mail 邮箱,或手动粘贴邮箱',
|
||||||
|
successVerb: '获取',
|
||||||
|
label: 'YYDS Mail',
|
||||||
|
};
|
||||||
|
}
|
||||||
if (usesGeneratedAliasMailProvider()) {
|
if (usesGeneratedAliasMailProvider()) {
|
||||||
return getManagedAliasProviderUiCopy();
|
return getManagedAliasProviderUiCopy();
|
||||||
}
|
}
|
||||||
@@ -1554,6 +1570,11 @@ const MAIL_PROVIDER_LOGIN_CONFIGS = {
|
|||||||
url: 'https://github.com/QLHazyCoder/cloudflare_temp_email',
|
url: 'https://github.com/QLHazyCoder/cloudflare_temp_email',
|
||||||
buttonLabel: '部署',
|
buttonLabel: '部署',
|
||||||
},
|
},
|
||||||
|
[YYDS_MAIL_PROVIDER]: {
|
||||||
|
label: 'YYDS Mail',
|
||||||
|
url: 'https://vip.215.im/docs',
|
||||||
|
buttonLabel: '文档',
|
||||||
|
},
|
||||||
'2925': {
|
'2925': {
|
||||||
label: '2925 邮箱',
|
label: '2925 邮箱',
|
||||||
url: 'https://2925.com/#/mailList',
|
url: 'https://2925.com/#/mailList',
|
||||||
@@ -2860,8 +2881,12 @@ function restoreCustomEmailPoolEntriesFromState(state = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function usesCustomEmailPoolGenerator(provider = selectMailProvider.value) {
|
function usesCustomEmailPoolGenerator(provider = selectMailProvider.value) {
|
||||||
|
const providerUsesYydsMail = typeof isYydsMailProvider === 'function'
|
||||||
|
? isYydsMailProvider(provider)
|
||||||
|
: String(provider || '').trim().toLowerCase() === 'yyds-mail';
|
||||||
return !isCustomMailProvider(provider)
|
return !isCustomMailProvider(provider)
|
||||||
&& !isLuckmailProvider(provider)
|
&& !isLuckmailProvider(provider)
|
||||||
|
&& !providerUsesYydsMail
|
||||||
&& getSelectedEmailGenerator() === CUSTOM_EMAIL_POOL_GENERATOR;
|
&& getSelectedEmailGenerator() === CUSTOM_EMAIL_POOL_GENERATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3207,10 +3232,25 @@ function applyCloudMailSettingsState(state = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyYydsMailSettingsState(state = {}) {
|
||||||
|
const normalizeYydsBaseUrlValue = typeof normalizeYydsMailBaseUrl === 'function'
|
||||||
|
? normalizeYydsMailBaseUrl
|
||||||
|
: ((value) => String(value || '').trim() || 'https://maliapi.215.im/v1');
|
||||||
|
if (inputYydsMailApiKey) {
|
||||||
|
inputYydsMailApiKey.value = state?.yydsMailApiKey || '';
|
||||||
|
}
|
||||||
|
if (inputYydsMailBaseUrl) {
|
||||||
|
inputYydsMailBaseUrl.value = normalizeYydsBaseUrlValue(state?.yydsMailBaseUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function collectSettingsPayload() {
|
function collectSettingsPayload() {
|
||||||
const defaultGpcHelperApiUrl = typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined'
|
const defaultGpcHelperApiUrl = typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined'
|
||||||
? DEFAULT_GPC_HELPER_API_URL
|
? DEFAULT_GPC_HELPER_API_URL
|
||||||
: 'https://gpc.qlhazycoder.top';
|
: 'https://gpc.qlhazycoder.top';
|
||||||
|
const normalizeYydsBaseUrlValue = typeof normalizeYydsMailBaseUrl === 'function'
|
||||||
|
? normalizeYydsMailBaseUrl
|
||||||
|
: ((value) => String(value || '').trim() || 'https://maliapi.215.im/v1');
|
||||||
const { domains, activeDomain } = getCloudflareDomainsFromState();
|
const { domains, activeDomain } = getCloudflareDomainsFromState();
|
||||||
const selectedCloudflareDomain = normalizeCloudflareDomainValue(
|
const selectedCloudflareDomain = normalizeCloudflareDomainValue(
|
||||||
!cloudflareDomainEditMode ? selectCfDomain.value : activeDomain
|
!cloudflareDomainEditMode ? selectCfDomain.value : activeDomain
|
||||||
@@ -3981,6 +4021,8 @@ function collectSettingsPayload() {
|
|||||||
cloudMailAdminPassword: (typeof inputCloudMailAdminPassword !== 'undefined' && inputCloudMailAdminPassword) ? inputCloudMailAdminPassword.value : '',
|
cloudMailAdminPassword: (typeof inputCloudMailAdminPassword !== 'undefined' && inputCloudMailAdminPassword) ? inputCloudMailAdminPassword.value : '',
|
||||||
cloudMailReceiveMailbox: normalizeCloudMailReceiveMailboxInput((typeof inputCloudMailReceiveMailbox !== 'undefined' && inputCloudMailReceiveMailbox) ? inputCloudMailReceiveMailbox.value : ''),
|
cloudMailReceiveMailbox: normalizeCloudMailReceiveMailboxInput((typeof inputCloudMailReceiveMailbox !== 'undefined' && inputCloudMailReceiveMailbox) ? inputCloudMailReceiveMailbox.value : ''),
|
||||||
cloudMailDomain: normalizeCloudMailDomainInput((typeof inputCloudMailDomain !== 'undefined' && inputCloudMailDomain) ? inputCloudMailDomain.value : ''),
|
cloudMailDomain: normalizeCloudMailDomainInput((typeof inputCloudMailDomain !== 'undefined' && inputCloudMailDomain) ? inputCloudMailDomain.value : ''),
|
||||||
|
yydsMailApiKey: (typeof inputYydsMailApiKey !== 'undefined' && inputYydsMailApiKey) ? inputYydsMailApiKey.value.trim() : '',
|
||||||
|
yydsMailBaseUrl: normalizeYydsBaseUrlValue((typeof inputYydsMailBaseUrl !== 'undefined' && inputYydsMailBaseUrl) ? inputYydsMailBaseUrl.value : ''),
|
||||||
autoRunSkipFailures: inputAutoSkipFailures.checked,
|
autoRunSkipFailures: inputAutoSkipFailures.checked,
|
||||||
autoRunFallbackThreadIntervalMinutes: normalizeAutoRunThreadIntervalMinutes(inputAutoSkipFailuresThreadIntervalMinutes.value),
|
autoRunFallbackThreadIntervalMinutes: normalizeAutoRunThreadIntervalMinutes(inputAutoSkipFailuresThreadIntervalMinutes.value),
|
||||||
step6CookieCleanupEnabled: typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled
|
step6CookieCleanupEnabled: typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled
|
||||||
@@ -9359,8 +9401,11 @@ function applySettingsState(state) {
|
|||||||
}
|
}
|
||||||
inputCodex2ApiUrl.value = state?.codex2apiUrl || '';
|
inputCodex2ApiUrl.value = state?.codex2apiUrl || '';
|
||||||
inputCodex2ApiAdminKey.value = state?.codex2apiAdminKey || '';
|
inputCodex2ApiAdminKey.value = state?.codex2apiAdminKey || '';
|
||||||
|
const yydsMailProvider = typeof YYDS_MAIL_PROVIDER === 'string'
|
||||||
|
? YYDS_MAIL_PROVIDER
|
||||||
|
: 'yyds-mail';
|
||||||
const restoredMailProvider = isCustomMailProvider(state?.mailProvider)
|
const restoredMailProvider = isCustomMailProvider(state?.mailProvider)
|
||||||
|| [ICLOUD_PROVIDER, 'hotmail-api', GMAIL_PROVIDER, 'luckmail-api', '163', '163-vip', '126', 'qq', 'inbucket', '2925', 'cloudflare-temp-email', 'cloudmail'].includes(String(state?.mailProvider || '').trim())
|
|| [ICLOUD_PROVIDER, 'hotmail-api', GMAIL_PROVIDER, 'luckmail-api', yydsMailProvider, '163', '163-vip', '126', 'qq', 'inbucket', '2925', 'cloudflare-temp-email', 'cloudmail'].includes(String(state?.mailProvider || '').trim())
|
||||||
? String(state?.mailProvider || '163').trim()
|
? String(state?.mailProvider || '163').trim()
|
||||||
: (String(state?.emailGenerator || '').trim().toLowerCase() === 'custom'
|
: (String(state?.emailGenerator || '').trim().toLowerCase() === 'custom'
|
||||||
|| String(state?.emailGenerator || '').trim().toLowerCase() === 'manual'
|
|| String(state?.emailGenerator || '').trim().toLowerCase() === 'manual'
|
||||||
@@ -9442,6 +9487,9 @@ function applySettingsState(state) {
|
|||||||
if (typeof applyCloudMailSettingsState === 'function') {
|
if (typeof applyCloudMailSettingsState === 'function') {
|
||||||
applyCloudMailSettingsState(state);
|
applyCloudMailSettingsState(state);
|
||||||
}
|
}
|
||||||
|
if (typeof applyYydsMailSettingsState === 'function') {
|
||||||
|
applyYydsMailSettingsState(state);
|
||||||
|
}
|
||||||
renderCloudflareDomainOptions(state?.cloudflareDomain || '');
|
renderCloudflareDomainOptions(state?.cloudflareDomain || '');
|
||||||
setCloudflareDomainEditMode(false, { clearInput: true });
|
setCloudflareDomainEditMode(false, { clearInput: true });
|
||||||
inputAutoSkipFailures.checked = Boolean(state?.autoRunSkipFailures);
|
inputAutoSkipFailures.checked = Boolean(state?.autoRunSkipFailures);
|
||||||
@@ -10157,6 +10205,13 @@ function isLuckmailProvider(provider = selectMailProvider.value) {
|
|||||||
return String(provider || '').trim().toLowerCase() === LUCKMAIL_PROVIDER;
|
return String(provider || '').trim().toLowerCase() === LUCKMAIL_PROVIDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isYydsMailProvider(provider = selectMailProvider.value) {
|
||||||
|
const yydsMailProvider = typeof YYDS_MAIL_PROVIDER === 'string'
|
||||||
|
? YYDS_MAIL_PROVIDER
|
||||||
|
: 'yyds-mail';
|
||||||
|
return String(provider || '').trim().toLowerCase() === yydsMailProvider;
|
||||||
|
}
|
||||||
|
|
||||||
function isIcloudMailProvider(provider = selectMailProvider.value) {
|
function isIcloudMailProvider(provider = selectMailProvider.value) {
|
||||||
return String(provider || '').trim().toLowerCase() === ICLOUD_PROVIDER;
|
return String(provider || '').trim().toLowerCase() === ICLOUD_PROVIDER;
|
||||||
}
|
}
|
||||||
@@ -10188,6 +10243,29 @@ function normalizeLuckmailEmailType(value = '') {
|
|||||||
: DEFAULT_LUCKMAIL_EMAIL_TYPE;
|
: DEFAULT_LUCKMAIL_EMAIL_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailBaseUrl(value = '') {
|
||||||
|
if (window.YydsMailUtils?.normalizeYydsMailBaseUrl) {
|
||||||
|
return window.YydsMailUtils.normalizeYydsMailBaseUrl(value);
|
||||||
|
}
|
||||||
|
const trimmed = String(value || '').trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
return DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
}
|
||||||
|
const candidate = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
|
||||||
|
try {
|
||||||
|
const parsed = new URL(candidate);
|
||||||
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||||
|
return DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
}
|
||||||
|
parsed.hash = '';
|
||||||
|
parsed.search = '';
|
||||||
|
parsed.pathname = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\/+$/, '');
|
||||||
|
return `${parsed.origin}${parsed.pathname}` || DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getSelectedEmailGenerator() {
|
function getSelectedEmailGenerator() {
|
||||||
const generator = String(selectEmailGenerator.value || '').trim().toLowerCase();
|
const generator = String(selectEmailGenerator.value || '').trim().toLowerCase();
|
||||||
if (generator === 'custom' || generator === 'manual') {
|
if (generator === 'custom' || generator === 'manual') {
|
||||||
@@ -10678,10 +10756,13 @@ function updateMailProviderUI() {
|
|||||||
const useInbucket = selectMailProvider.value === 'inbucket';
|
const useInbucket = selectMailProvider.value === 'inbucket';
|
||||||
const useHotmail = selectMailProvider.value === 'hotmail-api';
|
const useHotmail = selectMailProvider.value === 'hotmail-api';
|
||||||
const useLuckmail = canShowLuckmail && isLuckmailProvider();
|
const useLuckmail = canShowLuckmail && isLuckmailProvider();
|
||||||
|
const useYydsMail = typeof isYydsMailProvider === 'function'
|
||||||
|
? isYydsMailProvider()
|
||||||
|
: String(selectMailProvider.value || '').trim().toLowerCase() === 'yyds-mail';
|
||||||
const useCustomEmail = isCustomMailProvider();
|
const useCustomEmail = isCustomMailProvider();
|
||||||
const useCustomMailProviderPool = useCustomEmail && usesCustomMailProviderPool(selectMailProvider.value);
|
const useCustomMailProviderPool = useCustomEmail && usesCustomMailProviderPool(selectMailProvider.value);
|
||||||
const useIcloudProvider = isIcloudMailProvider();
|
const useIcloudProvider = isIcloudMailProvider();
|
||||||
const useEmailGenerator = !useHotmail && !useLuckmail && !useCustomEmail && (!useGeneratedAlias || useGmail);
|
const useEmailGenerator = !useHotmail && !useLuckmail && !useYydsMail && !useCustomEmail && (!useGeneratedAlias || useGmail);
|
||||||
const useCloudflareTempEmailProvider = selectMailProvider.value === 'cloudflare-temp-email';
|
const useCloudflareTempEmailProvider = selectMailProvider.value === 'cloudflare-temp-email';
|
||||||
const useCloudMailProvider = selectMailProvider.value === 'cloudmail';
|
const useCloudMailProvider = selectMailProvider.value === 'cloudmail';
|
||||||
const aliasUiCopy = useGeneratedAlias
|
const aliasUiCopy = useGeneratedAlias
|
||||||
@@ -10751,6 +10832,9 @@ function updateMailProviderUI() {
|
|||||||
if (typeof cloudMailSection !== 'undefined' && cloudMailSection) {
|
if (typeof cloudMailSection !== 'undefined' && cloudMailSection) {
|
||||||
cloudMailSection.style.display = showCloudMailSettings ? '' : 'none';
|
cloudMailSection.style.display = showCloudMailSettings ? '' : 'none';
|
||||||
}
|
}
|
||||||
|
if (typeof yydsMailSection !== 'undefined' && yydsMailSection) {
|
||||||
|
yydsMailSection.style.display = useYydsMail ? '' : 'none';
|
||||||
|
}
|
||||||
if (typeof rowCloudMailBaseUrl !== 'undefined' && rowCloudMailBaseUrl) rowCloudMailBaseUrl.style.display = showCloudMailSettings ? '' : 'none';
|
if (typeof rowCloudMailBaseUrl !== 'undefined' && rowCloudMailBaseUrl) rowCloudMailBaseUrl.style.display = showCloudMailSettings ? '' : 'none';
|
||||||
if (typeof rowCloudMailAdminEmail !== 'undefined' && rowCloudMailAdminEmail) rowCloudMailAdminEmail.style.display = showCloudMailSettings ? '' : 'none';
|
if (typeof rowCloudMailAdminEmail !== 'undefined' && rowCloudMailAdminEmail) rowCloudMailAdminEmail.style.display = showCloudMailSettings ? '' : 'none';
|
||||||
if (typeof rowCloudMailAdminPassword !== 'undefined' && rowCloudMailAdminPassword) rowCloudMailAdminPassword.style.display = showCloudMailSettings ? '' : 'none';
|
if (typeof rowCloudMailAdminPassword !== 'undefined' && rowCloudMailAdminPassword) rowCloudMailAdminPassword.style.display = showCloudMailSettings ? '' : 'none';
|
||||||
@@ -10818,7 +10902,7 @@ function updateMailProviderUI() {
|
|||||||
}
|
}
|
||||||
inputEmailPrefix.style.display = '';
|
inputEmailPrefix.style.display = '';
|
||||||
inputEmailPrefix.readOnly = false;
|
inputEmailPrefix.readOnly = false;
|
||||||
selectEmailGenerator.disabled = useHotmail || useLuckmail || useCustomEmail || (useGeneratedAlias && !useGmail);
|
selectEmailGenerator.disabled = useHotmail || useLuckmail || useYydsMail || useCustomEmail || (useGeneratedAlias && !useGmail);
|
||||||
if (useGmail) {
|
if (useGmail) {
|
||||||
labelEmailPrefix.textContent = 'Gmail 原邮箱';
|
labelEmailPrefix.textContent = 'Gmail 原邮箱';
|
||||||
inputEmailPrefix.placeholder = '例如 yourname@gmail.com';
|
inputEmailPrefix.placeholder = '例如 yourname@gmail.com';
|
||||||
@@ -12945,6 +13029,16 @@ inputVpsPassword.addEventListener('blur', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[inputYydsMailApiKey, inputYydsMailBaseUrl].forEach((input) => {
|
||||||
|
input?.addEventListener('input', () => {
|
||||||
|
markSettingsDirty(true);
|
||||||
|
scheduleSettingsAutoSave();
|
||||||
|
});
|
||||||
|
input?.addEventListener('blur', () => {
|
||||||
|
saveSettings({ silent: true }).catch(() => { });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
selectLuckmailEmailType?.addEventListener('change', () => {
|
selectLuckmailEmailType?.addEventListener('change', () => {
|
||||||
markSettingsDirty(true);
|
markSettingsDirty(true);
|
||||||
saveSettings({ silent: true }).catch(() => { });
|
saveSettings({ silent: true }).catch(() => { });
|
||||||
|
|||||||
@@ -688,12 +688,15 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
|||||||
" luckmailBaseUrl: 'https://mails.luckyous.com',",
|
" luckmailBaseUrl: 'https://mails.luckyous.com',",
|
||||||
" luckmailEmailType: 'ms_graph',",
|
" luckmailEmailType: 'ms_graph',",
|
||||||
" luckmailDomain: '',",
|
" luckmailDomain: '',",
|
||||||
|
" yydsMailApiKey: '',",
|
||||||
|
" yydsMailBaseUrl: 'https://maliapi.215.im/v1',",
|
||||||
" panelMode: 'cpa',",
|
" panelMode: 'cpa',",
|
||||||
' luckmailUsedPurchases: {},',
|
' luckmailUsedPurchases: {},',
|
||||||
' luckmailPreserveTagId: 0,',
|
' luckmailPreserveTagId: 0,',
|
||||||
" luckmailPreserveTagName: '保留',",
|
" luckmailPreserveTagName: '保留',",
|
||||||
" currentLuckmailPurchase: { token: 'stale' },",
|
" currentLuckmailPurchase: { token: 'stale' },",
|
||||||
" currentLuckmailMailCursor: { messageId: 'stale' },",
|
" currentLuckmailMailCursor: { messageId: 'stale' },",
|
||||||
|
" currentYydsMailInbox: { address: 'stale@example.com', token: 'stale-token' },",
|
||||||
' currentPhoneActivation: null,',
|
' currentPhoneActivation: null,',
|
||||||
' reusablePhoneActivation: null,',
|
' reusablePhoneActivation: null,',
|
||||||
' email: null,',
|
' email: null,',
|
||||||
@@ -725,6 +728,12 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
|||||||
'function normalizeLuckmailUsedPurchases(value) {',
|
'function normalizeLuckmailUsedPurchases(value) {',
|
||||||
' return value || {};',
|
' return value || {};',
|
||||||
'}',
|
'}',
|
||||||
|
'function normalizeYydsMailApiKey(value) {',
|
||||||
|
" return String(value || '').trim();",
|
||||||
|
'}',
|
||||||
|
'function normalizeYydsMailBaseUrl(value) {',
|
||||||
|
" return (String(value || '').trim() || 'https://maliapi.215.im/v1').replace(/\\/$/, '');",
|
||||||
|
'}',
|
||||||
'async function getPersistedSettings() {',
|
'async function getPersistedSettings() {',
|
||||||
" return { mailProvider: '163' };",
|
" return { mailProvider: '163' };",
|
||||||
'}',
|
'}',
|
||||||
@@ -752,6 +761,8 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
|||||||
" luckmailUsedPurchases: { 88: true },",
|
" luckmailUsedPurchases: { 88: true },",
|
||||||
' luckmailPreserveTagId: 9,',
|
' luckmailPreserveTagId: 9,',
|
||||||
" luckmailPreserveTagName: '保留',",
|
" luckmailPreserveTagName: '保留',",
|
||||||
|
" yydsMailApiKey: 'AC-session',",
|
||||||
|
" yydsMailBaseUrl: 'https://maliapi.215.im/v1/',",
|
||||||
' };',
|
' };',
|
||||||
' },',
|
' },',
|
||||||
' async clear() {',
|
' async clear() {',
|
||||||
@@ -786,6 +797,9 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
|||||||
assert.equal(snapshot.storedPayload.luckmailPreserveTagName, '保留');
|
assert.equal(snapshot.storedPayload.luckmailPreserveTagName, '保留');
|
||||||
assert.equal(snapshot.storedPayload.currentLuckmailPurchase, null);
|
assert.equal(snapshot.storedPayload.currentLuckmailPurchase, null);
|
||||||
assert.equal(snapshot.storedPayload.currentLuckmailMailCursor, null);
|
assert.equal(snapshot.storedPayload.currentLuckmailMailCursor, null);
|
||||||
|
assert.equal(snapshot.storedPayload.yydsMailApiKey, 'AC-session');
|
||||||
|
assert.equal(snapshot.storedPayload.yydsMailBaseUrl, 'https://maliapi.215.im/v1');
|
||||||
|
assert.equal(snapshot.storedPayload.currentYydsMailInbox, null);
|
||||||
assert.deepStrictEqual(snapshot.storedPayload.reusablePhoneActivation, {
|
assert.deepStrictEqual(snapshot.storedPayload.reusablePhoneActivation, {
|
||||||
activationId: 'rx-001',
|
activationId: 'rx-001',
|
||||||
phoneNumber: '66951112222',
|
phoneNumber: '66951112222',
|
||||||
|
|||||||
@@ -15,3 +15,38 @@ test('verification flow module exposes a factory', () => {
|
|||||||
|
|
||||||
assert.equal(typeof api?.createVerificationFlowHelpers, 'function');
|
assert.equal(typeof api?.createVerificationFlowHelpers, 'function');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('verification flow routes YYDS Mail provider to background poller', async () => {
|
||||||
|
const source = fs.readFileSync('background/verification-flow.js', 'utf8');
|
||||||
|
const globalScope = {};
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageBackgroundVerificationFlow;`)(globalScope);
|
||||||
|
const pollCalls = [];
|
||||||
|
const helpers = api.createVerificationFlowHelpers({
|
||||||
|
addLog: async () => {},
|
||||||
|
buildVerificationPollPayload: () => ({ maxAttempts: 1, intervalMs: 1 }),
|
||||||
|
getState: async () => ({}),
|
||||||
|
getTabId: async () => 1,
|
||||||
|
isStopError: () => false,
|
||||||
|
pollYydsMailVerificationCode: async (step, state, payload) => {
|
||||||
|
pollCalls.push({ step, state, payload });
|
||||||
|
return { ok: true, code: '123456', emailTimestamp: 1, mailId: 'msg-1' };
|
||||||
|
},
|
||||||
|
sendToContentScript: async () => ({}),
|
||||||
|
setState: async () => {},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
YYDS_MAIL_PROVIDER: 'yyds-mail',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await helpers.pollFreshVerificationCode(
|
||||||
|
4,
|
||||||
|
{ mailProvider: 'yyds-mail' },
|
||||||
|
{ provider: 'yyds-mail', label: 'YYDS Mail' },
|
||||||
|
{ disableTimeBudgetCap: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(result.code, '123456');
|
||||||
|
assert.equal(pollCalls.length, 1);
|
||||||
|
assert.equal(pollCalls[0].step, 4);
|
||||||
|
assert.equal(pollCalls[0].payload.maxAttempts, 1);
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const assert = require('node:assert/strict');
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
HOTMAIL_PROVIDER,
|
HOTMAIL_PROVIDER,
|
||||||
|
YYDS_MAIL_PROVIDER,
|
||||||
getIcloudForwardMailConfig,
|
getIcloudForwardMailConfig,
|
||||||
getIcloudForwardMailProviderOptions,
|
getIcloudForwardMailProviderOptions,
|
||||||
getMailProviderConfig,
|
getMailProviderConfig,
|
||||||
@@ -14,6 +15,7 @@ const {
|
|||||||
test('normalizeMailProvider accepts 126 and falls back to 163', () => {
|
test('normalizeMailProvider accepts 126 and falls back to 163', () => {
|
||||||
assert.equal(normalizeMailProvider('126'), '126');
|
assert.equal(normalizeMailProvider('126'), '126');
|
||||||
assert.equal(normalizeMailProvider('163-vip'), '163-vip');
|
assert.equal(normalizeMailProvider('163-vip'), '163-vip');
|
||||||
|
assert.equal(normalizeMailProvider(YYDS_MAIL_PROVIDER), YYDS_MAIL_PROVIDER);
|
||||||
assert.equal(normalizeMailProvider('unknown-provider'), '163');
|
assert.equal(normalizeMailProvider('unknown-provider'), '163');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,6 +40,16 @@ test('getMailProviderConfig preserves the hotmail provider sentinel', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getMailProviderConfig preserves the YYDS Mail provider sentinel', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getMailProviderConfig({ mailProvider: YYDS_MAIL_PROVIDER }),
|
||||||
|
{
|
||||||
|
provider: YYDS_MAIL_PROVIDER,
|
||||||
|
label: 'YYDS Mail',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('iCloud forward mailbox helpers normalize and expose supported providers', () => {
|
test('iCloud forward mailbox helpers normalize and expose supported providers', () => {
|
||||||
assert.equal(normalizeIcloudTargetMailboxType('forward-mailbox'), 'forward-mailbox');
|
assert.equal(normalizeIcloudTargetMailboxType('forward-mailbox'), 'forward-mailbox');
|
||||||
assert.equal(normalizeIcloudTargetMailboxType('unknown'), 'icloud-inbox');
|
assert.equal(normalizeIcloudTargetMailboxType('unknown'), 'icloud-inbox');
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
|
||||||
|
const utils = require('../yyds-mail-utils.js');
|
||||||
|
require('../background/yyds-mail-provider.js');
|
||||||
|
|
||||||
|
function createProviderApi(options = {}) {
|
||||||
|
const {
|
||||||
|
state = {
|
||||||
|
yydsMailApiKey: 'AC-demo',
|
||||||
|
yydsMailBaseUrl: 'https://maliapi.215.im/v1',
|
||||||
|
currentYydsMailInbox: null,
|
||||||
|
email: '',
|
||||||
|
},
|
||||||
|
fetchImpl,
|
||||||
|
} = options;
|
||||||
|
let currentState = { ...state };
|
||||||
|
const logs = [];
|
||||||
|
const persistCalls = [];
|
||||||
|
const stateUpdates = [];
|
||||||
|
const calls = [];
|
||||||
|
|
||||||
|
const api = globalThis.MultiPageBackgroundYydsMailProvider.createYydsMailProvider({
|
||||||
|
addLog: async (message, level) => logs.push({ message, level }),
|
||||||
|
buildYydsMailHeaders: utils.buildYydsMailHeaders,
|
||||||
|
DEFAULT_YYDS_MAIL_BASE_URL: utils.DEFAULT_YYDS_MAIL_BASE_URL,
|
||||||
|
fetchImpl: fetchImpl || (async (url, request = {}) => {
|
||||||
|
calls.push({
|
||||||
|
url: String(url),
|
||||||
|
method: request.method,
|
||||||
|
headers: request.headers,
|
||||||
|
body: request.body ? JSON.parse(request.body) : undefined,
|
||||||
|
});
|
||||||
|
if (String(url).endsWith('/accounts')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
text: async () => JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
id: 'inbox-1',
|
||||||
|
address: 'fresh@example.com',
|
||||||
|
token: 'temp-token',
|
||||||
|
expiresAt: '2026-03-15T12:00:00Z',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (String(url).includes('/messages/msg-1')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
text: async () => JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
id: 'msg-1',
|
||||||
|
from: { address: 'noreply@tm.openai.com' },
|
||||||
|
subject: 'OpenAI verification code',
|
||||||
|
text: 'Your ChatGPT code is 987654.',
|
||||||
|
createdAt: '2026-03-14T12:30:00Z',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (String(url).includes('/messages')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
text: async () => JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
messages: [{
|
||||||
|
id: 'msg-1',
|
||||||
|
from: { address: 'noreply@tm.openai.com' },
|
||||||
|
subject: 'OpenAI verification code',
|
||||||
|
createdAt: '2026-03-14T12:30:00Z',
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error(`unexpected URL ${url}`);
|
||||||
|
}),
|
||||||
|
getState: async () => currentState,
|
||||||
|
joinYydsMailUrl: utils.joinYydsMailUrl,
|
||||||
|
normalizeYydsMailAddress: utils.normalizeYydsMailAddress,
|
||||||
|
normalizeYydsMailApiKey: utils.normalizeYydsMailApiKey,
|
||||||
|
normalizeYydsMailBaseUrl: utils.normalizeYydsMailBaseUrl,
|
||||||
|
normalizeYydsMailCurrentInbox: utils.normalizeYydsMailCurrentInbox,
|
||||||
|
normalizeYydsMailInbox: utils.normalizeYydsMailInbox,
|
||||||
|
normalizeYydsMailMessageDetail: utils.normalizeYydsMailMessageDetail,
|
||||||
|
normalizeYydsMailMessages: utils.normalizeYydsMailMessages,
|
||||||
|
persistRegistrationEmailState: async (callState, email, persistOptions) => {
|
||||||
|
persistCalls.push({ state: callState, email, options: persistOptions });
|
||||||
|
currentState = { ...currentState, email };
|
||||||
|
},
|
||||||
|
pickVerificationMessageWithTimeFallback: (messages) => {
|
||||||
|
const match = messages.find((message) => message.verification_code);
|
||||||
|
return match
|
||||||
|
? {
|
||||||
|
match: {
|
||||||
|
code: match.verification_code,
|
||||||
|
receivedAt: Date.parse(match.receivedDateTime),
|
||||||
|
message: match,
|
||||||
|
},
|
||||||
|
usedRelaxedFilters: false,
|
||||||
|
usedTimeFallback: false,
|
||||||
|
}
|
||||||
|
: { match: null, usedRelaxedFilters: false, usedTimeFallback: false };
|
||||||
|
},
|
||||||
|
setState: async (updates) => {
|
||||||
|
stateUpdates.push(updates);
|
||||||
|
currentState = { ...currentState, ...updates };
|
||||||
|
},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
YYDS_MAIL_PROVIDER: utils.YYDS_MAIL_PROVIDER,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...api,
|
||||||
|
snapshot() {
|
||||||
|
return { calls, currentState, logs, persistCalls, stateUpdates };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('fetchYydsMailAddress creates an inbox with localPart only and stores the returned address/token', async () => {
|
||||||
|
const api = createProviderApi();
|
||||||
|
const email = await api.fetchYydsMailAddress(null, { localPart: 'fresh' });
|
||||||
|
const snapshot = api.snapshot();
|
||||||
|
|
||||||
|
assert.equal(email, 'fresh@example.com');
|
||||||
|
assert.equal(snapshot.calls[0].url, 'https://maliapi.215.im/v1/accounts');
|
||||||
|
assert.equal(snapshot.calls[0].method, 'POST');
|
||||||
|
assert.deepEqual(snapshot.calls[0].body, { localPart: 'fresh' });
|
||||||
|
assert.equal(snapshot.calls[0].headers['X-API-Key'], 'AC-demo');
|
||||||
|
assert.equal(snapshot.calls[0].headers.Authorization, undefined);
|
||||||
|
assert.equal(snapshot.currentState.currentYydsMailInbox.address, 'fresh@example.com');
|
||||||
|
assert.equal(snapshot.currentState.currentYydsMailInbox.token, 'temp-token');
|
||||||
|
assert.equal(snapshot.persistCalls[0].email, 'fresh@example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pollYydsMailVerificationCode lists messages with temp token and reads detail before matching code', async () => {
|
||||||
|
const api = createProviderApi({
|
||||||
|
state: {
|
||||||
|
yydsMailApiKey: 'AC-demo',
|
||||||
|
yydsMailBaseUrl: 'https://maliapi.215.im/v1',
|
||||||
|
currentYydsMailInbox: {
|
||||||
|
id: 'inbox-1',
|
||||||
|
address: 'fresh@example.com',
|
||||||
|
token: 'temp-token',
|
||||||
|
},
|
||||||
|
email: 'fresh@example.com',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await api.pollYydsMailVerificationCode(4, null, {
|
||||||
|
maxAttempts: 1,
|
||||||
|
intervalMs: 1,
|
||||||
|
senderFilters: ['openai'],
|
||||||
|
subjectFilters: ['code'],
|
||||||
|
});
|
||||||
|
const snapshot = api.snapshot();
|
||||||
|
const listCall = snapshot.calls.find((call) => call.url.includes('/messages?'));
|
||||||
|
const detailCall = snapshot.calls.find((call) => call.url.includes('/messages/msg-1'));
|
||||||
|
|
||||||
|
assert.equal(result.code, '987654');
|
||||||
|
assert.equal(listCall.headers.Authorization, 'Bearer temp-token');
|
||||||
|
assert.equal(listCall.headers['X-API-Key'], undefined);
|
||||||
|
assert.match(listCall.url, /address=fresh%40example\.com/);
|
||||||
|
assert.equal(detailCall.headers.Authorization, 'Bearer temp-token');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clearYydsMailRuntimeState clears current inbox and optionally current email', async () => {
|
||||||
|
const api = createProviderApi({
|
||||||
|
state: {
|
||||||
|
currentYydsMailInbox: { address: 'fresh@example.com', token: 'temp-token' },
|
||||||
|
email: 'fresh@example.com',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await api.clearYydsMailRuntimeState({ clearEmail: true });
|
||||||
|
assert.deepEqual(api.snapshot().stateUpdates.at(-1), {
|
||||||
|
currentYydsMailInbox: null,
|
||||||
|
email: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
|
||||||
|
const {
|
||||||
|
DEFAULT_YYDS_MAIL_BASE_URL,
|
||||||
|
buildYydsMailHeaders,
|
||||||
|
joinYydsMailUrl,
|
||||||
|
normalizeYydsMailBaseUrl,
|
||||||
|
normalizeYydsMailInbox,
|
||||||
|
normalizeYydsMailMessageDetail,
|
||||||
|
normalizeYydsMailMessages,
|
||||||
|
} = require('../yyds-mail-utils.js');
|
||||||
|
|
||||||
|
test('normalizeYydsMailBaseUrl defaults to official v1 endpoint and trims trailing slash', () => {
|
||||||
|
assert.equal(normalizeYydsMailBaseUrl(''), DEFAULT_YYDS_MAIL_BASE_URL);
|
||||||
|
assert.equal(normalizeYydsMailBaseUrl('maliapi.215.im/v1/'), DEFAULT_YYDS_MAIL_BASE_URL);
|
||||||
|
assert.equal(normalizeYydsMailBaseUrl('http://127.0.0.1:8787/v1/'), 'http://127.0.0.1:8787/v1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildYydsMailHeaders separates API key creation auth from temp-token read auth', () => {
|
||||||
|
assert.deepEqual(buildYydsMailHeaders({ apiKey: 'AC-demo' }, { json: true }), {
|
||||||
|
'X-API-Key': 'AC-demo',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
buildYydsMailHeaders(
|
||||||
|
{ apiKey: 'AC-demo', token: 'temp-token' },
|
||||||
|
{ tempToken: 'temp-token', includeConfigApiKey: false }
|
||||||
|
),
|
||||||
|
{
|
||||||
|
Authorization: 'Bearer temp-token',
|
||||||
|
Accept: 'application/json',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('joinYydsMailUrl appends query parameters to the normalized base URL', () => {
|
||||||
|
assert.equal(
|
||||||
|
joinYydsMailUrl('https://maliapi.215.im/v1/', '/messages', { address: 'a+b@example.com', limit: 20 }),
|
||||||
|
'https://maliapi.215.im/v1/messages?address=a%2Bb%40example.com&limit=20'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('normalizeYydsMailInbox keeps the final returned address and temp token', () => {
|
||||||
|
assert.deepEqual(normalizeYydsMailInbox({
|
||||||
|
id: 'inbox-1',
|
||||||
|
address: 'User@Example.com',
|
||||||
|
token: 'jwt-token',
|
||||||
|
expiresAt: '2026-03-15T12:00:00Z',
|
||||||
|
}), {
|
||||||
|
id: 'inbox-1',
|
||||||
|
address: 'user@example.com',
|
||||||
|
token: 'jwt-token',
|
||||||
|
expiresAt: '2026-03-15T12:00:00Z',
|
||||||
|
isActive: true,
|
||||||
|
createdAt: null,
|
||||||
|
raw: {
|
||||||
|
id: 'inbox-1',
|
||||||
|
address: 'User@Example.com',
|
||||||
|
token: 'jwt-token',
|
||||||
|
expiresAt: '2026-03-15T12:00:00Z',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('normalizeYydsMailMessages and detail expose Graph-like fields for verification matching', () => {
|
||||||
|
const messages = normalizeYydsMailMessages({
|
||||||
|
messages: [{
|
||||||
|
id: 'msg-1',
|
||||||
|
from: { address: 'noreply@tm.openai.com' },
|
||||||
|
to: [{ address: 'User@Example.com' }],
|
||||||
|
subject: 'OpenAI verification code',
|
||||||
|
createdAt: '2026-03-14T12:30:00Z',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
assert.equal(messages[0].id, 'msg-1');
|
||||||
|
assert.equal(messages[0].address, 'user@example.com');
|
||||||
|
assert.equal(messages[0].from.emailAddress.address, 'noreply@tm.openai.com');
|
||||||
|
|
||||||
|
const detail = normalizeYydsMailMessageDetail({
|
||||||
|
id: 'msg-1',
|
||||||
|
subject: 'OpenAI verification code',
|
||||||
|
from: { address: 'noreply@tm.openai.com' },
|
||||||
|
text: 'Your ChatGPT code is 654321.',
|
||||||
|
createdAt: '2026-03-14T12:30:00Z',
|
||||||
|
});
|
||||||
|
assert.equal(detail.verification_code, '654321');
|
||||||
|
assert.match(detail.bodyPreview, /654321/);
|
||||||
|
});
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
(function yydsMailUtilsModule(root, factory) {
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = factory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.YydsMailUtils = factory();
|
||||||
|
})(typeof self !== 'undefined' ? self : globalThis, function createYydsMailUtils() {
|
||||||
|
const DEFAULT_YYDS_MAIL_BASE_URL = 'https://maliapi.215.im/v1';
|
||||||
|
const YYDS_MAIL_PROVIDER = 'yyds-mail';
|
||||||
|
|
||||||
|
function firstNonEmptyString(values) {
|
||||||
|
for (const value of values) {
|
||||||
|
if (value === undefined || value === null) continue;
|
||||||
|
const normalized = String(value).trim();
|
||||||
|
if (normalized) return normalized;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailBaseUrl(rawValue = '') {
|
||||||
|
const value = String(rawValue || '').trim();
|
||||||
|
if (!value) return DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
|
||||||
|
const candidate = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(value)
|
||||||
|
? value
|
||||||
|
: `https://${value}`;
|
||||||
|
try {
|
||||||
|
const parsed = new URL(candidate);
|
||||||
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||||
|
return DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
}
|
||||||
|
parsed.hash = '';
|
||||||
|
parsed.search = '';
|
||||||
|
const pathname = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\/+$/, '');
|
||||||
|
return `${parsed.origin}${pathname}` || DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_YYDS_MAIL_BASE_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailApiKey(value = '') {
|
||||||
|
return String(value || '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailAddress(value = '') {
|
||||||
|
return String(value || '').trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildYydsMailHeaders(config = {}, options = {}) {
|
||||||
|
const headers = {};
|
||||||
|
const apiKey = firstNonEmptyString([
|
||||||
|
options.apiKey,
|
||||||
|
options.includeConfigApiKey === false ? '' : config.apiKey,
|
||||||
|
options.includeConfigApiKey === false ? '' : config.yydsMailApiKey,
|
||||||
|
]);
|
||||||
|
const tempToken = firstNonEmptyString([
|
||||||
|
options.tempToken,
|
||||||
|
config.tempToken,
|
||||||
|
config.token,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (apiKey) {
|
||||||
|
headers['X-API-Key'] = apiKey;
|
||||||
|
}
|
||||||
|
if (tempToken) {
|
||||||
|
headers.Authorization = /^bearer\s+/i.test(tempToken)
|
||||||
|
? tempToken
|
||||||
|
: `Bearer ${tempToken}`;
|
||||||
|
}
|
||||||
|
if (options.json) {
|
||||||
|
headers['Content-Type'] = 'application/json';
|
||||||
|
}
|
||||||
|
if (options.acceptJson !== false) {
|
||||||
|
headers.Accept = 'application/json';
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinYydsMailUrl(baseUrl, path, params = {}) {
|
||||||
|
const normalizedBase = normalizeYydsMailBaseUrl(baseUrl);
|
||||||
|
const normalizedPath = String(path || '').trim();
|
||||||
|
const url = new URL(`${normalizedBase}${normalizedPath.startsWith('/') ? '' : '/'}${normalizedPath}`);
|
||||||
|
for (const [key, value] of Object.entries(params || {})) {
|
||||||
|
if (value === undefined || value === null || value === '') continue;
|
||||||
|
url.searchParams.set(key, String(value));
|
||||||
|
}
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailInbox(payload = {}) {
|
||||||
|
const safePayload = payload && typeof payload === 'object' ? payload : {};
|
||||||
|
return {
|
||||||
|
id: firstNonEmptyString([safePayload.id, safePayload.inbox_id, safePayload.inboxId]),
|
||||||
|
address: normalizeYydsMailAddress(firstNonEmptyString([safePayload.address, safePayload.email])),
|
||||||
|
token: firstNonEmptyString([safePayload.token, safePayload.tempToken, safePayload.accessToken]),
|
||||||
|
expiresAt: firstNonEmptyString([safePayload.expiresAt, safePayload.expires_at]) || null,
|
||||||
|
isActive: safePayload.isActive !== undefined ? Boolean(safePayload.isActive) : true,
|
||||||
|
createdAt: firstNonEmptyString([safePayload.createdAt, safePayload.created_at]) || null,
|
||||||
|
raw: safePayload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailCurrentInbox(value = null) {
|
||||||
|
if (!value || typeof value !== 'object') return null;
|
||||||
|
const inbox = normalizeYydsMailInbox(value);
|
||||||
|
return inbox.address && inbox.token ? inbox : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getYydsMailRows(payload) {
|
||||||
|
if (Array.isArray(payload)) return payload;
|
||||||
|
if (!payload || typeof payload !== 'object') return [];
|
||||||
|
const candidates = [
|
||||||
|
payload.messages,
|
||||||
|
payload.items,
|
||||||
|
payload.list,
|
||||||
|
payload.records,
|
||||||
|
payload.data?.messages,
|
||||||
|
payload.data?.items,
|
||||||
|
payload.data?.list,
|
||||||
|
payload.data?.records,
|
||||||
|
];
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (Array.isArray(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripHtmlTags(value = '') {
|
||||||
|
return String(value || '')
|
||||||
|
.replace(/<style[\s\S]*?<\/style>/gi, ' ')
|
||||||
|
.replace(/<script[\s\S]*?<\/script>/gi, ' ')
|
||||||
|
.replace(/<[^>]+>/g, ' ')
|
||||||
|
.replace(/ /gi, ' ')
|
||||||
|
.replace(/&/gi, '&')
|
||||||
|
.replace(/</gi, '<')
|
||||||
|
.replace(/>/gi, '>')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailSender(value = {}) {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return { name: '', address: value };
|
||||||
|
}
|
||||||
|
const safeValue = value && typeof value === 'object' ? value : {};
|
||||||
|
return {
|
||||||
|
name: firstNonEmptyString([safeValue.name]),
|
||||||
|
address: firstNonEmptyString([safeValue.address, safeValue.email]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailRecipients(value) {
|
||||||
|
const list = Array.isArray(value) ? value : (value ? [value] : []);
|
||||||
|
return list.map((item) => {
|
||||||
|
if (typeof item === 'string') return { name: '', address: normalizeYydsMailAddress(item) };
|
||||||
|
const safeItem = item && typeof item === 'object' ? item : {};
|
||||||
|
return {
|
||||||
|
name: firstNonEmptyString([safeItem.name]),
|
||||||
|
address: normalizeYydsMailAddress(firstNonEmptyString([safeItem.address, safeItem.email])),
|
||||||
|
};
|
||||||
|
}).filter((item) => item.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailCreatedAt(value = '') {
|
||||||
|
const source = firstNonEmptyString([value]);
|
||||||
|
if (!source) return '';
|
||||||
|
const parsed = Date.parse(source);
|
||||||
|
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : source;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailMessage(row = {}) {
|
||||||
|
if (!row || typeof row !== 'object') return null;
|
||||||
|
const from = normalizeYydsMailSender(row.from || row.sender);
|
||||||
|
const to = normalizeYydsMailRecipients(row.to || row.recipients || row.recipient);
|
||||||
|
const htmlValue = Array.isArray(row.html)
|
||||||
|
? row.html.join('\n')
|
||||||
|
: firstNonEmptyString([row.html, row.bodyHtml, row.body_html]);
|
||||||
|
const textValue = firstNonEmptyString([row.text, row.bodyText, row.body_text, row.bodyPreview, row.preview]);
|
||||||
|
const raw = firstNonEmptyString([row.raw, row.source]);
|
||||||
|
const bodyPreview = (textValue || stripHtmlTags(htmlValue) || stripHtmlTags(raw))
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: firstNonEmptyString([row.id, row.message_id, row.messageId]),
|
||||||
|
address: normalizeYydsMailAddress(firstNonEmptyString([
|
||||||
|
row.address,
|
||||||
|
row.mailbox,
|
||||||
|
to[0]?.address,
|
||||||
|
])),
|
||||||
|
inboxId: firstNonEmptyString([row.inboxId, row.inbox_id]),
|
||||||
|
subject: firstNonEmptyString([row.subject, row.title]),
|
||||||
|
from: {
|
||||||
|
name: from.name,
|
||||||
|
emailAddress: {
|
||||||
|
address: from.address,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to,
|
||||||
|
seen: Boolean(row.seen),
|
||||||
|
bodyPreview,
|
||||||
|
raw: raw || htmlValue || textValue || '',
|
||||||
|
text: textValue,
|
||||||
|
html: htmlValue,
|
||||||
|
receivedDateTime: normalizeYydsMailCreatedAt(firstNonEmptyString([
|
||||||
|
row.createdAt,
|
||||||
|
row.created_at,
|
||||||
|
row.receivedDateTime,
|
||||||
|
row.date,
|
||||||
|
])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailMessages(payload) {
|
||||||
|
return getYydsMailRows(payload)
|
||||||
|
.map((row) => normalizeYydsMailMessage(row))
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractYydsMailVerificationCode(text) {
|
||||||
|
const source = String(text || '');
|
||||||
|
const matchCn = source.match(/(?:代码为|验证码[^0-9]*?)[\s::]*(\d{6})/i);
|
||||||
|
if (matchCn) return matchCn[1];
|
||||||
|
|
||||||
|
const matchOpenAiLogin = source.match(/(?:chatgpt\s+log-?in\s+code|enter\s+this\s+code)[^0-9]{0,24}(\d{6})/i);
|
||||||
|
if (matchOpenAiLogin) return matchOpenAiLogin[1];
|
||||||
|
|
||||||
|
const matchChatGPT = source.match(/your\s+chatgpt\s+code\s+is\s+(\d{6})/i);
|
||||||
|
if (matchChatGPT) return matchChatGPT[1];
|
||||||
|
|
||||||
|
const matchEn = source.match(/code(?:\s+is|[\s:])+(\d{6})/i);
|
||||||
|
if (matchEn) return matchEn[1];
|
||||||
|
|
||||||
|
const matchStandalone = source.match(/\b(\d{6})\b/);
|
||||||
|
return matchStandalone ? matchStandalone[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeYydsMailMessageDetail(payload = {}) {
|
||||||
|
const message = normalizeYydsMailMessage(payload);
|
||||||
|
if (!message) return null;
|
||||||
|
const combined = [
|
||||||
|
message.subject,
|
||||||
|
message.from?.emailAddress?.address,
|
||||||
|
message.bodyPreview,
|
||||||
|
message.text,
|
||||||
|
message.html,
|
||||||
|
message.raw,
|
||||||
|
].filter(Boolean).join(' ');
|
||||||
|
return {
|
||||||
|
...message,
|
||||||
|
bodyPreview: message.bodyPreview || stripHtmlTags(combined),
|
||||||
|
verification_code: extractYydsMailVerificationCode(combined) || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
DEFAULT_YYDS_MAIL_BASE_URL,
|
||||||
|
YYDS_MAIL_PROVIDER,
|
||||||
|
buildYydsMailHeaders,
|
||||||
|
extractYydsMailVerificationCode,
|
||||||
|
firstNonEmptyString,
|
||||||
|
joinYydsMailUrl,
|
||||||
|
normalizeYydsMailAddress,
|
||||||
|
normalizeYydsMailApiKey,
|
||||||
|
normalizeYydsMailBaseUrl,
|
||||||
|
normalizeYydsMailCurrentInbox,
|
||||||
|
normalizeYydsMailInbox,
|
||||||
|
normalizeYydsMailMessage,
|
||||||
|
normalizeYydsMailMessageDetail,
|
||||||
|
normalizeYydsMailMessages,
|
||||||
|
stripHtmlTags,
|
||||||
|
};
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user