diff --git a/content/mail-163.js b/content/mail-163.js index 547ea73..edab71b 100644 --- a/content/mail-163.js +++ b/content/mail-163.js @@ -377,12 +377,13 @@ function collectOpenedMailTextCandidates() { return texts.sort((a, b) => b.length - a.length); } -function readOpenedMailText(item) { +function selectOpenedMailTextCandidate(item, candidates = [], options = {}) { const subject = normalizeText(getMailSubjectText(item)).toLowerCase(); const sender = normalizeText(getMailSenderText(item)).toLowerCase(); - const candidates = collectOpenedMailTextCandidates(); + const excludedSet = new Set((options.excludedTexts || []).map((value) => normalizeText(value))); + const allowExcludedFallback = options.allowExcludedFallback !== false; - const preferred = candidates.find((candidate) => { + const pickCandidate = (source) => source.find((candidate) => { const lower = candidate.toLowerCase(); if (subject && lower.includes(subject)) { return true; @@ -391,9 +392,20 @@ function readOpenedMailText(item) { return true; } return Boolean(extractVerificationCode(candidate) && /chatgpt|openai|verification|验证码|login code/i.test(lower)); - }); + }) || source[0] || ''; - return preferred || candidates[0] || ''; + const filteredCandidates = candidates.filter((candidate) => !excludedSet.has(normalizeText(candidate))); + const preferred = pickCandidate(filteredCandidates); + if (preferred || !allowExcludedFallback) { + return preferred; + } + + return pickCandidate(candidates); +} + +function readOpenedMailText(item, options = {}) { + const candidates = collectOpenedMailTextCandidates(); + return selectOpenedMailTextCandidate(item, candidates, options); } async function returnToInbox() { @@ -417,7 +429,8 @@ async function returnToInbox() { } async function openMailAndGetMessageText(item) { - const beforeText = readOpenedMailText(item); + const beforeCandidates = collectOpenedMailTextCandidates(); + const beforeText = selectOpenedMailTextCandidate(item, beforeCandidates); if (typeof simulateClick === 'function') { simulateClick(item); } else { @@ -427,7 +440,10 @@ async function openMailAndGetMessageText(item) { let openedText = ''; for (let i = 0; i < 24; i += 1) { await sleep(250); - const candidate = readOpenedMailText(item); + const candidate = readOpenedMailText(item, { + excludedTexts: beforeCandidates, + allowExcludedFallback: false, + }); if (!candidate) { continue; } diff --git a/tests/mail-163-content.test.js b/tests/mail-163-content.test.js index 34f3ec3..72f7bd6 100644 --- a/tests/mail-163-content.test.js +++ b/tests/mail-163-content.test.js @@ -147,6 +147,7 @@ test('readOpenedMailText prefers opened body content that contains the verificat const bundle = [ extractFunction('normalizeText'), extractFunction('collectOpenedMailTextCandidates'), + extractFunction('selectOpenedMailTextCandidate'), extractFunction('readOpenedMailText'), extractFunction('extractVerificationCode'), ].join('\n'); @@ -194,6 +195,7 @@ test('openMailAndGetMessageText reads opened body text and returns to inbox', as const bundle = [ extractFunction('normalizeText'), extractFunction('collectOpenedMailTextCandidates'), + extractFunction('selectOpenedMailTextCandidate'), extractFunction('readOpenedMailText'), extractFunction('returnToInbox'), extractFunction('openMailAndGetMessageText'), @@ -262,6 +264,82 @@ return { assert.equal(api.isInInbox(), true); }); +test('openMailAndGetMessageText ignores stale pre-open text that contains an old code', async () => { + const bundle = [ + extractFunction('normalizeText'), + extractFunction('collectOpenedMailTextCandidates'), + extractFunction('selectOpenedMailTextCandidate'), + extractFunction('readOpenedMailText'), + extractFunction('returnToInbox'), + extractFunction('openMailAndGetMessageText'), + extractFunction('extractVerificationCode'), + ].join('\n'); + + const api = new Function(` +let stage = 'before'; +const oldText = 'OpenAI Your temporary ChatGPT login code. Ignore this old code 111111. '.repeat(10); +const newText = 'OpenAI Your temporary ChatGPT login code. Your new code is 222222.'; +const mailItem = { + click() { + stage = 'after'; + }, +}; +const inboxLink = { + click() { + stage = 'done'; + }, +}; + +function getMailSubjectText() { + return 'Your temporary ChatGPT login code'; +} + +function getMailSenderText() { + return 'OpenAI'; +} + +const document = { + querySelector(selector) { + if (selector === '.nui-tree-item-text[title="收件箱"], [title="收件箱"]') return inboxLink; + return null; + }, + querySelectorAll(selector) { + if (selector === 'iframe') { + return []; + } + if (stage === 'before') { + return [{ innerText: oldText, textContent: oldText }]; + } + if (stage === 'after') { + return [ + { innerText: oldText, textContent: oldText }, + { innerText: newText, textContent: newText }, + ]; + } + return []; + }, + body: { + innerText: '', + textContent: '', + }, +}; + +function findMailItems() { + return stage === 'done' ? [mailItem] : []; +} + +async function sleep() {} + +${bundle} + +return { openMailAndGetMessageText, mailItem }; +`)(); + + const text = await api.openMailAndGetMessageText(api.mailItem); + assert.match(text, /222222/); + assert.doesNotMatch(text, /111111/); +}); + test('handlePollEmail ignores same-minute old snapshot mail before fallback', async () => { const bundle = [ extractFunction('normalizeText'),