DAY 194

看到活動一鍵丟進行事曆

SLIDES · 9
Day 194 看到活動一鍵丟進行事曆 — 投影片 1Day 194 看到活動一鍵丟進行事曆 — 投影片 2Day 194 看到活動一鍵丟進行事曆 — 投影片 3Day 194 看到活動一鍵丟進行事曆 — 投影片 4Day 194 看到活動一鍵丟進行事曆 — 投影片 5Day 194 看到活動一鍵丟進行事曆 — 投影片 6Day 194 看到活動一鍵丟進行事曆 — 投影片 7Day 194 看到活動一鍵丟進行事曆 — 投影片 8Day 194 看到活動一鍵丟進行事曆 — 投影片 9
1 / 9

最近為了拓展業務、找人交流,我會接觸到不少活動。這些活動資訊散落在各處——LINE 對話、Luma、Accupass、獨立網頁、社群貼文,有時候甚至只出現在一張圖上,不見得每個地方都支援一鍵加進行事曆。日程我一直是用 Google 日曆在管,麻煩的是每一場都得自己手動敲名稱、時間、地點;更煩的是——有些熱門活動報名完,還得等主辦通知有沒有被選上,我需要在行事曆上一眼就分得出來,哪些已經確定、哪些還卡在等結果。

其實就是有點麻煩,於是我用 Fable 寫了一套簡單的 Raycast(Mac 快捷指令面板、加強版 Spotlight)extension,取名 Event Sidekick——sidekick 是「副手」的意思,它幫我跑腿,主角還是我。用起來很直接:不管活動資訊出現在哪,只要一張截圖,就能把活動自動建好,不必再自己慢慢 key。看到活動,一個快捷鍵抓下來,AI 幫我把名稱、時間、地點抽成結構化欄位,順手比對 Google 日曆有沒有撞,然後——停在這裡,等我按確認才會真的訂。省下的就是每次那幾分鐘的麻煩。我從一開始就給它定死一條規則:AI 可以抽、可以建議,但絕不自己把東西寫進我的行事曆。

一個快捷鍵,從哪裡都能抓

抓的來源我盡量做到「你手邊是什麼就抓什麼」:

  • 剪貼簿裡是圖就讀圖、是網址就抓網頁、是文字就直接吃
  • 螢幕框選(screencapture -i,按 Esc 取消)擷取任何畫面
  • 選取中的文字直接送進去

如果一張截圖裝不下——像那種落落長的活動說明——可以連拍好幾張,丟進一個 screenshot stack(截圖暫存區),最後讓 AI 把這幾張湊成同一場活動。抓下來的東西也不會憑空消失:還未確定的會列在暫存區,中途被打斷也不怕弄丟。

截取完活動資訊後不直接建立

AI 把欄位抽好之後,跳出來的不是「已經幫你訂好了」,而是一張可以逐欄修改的確認單:每個欄位都能改,旁邊擺著 AI 的信心分數,還有它是從哪一段原始文字抽出來的——你可以當場對照它有沒有讀錯。

同一個畫面,它也已經去問過 Google 行事曆那個時段你有沒有空。這裡拿 freeBusy(Google 一支只回你「某段時間忙或不忙」、不吐內容的 API)當準,再用 events.list 補上衝突事件的標題(碰到私人事件就只顯示一塊匿名的忙碌時段)。你還可以設一個 buffer,要求活動前後各留 15/30/60 分鐘才算真的沒撞。

接著它會給一個很直白的建議:Book(訂)、Maybe(再看看)、Skip(跳過)。信心低於四成、或這場活動一天前就結束了,直接 Skip;有衝突、缺欄位、信心不夠、或抽取時標了模稜兩可的地方,降到 Maybe。有一條規則我覺得最關鍵——只要行事曆「還沒查成功」,它最高只能給到 Maybe,因為這工具存在的唯一理由,就是先幫你查。

這也接回開頭那個麻煩:報名了熱門活動、還在等主辦通知有沒有上的那種,就用暫訂先把時段佔起來。不確定的活動(信心低、日期用猜的、缺結束時間)一律導去 Pending Approval(暫訂),在 Google 行事曆上一眼就認得出來:標題掛 [Pending] 前綴、事件染成灰色、狀態設成 tentative。等對方真的確認你上了,再回來按一下 Mark Approved,前綴 [Pending]、顏色、狀態 tentative(暫訂)一次清乾淨,轉成正式行程。

沒有 Anthropic key 也能用,抽取這段不連網

想要最好的抽取品質,就掛一把 Anthropic API key,抽取交給 Claude 的 Fable。但我特別留了一條路:不填 key,抽取就改用 macOS 內建的 Vision OCR(光學文字辨識,把圖片裡的字讀出來)加一個小小的 heuristic parser(靠規則去猜時間地點的解析器,完全不碰 AI)。品質當然差一截、要多手動改幾個欄位,但「把截圖變成欄位」這一段完全在本機跑,沒有東西送出去。

先講清楚,免得誤會:查衝突跟真正把 event 建起來,一定得連 Google Calendar 的 API,這條省不掉——行事曆本來就在 Google 上。我能決定的只有「抽取」要不要動用雲端;不填 Anthropic key,抽取就純靠本機 OCR,會出門的就只剩 Google 那幾支必要的呼叫。

至於有掛 key 的情況,也只有「要抽取的那段文字/網頁內容/圖片」會送去 Claude,而且圖片送出前會先重新編碼成 PNG——順手把 EXIF/GPS(照片內嵌的拍攝資訊,含地點座標)清掉——再壓到 4.5 MB 以下。

為什麼一個記行事曆的小工具,我做了一堆資安防護

答案藏在「從網址抓活動」那條路上:你貼一個 URL,我的程式就得去把那個頁面抓下來、把文字送去 Anthropic API。這代表它會「照著一個外部給的網址去發請求」——而這正是 SSRF(Server-Side Request Forgery,誘導伺服器去打它本來不該打的位址,經典目標是雲端的內網 metadata)最愛的溫床。

所以這條路上我擋了一整排:

  • 只收 http/https,網址裡夾帶帳密的一律拒絕
  • loopback、私有網段、link-local(雲端 metadata 位址)、CGNAT、multicast 全部擋掉——而且不只擋 IP 字面值,連 DNS 解析出來的位址也要再驗一次
  • redirect 每一跳都重新驗(最多五跳)
  • 回應用串流讀,讀到 1.5 MB 就切斷;圖片在真正讀進來之前先檢查大小

我甚至把還沒解決的殘留風險寫進 README:DNS rebinding(我驗過之後,惡意 DNS 再偷改一次答案,把連線導去內網)理論上還是有機會——要根治得自己接一個 undici 的 custom Agent(Node.js HTTP client 的自定義代理),而 Raycast 的執行環境沒給我乾淨的介面做這件事。與其假裝沒這回事,我寧可寫清楚。

Google 授權也走最小權限:用 Raycast 的 PKCE(一種不必保存 client secret 的 OAuth 授權流程),只要 calendar.events.ownedfreebusy 兩個 scope(權限範圍),能不多拿就不多拿。

適合誰、接下來想補什麼

適合常被活動公告轟炸、又怕漏訂或重複訂的人——尤其你的活動來源很雜,截圖、網頁、email 混著來。它目前是我自己私用的擴充,還沒上架 Raycast store,想跑就得自己 clone 下來 yarn dev

接下來想補的:抽完之後自動判斷要不要順手排個提醒,還有把 pending 清單做成能批次處理。

一個 AI 工具,你大概會以為最得意的是模型抽得多準;但我自己最喜歡的,反而是那條「絕不自己訂」的規則。AI 幫你讀、幫你查、幫你把欄位都填好,把最後那一下確認留給你——這樣我才敢真的放心用它。

最近開始做免費的一對一諮詢,幫你把 AI 接進自己的工作流——有需要的話可以約:https://www.dawsonwang.com/

延伸閱讀
看完整 194 篇 →