From e5ae540a3c21112bb57080c61f45955201525843 Mon Sep 17 00:00:00 2001 From: dada2321 Date: Sun, 19 Apr 2026 04:57:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=B7=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Process.md | 20 +++++++++++ content/mail-163.js | 15 +++++++-- tests/mail-163-content.test.js | 61 ++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 Process.md diff --git a/Process.md b/Process.md new file mode 100644 index 0000000..51e0468 --- /dev/null +++ b/Process.md @@ -0,0 +1,20 @@ +# Process + +## 2026-04-19 + +### 今日战利品 +- 修复了 163 邮箱顶部“刷新”按钮的识别问题。 +- 根因不是插件不知道顶部按钮在哪里,而是它用 DOM 文本做识别时,代码只认“刷新”,但 163 实际渲染出来可能是“刷 新”。 +- 给 `163` 邮箱补上了回归测试,确保以后再次遇到这种带空格的按钮文案时,脚本仍然会优先点击顶部工具栏刷新,而不是误退回到“收件箱”。 +- 额外补了更直白的刷新分支日志,后续排查时可以直接看出本次命中的是“顶部刷新”“左侧收信”还是“收件箱兜底”。 + +### 知识地图 +- 这一块属于“自动化脚本稳定性”里的 DOM 识别层。 +- 老板可以把它理解成:脚本不是靠“看见页面上方那个位置”去点按钮,而是靠“读页面结构里的标签文字和属性”去认目标。 +- 所以这里学到的核心不是某一个按钮怎么点,而是“页面自动化里,视觉上像同一个按钮,DOM 文本却可能有空格、换行、图标包裹等变体”,识别逻辑必须做抗噪处理。 +- 这部分在大神知识体系里,位置属于:前端页面结构理解 -> 浏览器自动化 -> 选择器与文本匹配鲁棒性。 + +### 下一步导航 +- 可以继续把 163 的删除兜底逻辑也做一次同类检查,因为顶部“删 除”从 DOM 结构看也可能存在同样的空格拆字风险。 +- 如果老板接下来还想继续提稳收信成功率,可以再查一层:163 顶部“刷新”点击之后,页面到底有没有真正触发新的列表请求,以及请求完成信号该监听什么。 +- 如果要把这一块做成长期稳定方案,下一步适合补“刷新动作观测点”日志,比如记录刷新前后列表首封邮件 ID、请求时间、是否出现 loading。 diff --git a/content/mail-163.js b/content/mail-163.js index 1ab01e3..424fdb0 100644 --- a/content/mail-163.js +++ b/content/mail-163.js @@ -461,9 +461,18 @@ if (!isTopFrame) { async function refreshInbox() { const toolbarBtns = document.querySelectorAll('.nui-btn .nui-btn-text'); for (const btn of toolbarBtns) { - if (normalizeText(btn.textContent || '') === '刷新') { + // 163 会把“刷 新”拆成带空格的文字,所以这里要先压平空白, + // 不然脚本明明看到了顶部刷新按钮,也会误以为没找到。 + const compactLabel = String([ + btn.textContent || '', + btn.getAttribute?.('aria-label') || '', + btn.getAttribute?.('title') || '', + ].join('')).replace(/\s+/g, ''); + + if (compactLabel === '刷新') { simulateClick(btn.closest('.nui-btn') || btn); - console.log(MAIL163_PREFIX, 'Clicked refresh button'); + console.log(MAIL163_PREFIX, 'Clicked top toolbar refresh button'); + log('[163 邮箱] 已命中顶部工具栏“刷新”按钮'); await sleep(800); return; } @@ -474,6 +483,7 @@ if (!isTopFrame) { if (normalizeText(btn.textContent || '').includes('收信')) { simulateClick(btn); console.log(MAIL163_PREFIX, 'Clicked receive button'); + log('[163 邮箱] 未找到顶部刷新,改用左侧“收信”按钮'); await sleep(800); return; } @@ -482,6 +492,7 @@ if (!isTopFrame) { const inboxLink = findInboxLink(); if (inboxLink) { simulateClick(inboxLink); + log('[163 邮箱] 未找到顶部刷新或左侧“收信”,回退到点击“收件箱”'); await sleep(800); return; } diff --git a/tests/mail-163-content.test.js b/tests/mail-163-content.test.js index 9f70a04..0f901a6 100644 --- a/tests/mail-163-content.test.js +++ b/tests/mail-163-content.test.js @@ -146,3 +146,64 @@ return { assert.equal(result.code, '480382'); assert.deepEqual(api.getOpenedMailIds(), ['mail-1']); }); + +test('refreshInbox prefers the top toolbar refresh button even when 163 renders the label as 刷 新', async () => { + const bundle = [ + extractFunction('normalizeText'), + extractFunction('refreshInbox'), + ].join('\n'); + + const api = new Function(` +const MAIL163_PREFIX = '[MultiPage:mail-163]'; +const clickOrder = []; + +const refreshButton = { + tagName: 'DIV', + textContent: '刷 新', +}; + +const refreshLabel = { + textContent: '刷 新', + closest(selector) { + return selector === '.nui-btn' ? refreshButton : null; + }, +}; + +const inboxLink = { + tagName: 'SPAN', + textContent: '收件箱', +}; + +const document = { + querySelectorAll(selector) { + if (selector === '.nui-btn .nui-btn-text') return [refreshLabel]; + if (selector === '.ra0') return []; + return []; + }, +}; + +function simulateClick(node) { + clickOrder.push(node.textContent); +} + +function findInboxLink() { + return inboxLink; +} + +async function sleep() {} +function log() {} + +${bundle} + +return { + refreshInbox, + getClickOrder() { + return clickOrder.slice(); + }, +}; +`)(); + + await api.refreshInbox(); + + assert.deepEqual(api.getClickOrder(), ['刷 新']); +});