很多在大型企業上班的人,一定都遇過這個痛點: 公司的 Outlook 日曆鎖得死死的,不對外開放 Microsoft Graph API,也封鎖了 Google Calendar 等一切外部同步通道。每次想在手機上確認工作行程,都得手動手動再手動,極度沒有效率。
身為一個「把寫程式當作效率工具」的非典型開發者,我實在無法忍受這種重複勞動。既然正規的「Server-to-Server」路徑被封死,那我決定走一條最迂迴、也最有趣的「瀏覽器端直連」路徑:
「只要我的瀏覽器能看得到日曆,我就有辦法把它送出去。」
原本以為只是簡單重寫 window.fetch 攔截 API 就搞定,動手後才發現微軟的防禦力驚人,這中間我踩過了三個超級大坑,最後用了一個非常 Geek 的架構成功打通。
🛠️ 終極同步架構拆解
1. 突破 Web Worker 的網路隔離(最大難關 💀)
現在新版的 Outlook 網頁版(OWA)為了效能,將日曆資料的請求與解析全部移到了背景的 Web Worker (DataWorker) 中執行。因為 Worker 運作在獨立的線程(Thread),我們在網頁主線程重寫 window.fetch 根本是瞎子摸象,完全攔截不到封包!
解法:我直接劫持了主線程的
MessagePort.prototype.addEventListener。當背景 Worker 處理完行程、準備透過postMessage將 JavaScript 物件傳回主畫面渲染的瞬間,直接在通訊通道(Message Channel)上將資料攔截下來!
2. 深度遞迴掃描與雜訊過濾
攔截到 Message 後,發現微軟傳遞的 JSON 結構極度龐大且複雜。而且裡面混雜了大量「系統區塊」(例如每天的彈性工作時間灰色背景,這根本不是真實行程),且行程 ID 的 Base64 編碼中常常帶有斜線 /,會破壞資料結構。
解法:寫一個「深度優先(DFS)」的遞迴掃描器,自動在複雜的 JSON 樹狀結構中,精準剝離出含有主題、時間、地點的真實行程,並自動過濾掉系統隱藏區塊。同時,對行程 ID 進行「消毒(Sanitize)」,把斜線全換成底線。
3. 繞過網頁 Content Security Policy (CSP) 限制
在 Outlook 網頁中,嚴格的 CSP 限制會阻擋我們直接向外部 API 發送請求。
解法:利用 Tampermonkey 的特權 API
GM_xmlhttpRequest。它是從瀏覽器擴充功能的背景(Background)發出請求,完全不受 Outlook 網頁端的 CSP 限制。我將整理好的行程資料,直接POST到我部署好的 Google Apps Script (GAS) Web App。
4. Google Apps Script 實時無感同步
GAS Web App 收到 POST 請求後,自動比對 Google 日曆上現有的行程描述(比對我們自訂塞進去的 Outlook ID),若不存在就建立,若時間或地點有變動就直接更新(Deduplication & Upsert)。
💡 實測成果
現在,只要我上班開著 Outlook 瀏覽日曆,Userscript 就會在背景默默把資料推送到 GAS Web App。
零時差:網頁載入或切換月份的瞬間,Google Calendar 馬上同步更新。
零維護:不需要 Firebase 資料庫,不用維持任何伺服器運行,完全無感。
用技術手段(科學)解決現實世界的效率障礙,用駭客精神(哲學)笑看 corporate 嚴格卻留有縫隙的安全限制。
有些路雖然迂迴,但沿途解決問題的風景,才是寫程式最讓人著迷的地方。驗證成功,爽快!
#GitHub #Userscript #GoogleAppsScript #Outlook #Productivity #HackerSpirit #WebWorker #Tampermonkey