fix: harden 2925 mailbox reuse and forwarded mail parsing
- 合并 PR #169 的核心改动:在复用 2925 邮箱页前恢复 content script ready,并支持 OpenAI bounce 转发地址反解与中文日期解析。 - 本地补充修复:无。 - 影响范围:2925 邮箱会话确认、2925 邮件轮询、相关回归测试。
This commit is contained in:
@@ -537,6 +537,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof ensureContentScriptReadyOnTab === 'function') {
|
||||
await ensureContentScriptReadyOnTab(MAIL2925_SOURCE, tabId, {
|
||||
inject: MAIL2925_INJECT,
|
||||
injectSource: MAIL2925_INJECT_SOURCE,
|
||||
timeoutMs: 20000,
|
||||
retryDelayMs: 800,
|
||||
logMessage: '步骤 0:2925 登录页内容脚本未就绪,正在等待页面稳定后继续登录...',
|
||||
});
|
||||
}
|
||||
|
||||
if (!forceRelogin && !isMail2925LoginUrl(openedUrl) && !normalizedExpectedMailboxEmail) {
|
||||
await addLog('2925:当前邮箱页未跳转到登录页,将直接复用已登录会话。', 'info');
|
||||
return buildSuccessPayload();
|
||||
@@ -553,16 +563,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof ensureContentScriptReadyOnTab === 'function') {
|
||||
await ensureContentScriptReadyOnTab(MAIL2925_SOURCE, tabId, {
|
||||
inject: MAIL2925_INJECT,
|
||||
injectSource: MAIL2925_INJECT_SOURCE,
|
||||
timeoutMs: 20000,
|
||||
retryDelayMs: 800,
|
||||
logMessage: '步骤 0:2925 登录页内容脚本未就绪,正在等待页面稳定后继续登录...',
|
||||
});
|
||||
}
|
||||
|
||||
if (forceRelogin && typeof sleepWithStop === 'function') {
|
||||
await addLog('2925:登录页已打开,等待 3 秒后开始检查输入框并执行登录...', 'info');
|
||||
await sleepWithStop(3000);
|
||||
|
||||
+50
-2
@@ -699,10 +699,29 @@ function extractEmails(text = '') {
|
||||
return [...new Set(matches.map((item) => item.toLowerCase()))];
|
||||
}
|
||||
|
||||
function extractForwardedTargetEmails(text = '') {
|
||||
const normalizedText = String(text || '').toLowerCase();
|
||||
const matches = normalizedText.match(/bounce\+[a-z0-9._%+-]*-([a-z0-9._%+-]+)=([a-z0-9.-]+\.[a-z]{2,})@(?:tm\d*\.openai\.com|em\d+\.tm\.openai\.com)/gi) || [];
|
||||
const decoded = matches
|
||||
.map((candidate) => {
|
||||
const match = String(candidate || '').match(/bounce\+[a-z0-9._%+-]*-([a-z0-9._%+-]+)=([a-z0-9.-]+\.[a-z]{2,})@/i);
|
||||
if (!match) {
|
||||
return '';
|
||||
}
|
||||
return `${match[1].toLowerCase()}@${match[2].toLowerCase()}`;
|
||||
})
|
||||
.filter(Boolean);
|
||||
return [...new Set(decoded)];
|
||||
}
|
||||
|
||||
function emailMatchesTarget(candidate, targetEmail) {
|
||||
const normalizedCandidate = String(candidate || '').trim().toLowerCase();
|
||||
const normalizedTarget = String(targetEmail || '').trim().toLowerCase();
|
||||
return Boolean(normalizedCandidate && normalizedTarget && normalizedCandidate === normalizedTarget);
|
||||
if (!normalizedCandidate || !normalizedTarget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return normalizedCandidate === normalizedTarget;
|
||||
}
|
||||
|
||||
function getTargetEmailMatchState(text, targetEmail) {
|
||||
@@ -717,12 +736,30 @@ function getTargetEmailMatchState(text, targetEmail) {
|
||||
}
|
||||
|
||||
const extractedEmails = extractEmails(normalizedText);
|
||||
const forwardedTargetEmails = extractForwardedTargetEmails(normalizedText);
|
||||
if (!extractedEmails.length) {
|
||||
return forwardedTargetEmails.length
|
||||
? {
|
||||
matches: forwardedTargetEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)),
|
||||
hasExplicitEmail: true,
|
||||
}
|
||||
: { matches: true, hasExplicitEmail: false };
|
||||
}
|
||||
|
||||
const targetDomain = normalizedTarget.includes('@')
|
||||
? normalizedTarget.split('@').pop()
|
||||
: '';
|
||||
const comparableEmails = [...new Set(
|
||||
(targetDomain
|
||||
? [...extractedEmails, ...forwardedTargetEmails].filter((candidate) => String(candidate || '').trim().toLowerCase().endsWith(`@${targetDomain}`))
|
||||
: [...extractedEmails, ...forwardedTargetEmails])
|
||||
)];
|
||||
if (!comparableEmails.length) {
|
||||
return { matches: true, hasExplicitEmail: false };
|
||||
}
|
||||
|
||||
return {
|
||||
matches: extractedEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)),
|
||||
matches: comparableEmails.some((candidate) => emailMatchesTarget(candidate, normalizedTarget)),
|
||||
hasExplicitEmail: true,
|
||||
};
|
||||
}
|
||||
@@ -782,6 +819,17 @@ function parseMailItemTimestamp(item) {
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
match = timeText.match(/(\d{1,2})月(\d{1,2})日(?:\s*(\d{1,2}):(\d{2}))?/);
|
||||
if (match) {
|
||||
date.setMonth(Number(match[1]) - 1, Number(match[2]));
|
||||
if (match[3] && match[4]) {
|
||||
date.setHours(Number(match[3]), Number(match[4]), 0, 0);
|
||||
} else {
|
||||
date.setHours(0, 0, 0, 0);
|
||||
}
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
match = timeText.match(/(\d{4})-(\d{1,2})-(\d{1,2})\s*(\d{1,2}):(\d{2})/);
|
||||
if (match) {
|
||||
return new Date(
|
||||
|
||||
@@ -68,7 +68,7 @@ test('ensureMail2925MailboxSession reuses current mailbox page without sending l
|
||||
});
|
||||
|
||||
assert.equal(sendCalls, 0);
|
||||
assert.equal(readyCalls, 0);
|
||||
assert.equal(readyCalls, 1);
|
||||
assert.equal(result.result.usedExistingSession, true);
|
||||
});
|
||||
|
||||
|
||||
@@ -319,6 +319,7 @@ return {
|
||||
test('handlePollEmail skips explicit mismatched target emails when receive-mode matching is enabled', async () => {
|
||||
const bundle = [
|
||||
extractFunction('extractEmails'),
|
||||
extractFunction('extractForwardedTargetEmails'),
|
||||
extractFunction('emailMatchesTarget'),
|
||||
extractFunction('getTargetEmailMatchState'),
|
||||
extractFunction('normalizeMinuteTimestamp'),
|
||||
|
||||
Reference in New Issue
Block a user