上一篇,我讓 LINE Cafe Companion 開始記住使用者的咖啡偏好。
使用者可以說「安靜、有插座、適合工作」,下一次分享位置時,Bot 就會把這些條件帶進推薦。那一版解決的是「我想去哪一種咖啡廳」:Bot 不必每次都從頭認識我。
不過偏好記住了,推薦也出來了,還有一個很日常的斷點沒有被處理:看到一間喜歡的店後,通常會想著「改天去」,然後就沒有然後了。
所以這次我做了一個更小、但剛好把流程接起來的功能:讓每張咖啡廳推薦卡都能直接開啟 LINE 原生的 Datetime Picker,選好時間後再一鍵加入 Google Calendar。
這次我也特別改用 Codex Luna 來完成開發。
換句話說,上一篇讓 Bot 知道我想去什麼地方;這一篇,則是讓我可以把「想去」變成一個具體時間。
如果你還沒看過上一篇,可以先從這裡開始:從提醒到偏好記憶:我用 Codex Terra 讓 LINE Cafe Bot 不再每次都從頭認識你。
流程很短:
使用者不需要輸入 下週三下午三點去第二間 這種容易有歧義的句子,也不用自己在聊天視窗和行事曆之間切來切去。選擇店家、挑時間、建立行程,留在同一條 LINE 對話裡就能完成。

一開始也可以讓使用者直接輸入時間。但日期、下午晚上、跨月份和不同寫法,會讓自然語言解析變得複雜;更重要的是,使用者很難知道 Bot 最後理解成哪個時間。
LINE Message API 已經提供 Datetime Picker Action,所以這次我直接用它:
{
"type": "datetimepicker",
"label": "安排喝咖啡時間",
"data": "v=2&a=pick_time&s=abc123&c=2",
"mode": "datetime"
}
mode: "datetime" 會讓 LINE 顯示日期與時間的原生選擇介面。使用者選完後,LINE 會把結果放在 postback event 的 params.datetime,格式是:
2026-08-24T15:30
這很適合一個明確的操作:時間由使用者親手選定,程式只需要負責驗證、轉換和建立下一步行程,不必猜測語意。

真正麻煩的地方,不是把 Datetime Picker 放進卡片,而是使用者選完時間後,系統怎麼知道他剛剛點的是哪一間店?
我沒有把完整店名、座標或推薦資料塞進按鈕的 data 裡,而是先把這次搜尋存成短期的 Firestore session。每張店家卡片只帶這些必要資訊:
v=2&a=pick_time&s=abc123&c=2
v=2:Postback 資料版本。a=pick_time:這次要處理的是選時間。s=abc123:短期搜尋 session ID。c=2:這張卡片在本次推薦中的店家編號。收到 postback 後,後端再用 session ID 找回當時的推薦結果,取得正確店名。這樣做還有兩個好處:按鈕資料很小,而且不會把使用者的位置或其他推薦內容暴露在訊息裡。
搜尋 session 不只保存咖啡廳資料,也綁定了發起搜尋的 LINE userId 和對話來源,並設定 30 分鐘有效期限。
所以 Bot 收到 Datetime Picker 回傳的結果時,會依序確認:
這在一對一聊天看起來有點多,但 Bot 也可能被加進群組。多做這些驗證,才能避免 A 分享位置後,B 拿著舊按鈕替自己或別人建立錯誤行程。
LINE Datetime Picker 回傳的是台北當地時間,但字串本身沒有時區資訊。像 2026-08-24T15:30 如果直接當成 UTC 儲存,行事曆就會偏移八小時。
因此程式在驗證格式後,會把它明確轉成台灣時區的時間點,再建立 Google Calendar 的預填連結。最後 Bot 回覆的不是一串技術格式,而是使用者看得懂的內容:
已安排 8 月 24 日 15:30 到 XX Cafe 喝咖啡
下方再附上「加入 Google Calendar」。Datetime Picker 解決選時間,Calendar 則負責讓這個選擇真的留在使用者每天會看的地方。
這個 Datetime Picker Bot 也整合了上一篇的偏好記憶功能。使用者可以先說:
設定我的偏好:安靜、有插座、適合工作
確認後再傳送位置,推薦會顯示已套用的偏好;接著才從符合需求的店家裡挑一間、選時間。
兩個功能各自做一件事:偏好記憶幫忙縮小「去哪裡」的範圍,Datetime Picker 把「什麼時候去」定下來。它們放在一起後,推薦才不只是資訊列表,而更像一條可以走完的行動路徑。
設定偏好
↓
分享位置,取得適合的推薦
↓
選擇一家店
↓
LINE Datetime Picker 選時間
↓
加入 Google Calendar
前一篇偏好記憶功能,我使用的是 Codex Terra;這次我想試試看 Codex Luna。
Datetime Picker 乍看之下很像是「加一個按鈕」的功能,但它其實牽涉到既有的推薦卡片、LINE postback、Firestore 狀態、時區和 Google Calendar。Luna 在這次很適合協助我把工作切小:先讀現有流程,確認 LINE Action 的格式,再補上 session、訊息處理和測試,最後才部署到 Cloud Run。
開發中最容易踩到的幾個問題是:
200 OK,才能真的在 LINE 裡測。我的感受是,Luna 很適合這種邊界清楚、可以逐步驗證的功能:它能快速協助梳理既有程式和小範圍改動。我仍會把資料驗證、時區和部署檢查當成必做的人工確認,因為真正容易出錯的,往往是功能與功能接起來的地方。
這幾篇文章剛好讓我用不同模型做了不同類型的工作。依照 OpenAI 官方文件,Sol 適合複雜、開放式且需要更多判斷與打磨的任務;Terra 是日常工作的平衡型選擇;Luna 則適合目標清楚、可重複驗證的工作。
放到這個 LINE Bot 專案裡,我會這樣理解:
這不是把三者排成高低,而是先看問題的輪廓。需求還模糊、需要大量判斷時,用 Sol;需要穩定完成日常整合時,用 Terra;規格清楚、想有效率地反覆完成同類工作時,用 Luna。這次也提醒我:模型選擇只是起點,測試、部署檢查和手機上的實際操作,才是最後決定功能能不能交付的地方。
這次我把功能部署到 Cloud Run 的 codex-datetime-picker-action 服務,並保留原本的地圖推薦、「換一批」和「更適合工作」流程。部署後確認 health endpoint 正常,LINE Webhook 測試也回覆 200 OK。
實際用手機測試時,我會走一次完整流程:先設定偏好、傳送位置、確認推薦有套用偏好、點選其中一間店的時間按鈕、選擇日期時間,最後開啟 Google Calendar 檢查店名和時間。
這種測試比只看單元測試更重要。因為這個功能真正的價值,正是 LINE 的按鈕、postback、時區和行事曆能不能在同一支手機上順順地接起來。
上一篇的偏好記憶,讓 Bot 開始理解使用者想找什麼樣的地方;Datetime Picker 則提醒我,產品體驗不能只停在「推薦得不錯」。
人們常常不是不知道要去哪,而是少了一個把念頭變成行動的步驟。把選時間放在推薦卡片上,聽起來只是多一個按鈕,實際上卻把「這家看起來不錯」往前推到了「我已經安排好了」。
對我來說,這次最有意思的不是多了一個日期時間選擇器,而是讓推薦終於有了下一步:從「這家看起來不錯」,走到「我已經把時間留好了」。
👉 GitHub:
https://github.com/zonawang/codex-datetime-picker-action
👉 更多 LINE Bot 與 AI 實作紀錄: https://github.com/zonawang/zona-ai-learning-lab