iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
ChatGPT & Codex

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

Day 25 - 大家都說「都可以」,結果最難的是決定去哪:我把 LINE Bot 拉進群組一起投票

  • 分享至 

  • xImage
  •  

每次朋友約喝咖啡,群組裡很容易出現這段對話:

「週末要不要喝咖啡?」
「好啊!」
「那要去哪一間?」
「我都可以~」

看起來每個人都很好配合,事情卻常常停在這裡。

有人丟了一間店,其他人回貼圖;又有人貼了另一間,過幾個小時後,前面的連結已經被聊天訊息洗上去。最後不是由其中一個人默默決定,就是到了出門前還在問:「所以我們到底要去哪?」

我的 LINE Cafe Bot 原本已經能找附近咖啡廳,但那比較像我和 Bot 之間的一對一對話。

這次我想解決的是另一種情境:

如果 Bot 也在朋友群組裡,可不可以把大家的「都可以」,整理成真的選得出結果的投票?

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

我需要的不是另一個投票連結

LINE 本來就有投票功能,網路上也有很多表單工具。

但如果使用那些方法,我還是得先自己找店、複製店名和網址,再一間一間建立選項。投票只是最後一步,前面最麻煩的「有哪些店可以選」仍然沒有人處理。

所以這次的流程是從地點開始:

在群組傳送聚會位置
        ↓
Bot 推薦附近咖啡廳
        ↓
大家加入喜歡的候選店
        ↓
每個人投一票
        ↓
發起人截止並公布結果

使用者可以先輸入:

一起選咖啡廳

也可以更直覺地直接在群組傳送位置。最後我決定兩種方式都支援,因為一個需要先看說明才能使用的功能,很容易在真正聊天時被忘記。

同一個 Bot,進到群組後多了兩個身分

一對一聊天時,我通常只需要知道「是哪位使用者」。

群組情境不一樣。LINE webhook 會帶來兩個重要資訊:

  • groupId:這一輪投票屬於哪個群組。
  • userId:這個動作是哪位群組成員做的。

它們分別回答不同問題:

groupId → 候選店和投票應該存在哪裡
userId  → 誰建立投票、誰投了哪一間

如果只記 userId,每個人看到的可能是自己的清單,而不是群組共同的結果。

如果只記 groupId,Bot 又無法限制每個人只有一票,也不知道誰有權截止。

因此這次不是單純把原本的 Bot 邀進群組,而是重新整理「群組共同狀態」和「個別成員操作」的關係。

候選不是越多越好

群組選店最容易發生的另一件事,是每個人都貼一間,最後候選比原本搜尋結果還多。

所以我替第一版訂了幾個簡單規則:

  • 一次最多 5 間候選店。
  • 同一間 Google Maps 店家不會重複加入。
  • 每個群組同時只有一輪進行中的投票。
  • 投票保留 24 小時。

推薦卡片在群組模式下也刻意保持單純,只留下:

加入群組候選
先看地圖資訊

安排時間、記錄造訪和加入個人想去清單都還是原本 Bot 的功能,但它們不是大家現在要完成的事。

當目標是一起選店時,卡片上最醒目的按鈕就應該直接帶大家往下一步走。

https://ithelp.ithome.com.tw/upload/images/20260905/20183556xBmgnty4we.jpg

一個人只有一張「現在有效的票」

我不想限制大家按下按鈕後就不能反悔。

所以票數不是用「每按一次就加一」來保存,而是記錄每個人目前選擇哪個候選:

{
  "user_A": "cafe_2",
  "user_B": "cafe_1",
  "user_C": "cafe_2"
}

如果 user_A 後來改投 cafe_1,程式會直接更新他的選擇,而不是讓他多出第二票。

這樣同時做到兩件事:

  • 每位成員永遠只有一張有效票。
  • 大家可以在截止前改變心意。

每次投票後,Bot 都會重新顯示目前票數。想稍後回來看,也可以輸入「查看群組投票」。

截止不是誰手快誰說了算

群組裡所有人都可以:

  • 加入候選。
  • 投票或改票。
  • 查看目前票數。

但是只有這輪投票的發起人可以按下「截止並公布結果」。

如果最高票只有一間,Bot 會顯示店名、票數和 Google Maps 入口。

如果兩間店同票,它不會假裝很聰明地隨便選一間,而是直接說明平手:

最高票平手:
「Cafe A」、「Cafe B」,各 2 票。

因為這個功能的工作是整理大家的選擇,不是替群組偷偷改規則。

https://ithelp.ithome.com.tw/upload/images/20260905/20183556vwxNwpBwsb.jpg

舊按鈕不能改到新一輪投票

LINE 聊天記錄不會因為投票結束就消失。

如果今天的舊卡片到了下週還能點,而且點下去竟然改到新投票,整個票數會變得很難相信。

所以每一輪都會產生自己的 planId。按鈕送回來時,程式會一起檢查:

  • 是否仍是目前這一輪。
  • 是否來自原本的群組。
  • 是否已經截止。
  • 是否超過 24 小時。
  • 候選店是否仍然存在。

只要其中一項不對,就不會修改票數,而是請使用者取得最新的投票內容。

我沒有替每個人建立一份個人資料

群組功能需要分辨投票者,但不代表我需要知道每個人的名字或照片。

目前 Firestore 保存的是:

  • LINE group ID。
  • LINE user ID。
  • Google Maps 店名與網址。
  • 候選、票數、狀態和到期時間。

Bot 不會為了顯示「某某投了哪間」,額外呼叫 profile API 取得成員名稱。

第一版只顯示每間店的總票數。這已經足夠完成選店,也少保存一些沒有必要的個人資訊。

Codex 幫我把一句「大家一起選」拆成真正的規則

一開始我的需求很生活化:我只是厭倦每次約咖啡都卡在「我都可以」。

Codex 幫我繼續往下問:

  • 一個群組能同時有幾輪投票?
  • 同一個人可不可以改票?
  • 誰可以截止?
  • 平手怎麼辦?
  • 舊卡片還能不能使用?
  • 同一間店怎麼避免被重複加入?

這些問題看起來沒有「搜尋咖啡廳」那麼亮眼,卻決定了投票結果能不能被相信。

最後 Codex 把群組事件、Firestore transaction、postback 驗證、Flex Message 和原本的咖啡廳搜尋接在一起,也替正常流程與各種邊界補上測試。

我負責說明朋友真的會怎麼聊天,Codex 則把那些模糊的互動變成清楚的產品規則。

功能做完後,我真的把 Bot 拉進了群組

到這裡,程式已經可以:

找店 → 加入候選 → 投票/改票 → 公布結果

看起來功能完成了。

但真的把 Bot 邀進群組、傳出第一個位置後,我馬上發現兩件不對勁的事:

  1. 一對一聊天有 Loading 動畫,群組裡卻完全沒有。
  2. 推薦卡片顯示的仍是「安排喝咖啡時間」、「記錄這次造訪」和「加入想去清單」。

也就是說,程式裡有群組投票,不代表使用者在群組裡真的找得到它。

這兩個只有實際操作才會出現的問題,以及我和 Codex 怎麼一路查到 LINE API 限制、執行中的舊服務和 webhook 切換,會放在下一篇。

完整程式碼與官方文件

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

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

LINE Messaging API — Webhook event objects:
https://developers.line.biz/en/reference/messaging-api/#webhook-event-objects

LINE Messaging API — Postback action:
https://developers.line.biz/en/reference/messaging-api/#postback-action


上一篇
Day 24 - AI 看得懂菜單,為什麼還不能直接上線?我和 Codex 替飲品推薦加上的幾道保護
下一篇
Day 26 - 群組裡沒有 Loading,卡片也放錯按鈕:一個 LINE Bot 上線後才出現的兩道題
系列文
這隻 LINE Bot 不是我寫的:30 天讓 Codex 從零幫我做到上線28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言