Cal.com 預約提醒實作
由「查詢表單」升級到「可排 24h / 1h 會前提醒」
原本流程只做到「提交後即時回覆 + 24 小時後跟進」,原因唔係排程器唔夠,而係資料本身冇「預約時間」。提醒信本質係一個絕對時間任務;如果冇 booking_at,系統根本唔知道要喺邊個時間點前寄出。
今次做法係:用 Cal.com 做排程入口,`yoc-funnel` 做 webhook、D1 同 email jobs 中樞,保持之後可換工具都唔使重做整條資料鏈。
遇到嘅問題:點解「查詢表單」做唔到會前提醒?
當時 `contact.html` 係查詢模式,訪客唔會揀時段,所以 API 收到嘅只有「有人提交咗表單」。呢種資料只適合做相對時間流程(例如 +24h follow-up),做唔到絕對時間提醒(例如會前 24h / 1h)。
- 缺欄位:冇 `booking_at`,提醒任務無法排程。
- 改期取消:冇 `booking_id`,就算有新時間都難以取消舊提醒。
- 狀態不明:冇 `status`,流程唔知應該重排定停發。
解法:Cal.com 做前台排程,yoc-funnel 做中樞
落地時先固定最小必要欄位:`email`、`name`、`booking_at`、`event_type`、`booking_id`、`status`。跟住由 webhook 進入 `yoc-funnel`,寫入 D1 `bookings`,再排 email jobs:
- `booking-autoreply`:當 webhook 收到有效預約時,系統會即時建立確認信任務,確保預約者當下收到已成功預約的回覆。
- `booking-reminder-24h`:系統按 `booking_at` 計算會前 24 小時並建立提醒任務,避免只靠人工記錄造成遺漏。
- `booking-reminder-1h`:系統在會前 1 小時再建立一次提醒,減少預約者臨時忘記時段的情況。
收到 `rescheduled` / `cancelled` 時,流程會先取消舊 reminder,再按新時間重排(取消則只停發)。咁做可以避免同一個 booking 出現重覆提醒。
部署時實際卡點:登入狀態同 CLI 參數差異
本機 SQL 內容本身冇問題,但 remote D1 操作階段先後遇到兩類實際錯誤:第一類係未登入狀態(`You are not authenticated. Please run wrangler login.`);第二類係指令參數同 Wrangler 版本差異(例如 `--account-id` 被判定為未知參數)。當時排查重點唔係改 SQL,而係先把帳戶登入同指令格式對齊。
- 先把指令拆開逐行執行,避免多條命令黐埋一行造成參數解析錯誤。
- 重新做 `wrangler login` 並用 `wrangler whoami` 確認帳戶與權限狀態。
- 按當前 Wrangler 版本支援的參數重跑 remote 指令,避免沿用舊寫法。
- 確認 remote schema 成功套用後,先處理 secrets 同 deploy,避免跳步造成定位困難。
如何測試:created / rescheduled / cancelled 三段驗證
測試重點係「資料入庫正確」同「提醒任務狀態正確」。唔只睇 API 回 `ok`,要一齊查 `bookings` 同 `email_jobs`。
- created:應該建立 booking,並排出 autoreply + 24h + 1h jobs。
- rescheduled:舊 reminder 變 `CANCELLED`,新時間 jobs 重新建立。
- cancelled:相關 reminder 都應變 `CANCELLED`,唔再發送。
最後再做一次端到端:由 Cal.com 真實預約一筆,確認 webhook、D1、email outbox 三段都對齊。
實戰排查:由 403 到鎖定 Bot Fight Mode
最難嘅位唔係 webhook code,而係「Cal ping test 失敗但本機 curl 正常」。呢種情況好易誤判成程式壞。實際排查時我做咗以下幾輪:
- 重做基礎設定:先重新登入 Wrangler、重設 webhook secret,再完整重跑 deploy,先排除憑證狀態同版本未同步造成的假錯誤。
- 拆開驗證層:用 `curl` 直接呼叫 `POST /booking/webhook`,確認 Worker route、授權邏輯同 D1 寫入本身都正常運作。
- 改用第三方接收:把 Cal subscriber URL 暫時改到 webhook.site,確認 Cal 確實有把 webhook request 正常送出。
- 加即時觀察:同時開 `wrangler tail` 觀察入站;如果 Cal ping 後完全冇 `POST`,就代表 request 尚未進入 Worker。
- 回到 Cloudflare:在 Security Events 對同一時間戳,看到 `Managed Challenge` 並標示服務為 `Bot fight mode`,由此確認係邊緣層攔截。
最後用「兩邊確認」收斂:Cal 端(webhook.site 可收)+ Cloudflare 端(Events 顯示 Bot challenge)+ Worker 端(tail 無入站)。三邊對齊後,真正原因就唔係 payload 或 secret,而係 Bot Fight Mode 攔截 server-to-server webhook。
處理方式係:先臨時關閉 Bot Fight 驗證,再按 path 做最小放行策略。當 Cal ping 變 200、tail 見到 `POST /booking/webhook`,先算真正修好。
你都想加會前提醒,但唔想綁死單一平台?
可以先做一種 event type 嘅最小落地版本,確保 webhook、排程、改期取消流程都穩,再逐步擴展去 1:1 / 小組 / Live。
預約 30 分鐘診斷:幫你定中樞欄位同最小可行自動化。