工程 · 2026.10.25 · 4 min · 更新於 2026.10.26
muse.ai 寄信審批卡按不到:Chrome 擴充功能改讀 React 資料判斷
商店有人回報寄信的審批卡按不到。我把能觸發的卡全跑一遍,發現同一筆審批會以三種樣子出現,按鈕文字也隨動作和語言變。1.3.0 改成讀 muse 標在頁面上的屬性和 React 裡的審批資料,讀不到才退回認文字。
閱讀設定
商店有人留了一則 4 星評論:寄 email 的審批卡,還是要手動允許。
那是我的 Chrome 擴充功能 Auto Approve for Muse,專門自動按掉 muse.ai 的審批卡。我把 muse 能觸發的審批卡全跑過一遍,結論是認按鈕文字撐不住。同一筆審批會以三種樣子出現、還會原地轉換,按鈕文字也隨動作和語言變。
1.3.0 改成讀 muse 自己標在按鈕上的屬性,和 React 裡的審批資料,讀不到才退回文字判斷。
前一版怎麼找卡、信任清單怎麼設計,寫在自動按掉 muse 的審批卡。
寄信卡為什麼被跳過
Gmail 寄信卡有三個按鈕:「傳送」「一律允許此接收者」「不要傳送」。
原本的邏輯要先認出拒絕鈕和允許鈕。「不要傳送」不符合拒絕鈕的規則,「傳送」也不符合允許鈕的規則,整張卡就被跳過,連「等你決定」都沒列。
連接器卡的按鈕寫的是動作本身,每種動作都不一樣,文字清單永遠補不完。
後來發現 muse 在主要按鈕上標了 data-hatch-approval-primary-action,允許鈕改認這個屬性。拒絕鈕的規則則補上「不要」「不发」和英文的 don't。
同一筆審批,三種樣子
把卡全跑一遍之後,最麻煩的是同一筆審批會出現在三個地方:聊天室裡的單張卡、右側面板的待檢閱清單,和多項合併成一張的批次卡子項。聊天室和面板常常各有一份。
批次卡的子項是 [role=button][aria-expanded],裡面有自己的主要按鈕,標題在 span[title]。
麻煩在它會一直變。muse 處理一項時,會忽略同一張卡的其他點擊,按完再重畫整張卡;剩最後一項時,批次卡還會原地變回單張卡,換成新的節點。
一開始我連按好幾項,只有第一下有效。後來改成一張卡一輪只按一項,按完等 1 秒再看。
聊天室和面板各有一份,批次子項的標題不同。
從隔離環境讀 React 資料
三種樣子要認成同一筆,文字幫不上忙。單張卡標題是「允許 Ren 以 Gmail 傳送此電子郵件」,批次子項寫「在 Gmail Send email」,DOM 裡找不到共通的動作名稱。
只有 React 元件的 props(元件收到的資料)裡,兩邊的 policySubject 都是 connector.gmail.users.messages.send。
問題是擴充功能的 content script 跑在隔離環境:看得到網頁的 DOM,看不到網頁自己的 JS 變數,React 的內部資料也讀不到。
所以我加了一支 bridge.js,在 manifest 設 world: MAIN,讓它跑在 muse 頁面自己的 JS 環境裡。
React 會在每個 DOM 節點上掛一個內部物件(fiber),順著它往上走,就能找到元件的 props。bridge 從每張卡的節點往上找,找到帶審批資料的那層,把審批 ID 和 policySubject 寫成 DOM 屬性,交給 content script 讀:
// bridge.js:從 DOM 節點往上找 props 裡帶 approvalId 的元件
function approvalsOf(el) {
const key = Object.keys(el).find(k => k.startsWith('__reactFiber$'));
for (let f = key && el[key], i = 0; f && i < 12; f = f.return, i++) {
for (const v of Object.values(f.memoizedProps ?? {})) {
if (typeof v?.approvalId === 'string') return [v];
if (Array.isArray(v) && v.length && v.every(a => typeof a?.approvalId === 'string')) return v;
}
}
return [];
}
它也從 react-query 的快取找到審批查詢,把全部待審的 ID 寫到 <html> 上。
有了這些,信任規則改用 policySubject 比對,舊規則遇到同標題的卡會自動補上。重複卡用審批 ID 去重,只按一次。
全部待審扣掉這個分頁看過的,就知道別的聊天室或排程還有卡在等。這時打開右側面板的核可分頁,只按信任清單裡的;裡面全都不信任的話,10 分鐘內不再打開。
bridge 讀不到資料時,一律退回文字判斷。
bridge 只把資料寫成 DOM 屬性,信任判斷都在 content script。
結帳卡永遠不自動按
信任一個聊天室,代表那個聊天室要求什麼都放行。以前最壞是發出一篇收不回來的文,碰到結帳就變成花錢。
1.3.0 照 muse 翻譯檔裡的字樣(結帳、下訂單、購買、结账、下单)認出這類卡,不管信任怎麼設都不自動按,popup 也只給「只允許這一次」。
這張卡我沒真的觸發過。muse 的購買審批要走到付款那一步,得填真實個資送出商家表單,或連好錢包且收件地址在幾個特定國家,我決定不測。
換語言實測與改版風險
這次沒把擴充功能裝進 Chrome 跑,而是用 repo 的程式直接在 muse 頁面上跑一輪偵測、信任判斷和點擊,繁中、英文、簡體介面都切過。
換語言才抓到不少漏網的字樣。例如英文副聊天室實際寫 comes from,原本只認 is from 和 came from。chrome.storage 用的是假的,紀錄的寫入沒有實跑。
另一個坑跟文字無關:「一律允許」收在 Radix 選單裡,.click() 打不開,要送 pointerdown。
代價是這版依賴 muse 的內部結構:React props、react-query 的查詢、頭像屬性和分頁名稱。muse 改版就可能失效,失效時退回文字判斷,批次卡和重複卡會處理得比較差。
學到的是,替別人的網站做自動化,先找網站自己標的屬性和資料,會比認畫面文字穩。DOM 會原地轉換的元件,一輪只做一件事,等它重畫完再看下一步。

工程
發文要等兩分鐘:把 Astro 靜態部落格改成 Workers 即時渲染
部落格原本每次發文都要全站重 build,等 1 到 2 分鐘。改成 Cloudflare Workers 即時渲染加邊緣快取後,發文只清一次快取,約 5 秒上線,CPU 中位數 9ms,還留在免費方案。
留言
載入留言中…