fix: 支持新版 ChatGPT 登录验证码邮件匹配

This commit is contained in:
Ryan Liu
2026-04-24 18:06:13 +08:00
parent 9c1c0bc076
commit 70f3ffb266
13 changed files with 95 additions and 4 deletions
+3
View File
@@ -171,6 +171,9 @@ function extractVerificationCode(text) {
const cnMatch = normalized.match(/(?:验证码|代码)[^0-9]{0,16}(\d{6})/i); const cnMatch = normalized.match(/(?:验证码|代码)[^0-9]{0,16}(\d{6})/i);
if (cnMatch) return cnMatch[1]; if (cnMatch) return cnMatch[1];
const openAiLoginMatch = normalized.match(/(?:chatgpt\s+log-?in\s+code|enter\s+this\s+code)[^0-9]{0,24}(\d{6})/i);
if (openAiLoginMatch) return openAiLoginMatch[1];
const enMatch = normalized.match(/(?:verification\s+code|temporary\s+verification\s+code|your\s+chatgpt\s+code|code(?:\s+is)?)[^0-9]{0,16}(\d{6})/i); const enMatch = normalized.match(/(?:verification\s+code|temporary\s+verification\s+code|your\s+chatgpt\s+code|code(?:\s+is)?)[^0-9]{0,16}(\d{6})/i);
if (enMatch) return enMatch[1]; if (enMatch) return enMatch[1];
+3
View File
@@ -82,6 +82,9 @@ if (isTopFrame) {
const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/); const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/);
if (matchCn) return matchCn[1]; if (matchCn) return matchCn[1];
const matchOpenAiLogin = text.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 matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i); const matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i);
if (matchEn) return matchEn[1] || matchEn[2]; if (matchEn) return matchEn[1] || matchEn[2];
+4 -1
View File
@@ -64,6 +64,9 @@ function extractVerificationCode(text) {
const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/); const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/);
if (matchCn) return matchCn[1]; if (matchCn) return matchCn[1];
const matchOpenAiLogin = text.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 matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i); const matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i);
if (matchEn) return matchEn[1] || matchEn[2]; if (matchEn) return matchEn[1] || matchEn[2];
@@ -85,7 +88,7 @@ function rowMatchesFilters(mail, senderFilters, subjectFilters, targetEmail) {
const mailboxMatch = Boolean(targetLocal) && mailbox.includes(targetLocal); const mailboxMatch = Boolean(targetLocal) && mailbox.includes(targetLocal);
const forwardedDuck = /duckduckgo|forward(?:ed)?\s*by/i.test(mail.combinedText); const forwardedDuck = /duckduckgo|forward(?:ed)?\s*by/i.test(mail.combinedText);
const code = extractVerificationCode(mail.combinedText); const code = extractVerificationCode(mail.combinedText);
const keywordMatch = /openai|chatgpt|verify|verification|confirm|login|验证码|代码/.test(combined); const keywordMatch = /openai|chatgpt|verify|verification|confirm|log-?in|验证码|代码/.test(combined);
if (mailboxMatch) return { matched: true, mailboxMatch, code }; if (mailboxMatch) return { matched: true, mailboxMatch, code };
if (senderMatch || subjectMatch) return { matched: true, mailboxMatch: false, code }; if (senderMatch || subjectMatch) return { matched: true, mailboxMatch: false, code };
+5 -2
View File
@@ -114,7 +114,7 @@ function isLikelyMailItemNode(node) {
return false; return false;
} }
return /发件人|验证码|verification|chatgpt|openai|code/i.test(summaryText); return /发件人|验证码|verification|chatgpt|openai|code|log-?in/i.test(summaryText);
} }
function findMailItems() { function findMailItems() {
@@ -391,7 +391,7 @@ function selectOpenedMailTextCandidate(item, candidates = [], options = {}) {
if (sender && lower.includes(sender)) { if (sender && lower.includes(sender)) {
return true; return true;
} }
return Boolean(extractVerificationCode(candidate) && /chatgpt|openai|verification|验证码|login code/i.test(lower)); return Boolean(extractVerificationCode(candidate) && /chatgpt|openai|verification|验证码|log-?in\s+code/i.test(lower));
}) || source[0] || ''; }) || source[0] || '';
const filteredCandidates = candidates.filter((candidate) => !excludedSet.has(normalizeText(candidate))); const filteredCandidates = candidates.filter((candidate) => !excludedSet.has(normalizeText(candidate)));
@@ -704,6 +704,9 @@ function extractVerificationCode(text) {
const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/); const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/);
if (matchCn) return matchCn[1]; if (matchCn) return matchCn[1];
const matchOpenAiLogin = text.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 matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i); const matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i);
if (matchEn) return matchEn[1] || matchEn[2]; if (matchEn) return matchEn[1] || matchEn[2];
+4 -1
View File
@@ -670,7 +670,7 @@ function matchesMailFilters(text, senderFilters, subjectFilters) {
function extractVerificationCode(text, strictChatGPTCodeOnly = false) { function extractVerificationCode(text, strictChatGPTCodeOnly = false) {
if (strictChatGPTCodeOnly) { if (strictChatGPTCodeOnly) {
const strictMatch = String(text || '').match(/your\s+chatgpt\s+code\s+is\s+(\d{6})/i); const strictMatch = String(text || '').match(/(?:your\s+chatgpt\s+code\s+is|(?:chatgpt\s+log-?in\s+code|suspicious\s+log-?in)[\s\S]{0,120}enter\s+this\s+code)[^0-9]{0,24}(\d{6})/i);
return strictMatch ? strictMatch[1] : null; return strictMatch ? strictMatch[1] : null;
} }
@@ -679,6 +679,9 @@ function extractVerificationCode(text, strictChatGPTCodeOnly = false) {
const matchCn = normalized.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/); const matchCn = normalized.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/);
if (matchCn) return matchCn[1]; if (matchCn) return matchCn[1];
const matchOpenAiLogin = normalized.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 = normalized.match(/your\s+chatgpt\s+code\s+is\s+(\d{6})/i); const matchChatGPT = normalized.match(/your\s+chatgpt\s+code\s+is\s+(\d{6})/i);
if (matchChatGPT) return matchChatGPT[1]; if (matchChatGPT) return matchChatGPT[1];
+3
View File
@@ -174,6 +174,9 @@ function extractVerificationCode(text) {
const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/); const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/);
if (matchCn) return matchCn[1]; if (matchCn) return matchCn[1];
const matchOpenAiLogin = text.match(/(?:chatgpt\s+log-?in\s+code|enter\s+this\s+code)[^0-9]{0,24}(\d{6})/i);
if (matchOpenAiLogin) return matchOpenAiLogin[1];
// Pattern 2: English format "code is 370794" or "code: 370794" // Pattern 2: English format "code is 370794" or "code: 370794"
const matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i); const matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i);
if (matchEn) return matchEn[1] || matchEn[2]; if (matchEn) return matchEn[1] || matchEn[2];
+3
View File
@@ -38,6 +38,9 @@
const matchCn = source.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/i); const matchCn = source.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/i);
if (matchCn) return matchCn[1]; 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 matchEn = source.match(/code(?:\s+is|[\s:])+(\d{6})/i); const matchEn = source.match(/code(?:\s+is|[\s:])+(\d{6})/i);
if (matchEn) return matchEn[1]; if (matchEn) return matchEn[1];
+3
View File
@@ -89,6 +89,9 @@
const matchCn = source.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/i); const matchCn = source.match(/(?:代码为|验证码[^0-9]*?)[\s:]*(\d{6})/i);
if (matchCn) return matchCn[1]; 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); const matchChatGPT = source.match(/your\s+chatgpt\s+code\s+is\s+(\d{6})/i);
if (matchChatGPT) return matchChatGPT[1]; if (matchChatGPT) return matchChatGPT[1];
+10
View File
@@ -181,6 +181,7 @@ test('shouldClearHotmailCurrentSelection returns true only when account becomes
test('extractVerificationCode returns first six-digit code from multilingual mail text', () => { test('extractVerificationCode returns first six-digit code from multilingual mail text', () => {
assert.equal(extractVerificationCode('你的 ChatGPT 验证码为 370794,请勿泄露。'), '370794'); assert.equal(extractVerificationCode('你的 ChatGPT 验证码为 370794,请勿泄露。'), '370794');
assert.equal(extractVerificationCode('Your verification code is 654321.'), '654321'); assert.equal(extractVerificationCode('Your verification code is 654321.'), '654321');
assert.equal(extractVerificationCode('ChatGPT Log-in Code\nIf that was you, enter this code:\n\n982219'), '982219');
assert.equal(extractVerificationCode('No code here'), null); assert.equal(extractVerificationCode('No code here'), null);
}); });
@@ -202,6 +203,15 @@ test('extractVerificationCodeFromMessage reads code from the latest message subj
}), }),
'654321' '654321'
); );
assert.equal(
extractVerificationCodeFromMessage({
subject: 'ChatGPT Log-in Code',
bodyPreview: 'We noticed a suspicious log-in on your account. If that was you, enter this code:\n\n982219',
from: { emailAddress: { address: 'noreply@openai.com' } },
}),
'982219'
);
}); });
test('getHotmailListToggleLabel reflects expanded state and account count', () => { test('getHotmailListToggleLabel reflects expanded state and account count', () => {
+14
View File
@@ -81,6 +81,20 @@ return { readOpenedMailBody, extractVerificationCode };
assert.equal(api.extractVerificationCode(bodyText), '731091'); assert.equal(api.extractVerificationCode(bodyText), '731091');
}); });
test('extractVerificationCode matches the new suspicious log-in mail body', () => {
const bundle = [
extractFunction('extractVerificationCode'),
].join('\n');
const api = new Function(`
${bundle}
return { extractVerificationCode };
`)();
const bodyText = 'ChatGPT Log-in Code\nWe noticed a suspicious log-in on your account. If that was you, enter this code:\n\n982219';
assert.equal(api.extractVerificationCode(bodyText), '982219');
});
test('readOpenedMailBody ignores conversation list rows when no detail pane is open', () => { test('readOpenedMailBody ignores conversation list rows when no detail pane is open', () => {
const bundle = [ const bundle = [
extractFunction('normalizeText'), extractFunction('normalizeText'),
+14
View File
@@ -68,6 +68,20 @@ test('normalizeLuckmailTokenCode and normalizeLuckmailTokenMail extract verifica
assert.equal(normalizedMail.verification_code, '654321'); assert.equal(normalizedMail.verification_code, '654321');
assert.equal(extractLuckmailVerificationCode('你的验证码为 778899'), '778899'); assert.equal(extractLuckmailVerificationCode('你的验证码为 778899'), '778899');
assert.equal(
extractLuckmailVerificationCode('ChatGPT Log-in Code\nIf that was you, enter this code:\n\n982219'),
'982219'
);
const suspiciousLoginMail = normalizeLuckmailTokenMail({
message_id: 'mail-3',
from: 'noreply@openai.com',
subject: 'ChatGPT Log-in Code',
body: 'We noticed a suspicious log-in on your account. If that was you, enter this code:\n\n982219',
received_at: '2026-04-14T10:02:00Z',
});
assert.equal(suspiciousLoginMail.verification_code, '982219');
}); });
test('normalizeLuckmailProjectName and isLuckmailPurchaseForProject match openai case-insensitively', () => { test('normalizeLuckmailProjectName and isLuckmailPurchaseForProject match openai case-insensitively', () => {
+14
View File
@@ -340,6 +340,20 @@ return { openMailAndGetMessageText, mailItem };
assert.doesNotMatch(text, /111111/); assert.doesNotMatch(text, /111111/);
}); });
test('extractVerificationCode matches the new suspicious log-in mail body', () => {
const bundle = [
extractFunction('extractVerificationCode'),
].join('\n');
const api = new Function(`
${bundle}
return { extractVerificationCode };
`)();
const bodyText = 'ChatGPT Log-in Code\nWe noticed a suspicious log-in on your account. If that was you, enter this code:\n\n982219';
assert.equal(api.extractVerificationCode(bodyText), '982219');
});
test('handlePollEmail ignores same-minute old snapshot mail before fallback', async () => { test('handlePollEmail ignores same-minute old snapshot mail before fallback', async () => {
const bundle = [ const bundle = [
extractFunction('normalizeText'), extractFunction('normalizeText'),
+15
View File
@@ -546,6 +546,21 @@ return {
]); ]);
}); });
test('extractVerificationCode strict mode matches the new suspicious log-in mail body', () => {
const bundle = [
extractFunction('extractVerificationCode'),
].join('\n');
const api = new Function(`
${bundle}
return { extractVerificationCode };
`)();
const bodyText = 'ChatGPT Log-in Code\nWe noticed a suspicious log-in on your account. If that was you, enter this code:\n\n982219';
assert.equal(api.extractVerificationCode(bodyText, true), '982219');
assert.equal(api.extractVerificationCode(bodyText, false), '982219');
});
test('openMailAndGetMessageText always returns to inbox after opening a 2925 message', async () => { test('openMailAndGetMessageText always returns to inbox after opening a 2925 message', async () => {
const bundle = [ const bundle = [
extractFunction('returnToInbox'), extractFunction('returnToInbox'),