每次朋友約喝咖啡,群組裡很容易出現這段對話:
「週末要不要喝咖啡?」
「好啊!」
「那要去哪一間?」
「我都可以~」
看起來每個人都很好配合,事情卻常常停在這裡。
有人丟了一間店,其他人回貼圖;又有人貼了另一間,過幾個小時後,前面的連結已經被聊天訊息洗上去。最後不是由其中一個人默默決定,就是到了出門前還在問:「所以我們到底要去哪?」
我的 LINE Cafe Bot 原本已經能找附近咖啡廳,但那比較像我和 Bot 之間的一對一對話。
這次我想解決的是另一種情境:
如果 Bot 也在朋友群組裡,可不可以把大家的「都可以」,整理成真的選得出結果的投票?
於是我和 Codex 一起完成了 line-cafe-group-planner。
LINE 本來就有投票功能,網路上也有很多表單工具。
但如果使用那些方法,我還是得先自己找店、複製店名和網址,再一間一間建立選項。投票只是最後一步,前面最麻煩的「有哪些店可以選」仍然沒有人處理。
所以這次的流程是從地點開始:
在群組傳送聚會位置
↓
Bot 推薦附近咖啡廳
↓
大家加入喜歡的候選店
↓
每個人投一票
↓
發起人截止並公布結果
使用者可以先輸入:
一起選咖啡廳
也可以更直覺地直接在群組傳送位置。最後我決定兩種方式都支援,因為一個需要先看說明才能使用的功能,很容易在真正聊天時被忘記。
一對一聊天時,我通常只需要知道「是哪位使用者」。
群組情境不一樣。LINE webhook 會帶來兩個重要資訊:
groupId:這一輪投票屬於哪個群組。userId:這個動作是哪位群組成員做的。它們分別回答不同問題:
groupId → 候選店和投票應該存在哪裡
userId → 誰建立投票、誰投了哪一間
如果只記 userId,每個人看到的可能是自己的清單,而不是群組共同的結果。
如果只記 groupId,Bot 又無法限制每個人只有一票,也不知道誰有權截止。
因此這次不是單純把原本的 Bot 邀進群組,而是重新整理「群組共同狀態」和「個別成員操作」的關係。
群組選店最容易發生的另一件事,是每個人都貼一間,最後候選比原本搜尋結果還多。
所以我替第一版訂了幾個簡單規則:
推薦卡片在群組模式下也刻意保持單純,只留下:
加入群組候選
先看地圖資訊
安排時間、記錄造訪和加入個人想去清單都還是原本 Bot 的功能,但它們不是大家現在要完成的事。
當目標是一起選店時,卡片上最醒目的按鈕就應該直接帶大家往下一步走。

我不想限制大家按下按鈕後就不能反悔。
所以票數不是用「每按一次就加一」來保存,而是記錄每個人目前選擇哪個候選:
{
"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 票。
因為這個功能的工作是整理大家的選擇,不是替群組偷偷改規則。

LINE 聊天記錄不會因為投票結束就消失。
如果今天的舊卡片到了下週還能點,而且點下去竟然改到新投票,整個票數會變得很難相信。
所以每一輪都會產生自己的 planId。按鈕送回來時,程式會一起檢查:
只要其中一項不對,就不會修改票數,而是請使用者取得最新的投票內容。
群組功能需要分辨投票者,但不代表我需要知道每個人的名字或照片。
目前 Firestore 保存的是:
Bot 不會為了顯示「某某投了哪間」,額外呼叫 profile API 取得成員名稱。
第一版只顯示每間店的總票數。這已經足夠完成選店,也少保存一些沒有必要的個人資訊。
一開始我的需求很生活化:我只是厭倦每次約咖啡都卡在「我都可以」。
Codex 幫我繼續往下問:
這些問題看起來沒有「搜尋咖啡廳」那麼亮眼,卻決定了投票結果能不能被相信。
最後 Codex 把群組事件、Firestore transaction、postback 驗證、Flex Message 和原本的咖啡廳搜尋接在一起,也替正常流程與各種邊界補上測試。
我負責說明朋友真的會怎麼聊天,Codex 則把那些模糊的互動變成清楚的產品規則。
到這裡,程式已經可以:
找店 → 加入候選 → 投票/改票 → 公布結果
看起來功能完成了。
但真的把 Bot 邀進群組、傳出第一個位置後,我馬上發現兩件不對勁的事:
也就是說,程式裡有群組投票,不代表使用者在群組裡真的找得到它。
這兩個只有實際操作才會出現的問題,以及我和 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