const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const source = fs.readFileSync('content/phplife-mail.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; let inString = null; let escaped = false; for (; end < source.length; end += 1) { const ch = source[end]; if (escaped) { escaped = false; continue; } if (ch === '\\') { escaped = true; continue; } if (inString) { if (ch === inString) { inString = null; } continue; } if (ch === '"' || ch === '\'' || ch === '`') { inString = ch; continue; } if (ch === '{') depth += 1; if (ch === '}') { depth -= 1; if (depth === 0) { end += 1; break; } } } return source.slice(start, end); } test('phplife mail parser extracts code and recipient from roundcube message view', () => { const bundle = [ extractFunction('normalizeText'), extractFunction('parseRoundcubeTimestamp'), extractFunction('extractVerificationCodes'), extractFunction('getMessageDetailsFromDocument'), ].join('\n'); const api = new Function(`${bundle} return { getMessageDetailsFromDocument, parseRoundcubeTimestamp, extractVerificationCodes }; `)(); const selectors = { 'h2.subject': { textContent: 'Your ChatGPT code is 866785' }, '.headers-table .header.from': { textContent: 'noreply@tm.openai.com' }, '.headers-table .header.to': { textContent: 'n2026041807@a4sky.com' }, '.headers-table .header.date': { textContent: '今天 12:30' }, '#messagebody': { innerText: 'Enter this temporary verification code to continue: 866785. ChatGPT Log-in Code 866785', textContent: 'Enter this temporary verification code to continue: 866785. ChatGPT Log-in Code 866785', }, }; const fakeDocument = { querySelector(selector) { return selectors[selector] || null; }, body: { innerText: '', textContent: '', }, }; const details = api.getMessageDetailsFromDocument(fakeDocument); assert.equal(details.subject, 'Your ChatGPT code is 866785'); assert.equal(details.from, 'noreply@tm.openai.com'); assert.equal(details.to, 'n2026041807@a4sky.com'); assert.equal(details.codes[0], '866785'); assert.equal(Number.isFinite(details.emailTimestamp), true); }); test('phplife mail parser can read verification code directly from list row title', () => { const bundle = [ extractFunction('normalizeText'), extractFunction('parseRoundcubeTimestamp'), extractFunction('extractVerificationCodes'), extractFunction('getRowText'), extractFunction('getRowDetails'), ].join('\n'); const api = new Function(`${bundle} return { getRowDetails }; `)(); const subjectNode = { getAttribute(name) { return name === 'title' ? 'Your ChatGPT code is 866785' : ''; }, textContent: 'fallback subject', }; const fromNode = { getAttribute() { return ''; }, textContent: 'noreply@tm.openai.com' }; const toNode = { getAttribute() { return ''; }, textContent: 'n2026041807@a4sky.com' }; const dateNode = { getAttribute() { return ''; }, textContent: '今天 12:30' }; const fakeRow = { querySelector(selector) { if (selector === 'td.subject') return subjectNode; if (selector === 'td.fromto') return fromNode; if (selector === 'td.to') return toNode; if (selector === 'td.date') return dateNode; return null; }, textContent: 'Your ChatGPT code is 866785 noreply@tm.openai.com n2026041807@a4sky.com 今天 12:30', }; const details = api.getRowDetails(fakeRow); assert.equal(details.subject, 'Your ChatGPT code is 866785'); assert.equal(details.codes[0], '866785'); }); test('phplife refreshMessageList clicks inbox before refresh button', async () => { const bundle = [ extractFunction('ensureInboxActive'), extractFunction('refreshMessageList'), ].join('\n'); const api = new Function(`${bundle} const clickOrder = []; const inboxLink = { closest() { return { classList: { contains() { return true; } } }; }, click() { clickOrder.push('inbox'); }, }; const refreshButton = { click() { clickOrder.push('refresh'); }, }; function findInboxLink() { return inboxLink; } function findRefreshButton() { return refreshButton; } async function sleep() {} return { refreshMessageList, getClickOrder() { return clickOrder.slice(); }, }; `)(); await api.refreshMessageList(); assert.deepEqual(api.getClickOrder(), ['inbox', 'refresh']); }); test('phplife temporary login title opens detail to extract verification code', async () => { const bundle = [ extractFunction('normalizeText'), extractFunction('normalizeMinuteTimestamp'), extractFunction('shouldOpenRowForCodeDetection'), extractFunction('selectCandidateCode'), extractFunction('matchesCurrentMessage'), extractFunction('scoreRowCandidate'), extractFunction('tryOpenRowsAndRead'), ].join('\n'); const api = new Function(`${bundle} let opened = 0; const seenCodes = new Set(); function throwIfStopped() {} function getMessageListRows() { return [{ id: 'row-1' }]; } function getRowDetails() { return { row: { id: 'row-1' }, subject: 'Your temporary ChatGPT login code', from: 'noreply@tm.openai.com', to: 'n2026041807@a4sky.com', dateText: '今天 12:30', emailTimestamp: Date.now(), codes: [], combinedText: 'Your temporary ChatGPT login code noreply@tm.openai.com n2026041807@a4sky.com', }; } function matchesMailFilters() { return true; } function getTargetEmailMatchState() { return { matches: true, hasExplicitEmail: true }; } function log() {} function getCurrentMessageUid() { return ''; } function openMessageRow() { opened += 1; } async function sleep() {} async function waitForPreviewLoaded() { return { subject: 'Your temporary ChatGPT login code', combinedText: 'Your temporary ChatGPT login code 998877 noreply@tm.openai.com n2026041807@a4sky.com', emailTimestamp: Date.now(), codes: ['998877'], }; } return { tryOpenRowsAndRead, getOpenedCount() { return opened; }, }; `)(); const result = await api.tryOpenRowsAndRead(4, { senderFilters: ['openai'], subjectFilters: ['login', 'code'], targetEmail: 'n2026041807@a4sky.com', }, new Set(), 0); assert.equal(result.code, '998877'); assert.equal(api.getOpenedCount(), 1); }); test('phplife Chinese temporary login title also opens detail to extract verification code', async () => { const bundle = [ extractFunction('normalizeText'), extractFunction('normalizeMinuteTimestamp'), extractFunction('shouldOpenRowForCodeDetection'), extractFunction('selectCandidateCode'), extractFunction('matchesCurrentMessage'), extractFunction('scoreRowCandidate'), extractFunction('tryOpenRowsAndRead'), ].join('\n'); const api = new Function(`${bundle} let opened = 0; const seenCodes = new Set(); function throwIfStopped() {} function getMessageListRows() { return [{ id: 'row-1' }]; } function getRowDetails() { return { row: { id: 'row-1' }, subject: '你的临时 ChatGPT 登录代码', from: 'noreply@tm.openai.com', to: 'n2026041807@a4sky.com', dateText: '今天 12:30', emailTimestamp: Date.now(), codes: [], combinedText: '你的临时 ChatGPT 登录代码 noreply@tm.openai.com n2026041807@a4sky.com', }; } function matchesMailFilters() { return true; } function getTargetEmailMatchState() { return { matches: true, hasExplicitEmail: true }; } function log() {} function getCurrentMessageUid() { return ''; } function openMessageRow() { opened += 1; } async function sleep() {} async function waitForPreviewLoaded() { return { subject: '你的临时 ChatGPT 登录代码', combinedText: '你的临时 ChatGPT 登录代码 112233 noreply@tm.openai.com n2026041807@a4sky.com', emailTimestamp: Date.now(), codes: ['112233'], }; } return { tryOpenRowsAndRead, getOpenedCount() { return opened; }, }; `)(); const result = await api.tryOpenRowsAndRead(4, { senderFilters: ['openai'], subjectFilters: ['login', 'code', '登录', '代码'], targetEmail: 'n2026041807@a4sky.com', }, new Set(), 0); assert.equal(result.code, '112233'); assert.equal(api.getOpenedCount(), 1); }); test('phplife remote payload parser extracts target mailbox verification code directly from refresh API payload', () => { const bundle = [ extractFunction('normalizeText'), extractFunction('parseRoundcubeTimestamp'), extractFunction('extractVerificationCodes'), extractFunction('extractEmails'), extractFunction('collectPayloadStringFragments'), extractFunction('extractRemoteExecText'), extractFunction('readBalancedJsonObject'), extractFunction('parseRemoteMessageRowEntries'), extractFunction('extractRemotePayloadFragments'), extractFunction('getTargetEmailMatchState'), extractFunction('matchesMailFilters'), extractFunction('normalizeFragmentText'), extractFunction('buildRemoteRowDetails'), extractFunction('findExactTargetRemoteRows'), extractFunction('scoreRowCandidate'), extractFunction('normalizeMinuteTimestamp'), extractFunction('matchesCurrentMessage'), extractFunction('shouldOpenRowForCodeDetection'), extractFunction('scoreRemotePayloadCandidate'), extractFunction('selectCandidateCode'), extractFunction('findVerificationCodeFromRemotePayload'), ].join('\n'); const api = new Function(`${bundle} const seenCodes = new Set(); class DOMParser { parseFromString(text) { return { body: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, documentElement: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, }; } } return { findVerificationCodeFromRemotePayload }; `)(); const payload = JSON.stringify({ exec: [ "Your ChatGPT code is 445566n20260419170738@a4sky.com", ], }); const result = api.findVerificationCodeFromRemotePayload(payload, { senderFilters: ['openai'], subjectFilters: ['code', 'verification', '验证码'], targetEmail: 'n20260419170738@a4sky.com', }, new Set(), 0); assert.equal(result.code, '445566'); }); test('phplife remote payload parser ignores codes for other mailbox addresses', () => { const bundle = [ extractFunction('normalizeText'), extractFunction('parseRoundcubeTimestamp'), extractFunction('extractVerificationCodes'), extractFunction('extractEmails'), extractFunction('collectPayloadStringFragments'), extractFunction('extractRemoteExecText'), extractFunction('readBalancedJsonObject'), extractFunction('parseRemoteMessageRowEntries'), extractFunction('extractRemotePayloadFragments'), extractFunction('getTargetEmailMatchState'), extractFunction('matchesMailFilters'), extractFunction('normalizeFragmentText'), extractFunction('buildRemoteRowDetails'), extractFunction('findExactTargetRemoteRows'), extractFunction('scoreRowCandidate'), extractFunction('normalizeMinuteTimestamp'), extractFunction('matchesCurrentMessage'), extractFunction('shouldOpenRowForCodeDetection'), extractFunction('scoreRemotePayloadCandidate'), extractFunction('selectCandidateCode'), extractFunction('findVerificationCodeFromRemotePayload'), ].join('\n'); const api = new Function(`${bundle} const seenCodes = new Set(); class DOMParser { parseFromString(text) { return { body: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, documentElement: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, }; } } return { findVerificationCodeFromRemotePayload }; `)(); const payload = JSON.stringify({ exec: [ "Your ChatGPT code is 111111other@a4sky.com", "Your ChatGPT code is 222222target@a4sky.com", ], }); const result = api.findVerificationCodeFromRemotePayload(payload, { senderFilters: ['openai'], subjectFilters: ['code', 'verification', '验证码'], targetEmail: 'target@a4sky.com', }, new Set(), 0); assert.equal(result.code, '222222'); }); test('phplife remote payload parser does not fall back to other mailbox rows when target mailbox rows exist without direct code', () => { const bundle = [ extractFunction('normalizeText'), extractFunction('parseRoundcubeTimestamp'), extractFunction('extractVerificationCodes'), extractFunction('extractEmails'), extractFunction('collectPayloadStringFragments'), extractFunction('extractRemoteExecText'), extractFunction('readBalancedJsonObject'), extractFunction('parseRemoteMessageRowEntries'), extractFunction('extractRemotePayloadFragments'), extractFunction('getTargetEmailMatchState'), extractFunction('matchesMailFilters'), extractFunction('normalizeFragmentText'), extractFunction('buildRemoteRowDetails'), extractFunction('findExactTargetRemoteRows'), extractFunction('scoreRowCandidate'), extractFunction('normalizeMinuteTimestamp'), extractFunction('matchesCurrentMessage'), extractFunction('shouldOpenRowForCodeDetection'), extractFunction('scoreRemotePayloadCandidate'), extractFunction('selectCandidateCode'), extractFunction('findVerificationCodeFromRemotePayload'), ].join('\n'); const api = new Function(`${bundle} const seenCodes = new Set(); class DOMParser { parseFromString(text) { return { body: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, documentElement: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, }; } } return { findVerificationCodeFromRemotePayload }; `)(); const payload = JSON.stringify({ exec: [ "this.add_message_row(900,{\"subject\":\"Your temporary ChatGPT login code\",\"fromto\":\"OpenAI<\\/span>\",\"date\":\"今天 18:24\",\"to\":\"target@a4sky.com<\\/span>\"},{\"ctype\":\"text/html\",\"mbox\":\"INBOX\"},false);", "this.add_message_row(899,{\"subject\":\"Your ChatGPT code is 999999\",\"fromto\":\"OpenAI<\\/span>\",\"date\":\"今天 18:24\",\"to\":\"other@a4sky.com<\\/span>\"},{\"ctype\":\"text/html\",\"mbox\":\"INBOX\"},false);", ], }); const result = api.findVerificationCodeFromRemotePayload(payload, { senderFilters: ['openai'], subjectFilters: ['code', 'verification', '验证码'], targetEmail: 'target@a4sky.com', }, new Set(), 0); assert.equal(result, null); }); test('phplife tryReadRemoteMessageList prefers search payload scoped to target mailbox', async () => { const bundle = [ extractFunction('normalizeText'), extractFunction('parseRoundcubeTimestamp'), extractFunction('extractVerificationCodes'), extractFunction('extractEmails'), extractFunction('collectPayloadStringFragments'), extractFunction('extractRemoteExecText'), extractFunction('readBalancedJsonObject'), extractFunction('parseRemoteMessageRowEntries'), extractFunction('extractRemotePayloadFragments'), extractFunction('getTargetEmailMatchState'), extractFunction('matchesMailFilters'), extractFunction('normalizeFragmentText'), extractFunction('buildRemoteRowDetails'), extractFunction('findExactTargetRemoteRows'), extractFunction('scoreRowCandidate'), extractFunction('normalizeMinuteTimestamp'), extractFunction('matchesCurrentMessage'), extractFunction('shouldOpenRowForCodeDetection'), extractFunction('scoreRemotePayloadCandidate'), extractFunction('selectCandidateCode'), extractFunction('findVerificationCodeFromRemotePayload'), extractFunction('tryReadRemoteMessageList'), ].join('\n'); const api = new Function(`${bundle} const PHPLIFE_MAIL_PREFIX = '[MultiPage:mail-phplife]'; function log() {} const seenCodes = new Set(); class DOMParser { parseFromString(text) { return { body: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, documentElement: { textContent: String(text || '').replace(/<[^>]+>/g, ' ') }, }; } } async function requestRemoteMessageSearch() { const row = { subject: '你的 ChatGPT 代码为 991895', fromto: 'OpenAI', date: '今天 18:56', to: 'n20260419185525@a4sky.com', }; const meta = { ctype: 'text/html', mbox: 'INBOX', }; return JSON.stringify({ exec: [ 'this.add_message_row(524,' + JSON.stringify(row) + ',' + JSON.stringify(meta) + ',false);' ], }); } async function requestRemoteMessageList() { throw new Error('list api should not be needed when search already matched'); } return { tryReadRemoteMessageList }; `)(); const result = await api.tryReadRemoteMessageList(4, { senderFilters: ['openai'], subjectFilters: ['code', 'verification', '验证码'], targetEmail: 'n20260419185525@a4sky.com', filterAfterTimestamp: 0, }, new Set()); assert.equal(result.code, '991895'); });