iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
ChatGPT & Codex

這隻 LINE Bot 不是我寫的:30 天讓 Codex 從零幫我做到上線系列 第 27

Day 27 - 咖啡廳選好了,大家還是約不成:我讓 LINE Bot 繼續幫群組選時間

  • 分享至 

  • xImage
  •  

上一次,我把 LINE Cafe Bot 拉進群組,讓大家可以一起加入候選咖啡廳、投票,最後選出最想去的那一間。

原本以為店選好之後,聚會就已經成功一半。

但真正約過朋友就會知道,群組裡接下來往往還有另一關:

「那禮拜六下午呢?」
「我三點後才可以。」
「禮拜日會不會比較好?」
「我都可以,你們決定~」

所以店家雖然選出來了,行程卻可能又停在聊天訊息裡。

這次我想把流程再往後接一步:

既然 Bot 已經知道大家選了哪間咖啡廳,可不可以接著幫大家把時間也定下來?

於是我和 Codex 一起完成了 line-cafe-group-scheduler

這次我沒有只給 Codex 一句「做一個時間投票」,就請它從空白開始。我請它先讀上一版的 line-cafe-group-planner,弄清楚選店結果存在哪裡、LINE 的 postback 怎麼分流,以及群組和成員分別用什麼 ID 辨識。

然後我說明真正的使用情境:不是某個人建好一張表請大家填,而是群組裡的人會一個接一個提時間,也可能隨時改變主意。

Codex 把這段口語需求整理成幾個必須共用的資料:

groupId      → 投票屬於哪個群組
scheduleId   → 按鈕屬於哪一輪時間投票
creatorId    → 誰能截止與處理平手
userId       → 是誰提時間、投票或改票

這是 Codex 在這次最重要的角色:我說「大家要一起選」,它幫我追問程式到底需要記得哪些事。

不用重新告訴 Bot 要去哪間

這個新功能不是另外做一個獨立的時間投票。

當咖啡廳投票出現單一最高票後,結果訊息會多一個按鈕:

接著一起選時間

按下後,Bot 會把剛才選出的咖啡廳直接帶進新流程,不用再輸入店名,也不用重新搜尋。

完整過程變成:

群組選出咖啡廳
        ↓
按「接著一起選時間」
        ↓
成員提出候選時間
        ↓
大家投票,也可以改票
        ↓
發起人截止投票
        ↓
加入 Calendar,並等待行程提醒

我很喜歡這種「接著做」的感覺。使用者不必理解 Bot 背後其實分成選店和選時間兩個功能,只會感覺自己正順著同一件事往下完成。

https://ithelp.ithome.com.tw/upload/images/20260907/20183556t1cZ3yBXlj.png

候選時間不是只能由一個人提

如果只讓發起人建立所有時間,他還是要先在群組裡問完每個人,再自己整理答案。

這樣只是把工作從聊天室搬到 Bot,並沒有真正變輕鬆。

所以這次的規則是:

  • 群組裡每個人都能提出時間。
  • 一次最多保留 5 個有效候選。
  • 可選範圍是 10 分鐘後到 60 天內。
  • 已經過期的候選不會一直占住名額。

成員按「提出候選時間」後,LINE 會直接打開 Datetime Picker。他不用自己輸入「這個禮拜六下午三點」,也不會出現每個人日期格式都不一樣的問題。

第一個人加入時間後,下一個人可以接著提第二個、第三個,直到找到幾個大家真的有可能配合的選項。

https://ithelp.ithome.com.tw/upload/images/20260907/20183556w1iMSoBN0U.png

Datetime Picker 選完後,Bot 怎麼知道是哪一輪投票?

LINE Datetime Picker 會幫使用者打開選擇介面,但真正按下確定後,webhook 傳回的資料分成兩部分。

一部分是我放進按鈕的識別資料:

v=1&gs=add&s=schedule_123

它表示這是「新增時間」動作,而且屬於 schedule_123 這一輪。

另一部分則是 LINE 放在 postback.params.datetime 的選擇結果:

2026-09-12T15:00

Bot 收到後,還會檢查群組 ID、排程 ID、時區與日期範圍,不是看到一個日期就直接寫進去。

這樣即使聊天室裡同時留著舊卡片,Bot 還是能分辨這個時間究竟屬於哪個群組、哪一輪投票。

這部分也是 Codex 幫我補出的邊界。我原本只在意「選完日期能不能存下來」,它則往後想到:如果一個月後有人回頭按舊卡片呢?因此選擇結果不能只有時間,還必須帶著可以驗證的投票身分。

每個人只有一張現在有效的票

時間投票沿用了群組選店時很實用的規則:每個人只有一票,但截止前可以改。

例如我原本投了禮拜六下午,後來發現禮拜日比較方便,只要再按一次新選項,Bot 就會把我原本的票移過去。

它不會因為我按了兩次,就讓我多出一票。

資料上也不是每按一次就新增一張票,而是用 userId 記錄每個人目前選的 optionId

{
  "user_A": "option_2",
  "user_B": "option_1",
  "user_C": "option_2"
}

user_A 改投時,程式只會更新他的值。更新會放在 Firestore transaction 裡,避免多位成員同時操作時,後寫入的人不小心蓋掉剛才的結果。

投票後,Bot 會重新顯示所有候選和當前票數。如果聊天訊息太多,也可以輸入:

查看群組時間

隨時取得最新結果。

只有發起人能截止,但平手不會卡住

所有成員都能提時間、投票和改票,但只有原本的群組選店發起人可以截止。

我想保留一個明確的決定者,不然只要有人覺得票數差不多了,就可能在其他人還沒表態時直接結束。

截止後,會出現兩種結果:

單一最高票 → 直接確認聚會時間
最高票平手 → 由發起人從平手選項中決選

Bot 不會隨機幫大家選一個,因為投票的意義是讓結果反映群組的選擇。遇到平手時,把最後決定權交給原發起人,比假裝 Bot 有一個「最公平答案」更誠實。

程式裡會用三個狀態表示這段流程:

collecting → tie_break → confirmed
     └───────────↗

正在收集時間時是 collecting;平手才進入 tie_break;只有選出單一時間後才會成為 confirmed。每個新增、投票或截止動作都會先檢查狀態,所以已經確認的行程不會被舊按鈕改掉。

https://ithelp.ithome.com.tw/upload/images/20260907/201835564KyJTja2i3.png

時間確定之後,不要又回到聊天室重新整理

當店家和時間都確定後,Bot 會整理出一則完整結果:

  • 咖啡廳名稱。
  • 聚會日期與時間。
  • Google Maps 入口。
  • 預設 90 分鐘的 Google Calendar 連結。
  • 聚會前的群組提醒。

每個人都可以自己按「加入 Calendar」,不用再從聊天訊息裡複製店名和時間。

Bot 預設會在聚會前 60 分鐘提醒整個群組。如果大家選的時間已經很近,它也不會安排一個早就過去的提醒,而是改在「現在到聚會之間的一半」送出。

例如 30 分鐘後就要見面,Bot 會在 15 分鐘後提醒,不會因為來不及提前一小時,就完全不做任何事。

提醒不是靠 Bot 一直在背景倒數,而是在時間確認後建立 Cloud Task。Task 到點才呼叫提醒入口,而 Firestore 會記錄 scheduled → sending → sent 狀態。即使過程中需要重試,同一個行程也不會因此對群組連續提醒好幾次。

https://ithelp.ithome.com.tw/upload/images/20260907/20183556m2T1U9YgUL.png

舊卡片不能回頭改掉新行程

LINE 群組裡的舊訊息會一直留著。如果上週的時間卡片還可以改到這週的投票,大家就不會知道眼前的結果還能不能相信。

因此每一個時間按鈕都會對應特定的群組和這一輪投票。Bot 會拒絕:

  • 上一輪投票留下的按鈕。
  • 從其他群組帶來的操作。
  • 已經確認後再投票或加時間。
  • 已經過期的時間選項。

而且當群組已經有一筆還沒到來的確認行程時,Bot 不會讓新投票把它蓋掉。

這些規則平常不太會被注意,但它們決定了一個群組工具用久之後會不會變得很亂。

Codex 幫我把「一起選時間」拆成完整流程

一開始,我的需求其實只有一句:選完咖啡廳之後,幫大家把時間也定下來。

Codex 幫我往下補齊了很多實際會發生的問題:

  • 誰可以提出時間?
  • 候選會不會無限增加?
  • 同一個人改票時怎麼計算?
  • 誰可以宣布截止?
  • 平手要怎麼處理?
  • 確定後如何變成真的行程?

我們的分工比較像這樣:

我:提出朋友真正會怎麼約、哪裡讓我覺得卡
Codex:讀懂現有程式,把情境轉成資料、權限和狀態
我:回到 LINE 裡實際點按鈕,確認用起來像不像真實對話
Codex:根據操作結果繼續追 handler、Firestore 和訊息流程

Codex 並沒有為這個功能重寫整隻 Bot。它保留原本的群組選店,從「單一最高票店家」這個已有結果開始,再將新的 schedule store、Datetime Picker action、時間卡片和提醒一層一層接上去。

它也沒有為了省事,在平手時隨機挑一個。當 Codex 把「平手怎麼辦」放回來問我時,我才確定最後決定權應該留給原發起人。這種來回確認,讓最後的規則不是 AI 自己猜的,而是真正符合我想做的產品。

最後,Codex 才把這些規則接回原本的群組選店、LINE Datetime Picker、Google Calendar 與提醒流程。

不過,功能完成後,我真的打開 LINE 操作,還是馬上找到了兩個問題:

  1. 非發起人誤按「截止投票」後,發起人反而找不到可以接手的按鈕。
  2. 加入第一個時間後,畫面只剩「查看並投票」,明明可以有 5 個候選,卻沒有下一步能繼續加。

這兩個都不是功能完全壞掉,而是「程式做得到,但使用者走不下去」。

下一篇,我會專門整理這兩個問題,以及我為什麼開始覺得:一個好的 Bot 不只要回答正確,還要把下一步留在使用者面前。

完整程式碼與參考資料

GitHub:
https://github.com/zonawang/line-cafe-group-scheduler

LINE Messaging API — Datetime picker action:
https://developers.line.biz/en/reference/messaging-api/#datetime-picker-action

LINE Messaging API — Group chats:
https://developers.line.biz/en/docs/messaging-api/group-chats/

LINE Messaging API — Quick reply:
https://developers.line.biz/en/docs/messaging-api/using-quick-reply/

Google Cloud Tasks — Create HTTP target tasks:
https://cloud.google.com/tasks/docs/creating-http-target-tasks

Cloud Firestore — Transactions:
https://firebase.google.com/docs/firestore/manage-data/transactions


上一篇
Day 26 - 群組裡沒有 Loading,卡片也放錯按鈕:一個 LINE Bot 上線後才出現的兩道題
下一篇
Day 28 - 不是發起人按了截止,接下來呢?我修好 LINE Bot 裡消失的下一步
系列文
這隻 LINE Bot 不是我寫的:30 天讓 Codex 從零幫我做到上線28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言