2925邮箱添加邮箱接收和发送功能

This commit is contained in:
QLHazyCoder
2026-04-21 23:21:19 +08:00
parent ab935a33b1
commit cf42dc73fd
15 changed files with 515 additions and 58 deletions
@@ -15,3 +15,64 @@ test('generated email helper module exposes a factory', () => {
assert.equal(typeof api?.createGeneratedEmailHelpers, 'function');
});
test('generated email helper falls back to normal generator when 2925 is in receive mode', async () => {
const source = fs.readFileSync('background/generated-email-helpers.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageGeneratedEmailHelpers;`)(globalScope);
const events = [];
const helpers = api.createGeneratedEmailHelpers({
addLog: async () => {},
buildGeneratedAliasEmail: () => {
throw new Error('should not build alias in receive mode');
},
buildCloudflareTempEmailHeaders: () => ({}),
CLOUDFLARE_TEMP_EMAIL_GENERATOR: 'cloudflare-temp-email',
DUCK_AUTOFILL_URL: 'https://duckduckgo.com/email',
fetch: async () => ({ ok: true, text: async () => '{}' }),
fetchIcloudHideMyEmail: async () => {
throw new Error('should not use icloud generator');
},
getCloudflareTempEmailAddressFromResponse: () => '',
getCloudflareTempEmailConfig: () => ({ baseUrl: '', adminAuth: '', domain: '' }),
getState: async () => ({
mailProvider: '2925',
mail2925Mode: 'receive',
emailGenerator: 'duck',
}),
ensureMail2925AccountForFlow: async () => {
throw new Error('should not allocate 2925 account in receive mode');
},
joinCloudflareTempEmailUrl: () => '',
normalizeCloudflareDomain: () => '',
normalizeCloudflareTempEmailAddress: () => '',
normalizeEmailGenerator: (value) => String(value || '').trim().toLowerCase(),
isGeneratedAliasProvider: (_provider, mail2925Mode) => mail2925Mode === 'provide',
reuseOrCreateTab: async () => {},
sendToContentScript: async (_source, message) => {
events.push(message.type);
return { email: 'duck@example.com', generated: true };
},
setEmailState: async (email) => {
events.push(['email', email]);
},
throwIfStopped: () => {},
});
const email = await helpers.fetchGeneratedEmail({
mailProvider: '2925',
mail2925Mode: 'receive',
emailGenerator: 'duck',
}, {
mailProvider: '2925',
mail2925Mode: 'receive',
generator: 'duck',
});
assert.equal(email, 'duck@example.com');
assert.deepStrictEqual(events, [
'FETCH_DUCK_EMAIL',
['email', 'duck@example.com'],
]);
});
+92 -1
View File
@@ -164,7 +164,7 @@ return {
assert.deepEqual(api.getReadAndDeleteCalls(), ['baseline', 'new']);
});
test('handlePollEmail ignores targetEmail and still tests any matching ChatGPT mail', async () => {
test('handlePollEmail keeps ignoring targetEmail when receive-mode matching is disabled', async () => {
const bundle = [
extractFunction('normalizeMinuteTimestamp'),
extractFunction('handlePollEmail'),
@@ -242,12 +242,103 @@ return {
maxAttempts: 4,
intervalMs: 1,
targetEmail: 'expected@example.com',
mail2925MatchTargetEmail: false,
});
assert.equal(result.code, '112233');
assert.deepEqual(api.getReadAndDeleteCalls(), ['mail-1']);
});
test('handlePollEmail skips explicit mismatched target emails when receive-mode matching is enabled', async () => {
const bundle = [
extractFunction('extractEmails'),
extractFunction('emailMatchesTarget'),
extractFunction('getTargetEmailMatchState'),
extractFunction('normalizeMinuteTimestamp'),
extractFunction('handlePollEmail'),
].join('\n');
const api = new Function(`
let state = 'ready';
const seenCodes = new Set();
const readAndDeleteCalls = [];
const mismatchMail = {
id: 'mail-1',
text: 'ChatGPT verification code 112233 for another.user@example.com',
};
const targetMail = {
id: 'mail-2',
text: 'ChatGPT verification code 445566 for expected@example.com',
};
function findMailItems() {
return state === 'ready' ? [mismatchMail, targetMail] : [];
}
function getMailItemId(item) {
return item.id;
}
function getCurrentMailIds(items = []) {
return new Set(items.map((item) => item.id));
}
function parseMailItemTimestamp() {
return Date.now();
}
function matchesMailFilters(text) {
return /chatgpt|openai|verification/i.test(String(text || ''));
}
function getMailItemText(item) {
return item.text;
}
function extractVerificationCode(text) {
const match = String(text || '').match(/(\\d{6})/);
return match ? match[1] : null;
}
async function sleep() {}
async function sleepRandom() {}
async function returnToInbox() {
return true;
}
async function refreshInbox() {}
async function openMailAndDeleteAfterRead(item) {
readAndDeleteCalls.push(item.id);
return item.text;
}
async function ensureSeenCodesSession() {}
function persistSeenCodes() {}
function log() {}
${bundle}
return {
handlePollEmail,
getReadAndDeleteCalls() {
return readAndDeleteCalls.slice();
},
};
`)();
const result = await api.handlePollEmail(8, {
senderFilters: ['chatgpt'],
subjectFilters: ['verification'],
maxAttempts: 1,
intervalMs: 1,
targetEmail: 'expected@example.com',
mail2925MatchTargetEmail: true,
});
assert.equal(result.code, '445566');
assert.deepEqual(api.getReadAndDeleteCalls(), ['mail-2']);
});
test('handlePollEmail only accepts 2925 mails inside the fixed lookback window', async () => {
const bundle = [
extractFunction('normalizeMinuteTimestamp'),
+9
View File
@@ -26,3 +26,12 @@ test('managed alias utils validate provider email with or without configured bas
assert.equal(api.isManagedAliasEmail('manual@gmail.com', 'gmail', ''), true);
assert.equal(api.isManagedAliasEmail('manual@qq.com', 'gmail', ''), false);
});
test('managed alias utils keep 2925 alias generation behind provide mode only', () => {
assert.equal(api.normalizeMail2925Mode('provide'), 'provide');
assert.equal(api.normalizeMail2925Mode('receive'), 'receive');
assert.equal(api.normalizeMail2925Mode('other'), 'provide');
assert.equal(api.usesManagedAliasGeneration('gmail'), true);
assert.equal(api.usesManagedAliasGeneration('2925', { mail2925Mode: 'provide' }), true);
assert.equal(api.usesManagedAliasGeneration('2925', { mail2925Mode: 'receive' }), false);
});
+109
View File
@@ -0,0 +1,109 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
function extractFunction(name) {
const markers = [`async function ${name}(`, `function ${name}(`];
const start = markers
.map((marker) => source.indexOf(marker))
.find((index) => index >= 0);
if (start < 0) {
throw new Error(`missing function ${name}`);
}
let parenDepth = 0;
let signatureEnded = false;
let braceStart = -1;
for (let i = start; i < source.length; i += 1) {
const ch = source[i];
if (ch === '(') {
parenDepth += 1;
} else if (ch === ')') {
parenDepth -= 1;
if (parenDepth === 0) {
signatureEnded = true;
}
} else if (ch === '{' && signatureEnded) {
braceStart = i;
break;
}
}
if (braceStart < 0) {
throw new Error(`missing body for function ${name}`);
}
let depth = 0;
let end = braceStart;
for (; end < source.length; end += 1) {
const ch = source[end];
if (ch === '{') depth += 1;
if (ch === '}') {
depth -= 1;
if (depth === 0) {
end += 1;
break;
}
}
}
return source.slice(start, end);
}
test('sidepanel html keeps 2925 mode row and standalone pool settings row', () => {
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
assert.match(html, /id="row-mail-2925-mode"/);
assert.match(html, /data-mail2925-mode="provide"/);
assert.match(html, /data-mail2925-mode="receive"/);
assert.match(html, /id="row-mail2925-pool-settings"/);
});
test('sidepanel only treats 2925 as generated alias provider in provide mode', () => {
const bundle = [
extractFunction('isManagedAliasProvider'),
extractFunction('usesGeneratedAliasMailProvider'),
].join('\n');
const api = new Function(`
const GMAIL_PROVIDER = 'gmail';
const MAIL_2925_MODE_PROVIDE = 'provide';
const MAIL_2925_MODE_RECEIVE = 'receive';
const DEFAULT_MAIL_2925_MODE = MAIL_2925_MODE_PROVIDE;
const selectMailProvider = { value: '2925' };
function normalizeMail2925Mode(value = '') {
return String(value || '').trim().toLowerCase() === MAIL_2925_MODE_RECEIVE
? MAIL_2925_MODE_RECEIVE
: DEFAULT_MAIL_2925_MODE;
}
function getSelectedMail2925Mode() {
return MAIL_2925_MODE_PROVIDE;
}
function getManagedAliasUtils() {
return {
usesManagedAliasGeneration(provider, options = {}) {
return String(provider || '').trim().toLowerCase() === 'gmail'
|| (String(provider || '').trim().toLowerCase() === '2925'
&& normalizeMail2925Mode(options.mail2925Mode) === MAIL_2925_MODE_PROVIDE);
},
};
}
${bundle}
return {
isManagedAliasProvider,
usesGeneratedAliasMailProvider,
};
`)();
assert.equal(api.isManagedAliasProvider('2925', 'provide'), true);
assert.equal(api.isManagedAliasProvider('2925', 'receive'), false);
assert.equal(api.usesGeneratedAliasMailProvider('2925', 'provide'), true);
assert.equal(api.usesGeneratedAliasMailProvider('2925', 'receive'), false);
assert.equal(api.usesGeneratedAliasMailProvider('gmail', 'receive'), true);
});
+43
View File
@@ -43,6 +43,49 @@ test('verification flow keeps 2925 polling cadence in the default payload', () =
assert.equal(step8Payload.intervalMs, 15000);
});
test('verification flow only enables 2925 target email matching in receive mode', () => {
const helpers = api.createVerificationFlowHelpers({
addLog: async () => {},
chrome: { tabs: { update: async () => {} } },
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
completeStepFromBackground: async () => {},
confirmCustomVerificationStepBypassRequest: async () => ({ confirmed: true }),
getHotmailVerificationPollConfig: () => ({}),
getHotmailVerificationRequestTimestamp: () => 0,
getState: async () => ({}),
getTabId: async () => 1,
HOTMAIL_PROVIDER: 'hotmail-api',
isStopError: () => false,
LUCKMAIL_PROVIDER: 'luckmail-api',
MAIL_2925_VERIFICATION_INTERVAL_MS: 15000,
MAIL_2925_VERIFICATION_MAX_ATTEMPTS: 15,
pollCloudflareTempEmailVerificationCode: async () => ({}),
pollHotmailVerificationCode: async () => ({}),
pollLuckmailVerificationCode: async () => ({}),
sendToContentScript: async () => ({}),
sendToMailContentScriptResilient: async () => ({}),
setState: async () => {},
setStepStatus: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
VERIFICATION_POLL_MAX_ROUNDS: 5,
});
const providePayload = helpers.getVerificationPollPayload(4, {
email: 'user@example.com',
mailProvider: '2925',
mail2925Mode: 'provide',
});
const receivePayload = helpers.getVerificationPollPayload(4, {
email: 'user@example.com',
mailProvider: '2925',
mail2925Mode: 'receive',
});
assert.equal(providePayload.mail2925MatchTargetEmail, false);
assert.equal(receivePayload.mail2925MatchTargetEmail, true);
});
test('verification flow runs beforeSubmit hook before filling the code', async () => {
const events = [];