iT邦幫忙

2026 iThome 鐵人賽

DAY 14
2
ChatGPT & Codex

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

Day 14 - Bot 功能都做好了,我卻忘了怎麼用:用 Codex App 和 Sol 替 LINE Cafe Bot 補上 Rich Menu

  • 分享至 

  • xImage
  •  

前幾天我打開自己的 LINE Cafe Bot,盯著輸入框幾秒,腦中冒出一個有點尷尬的問題:

我要輸入什麼,它才會開始找咖啡廳?

Bot 沒壞,功能其實比以前完整很多。它記得我偏好安靜、有插座的店,可以根據位置推薦附近咖啡廳,也能讓我選好時間再加入 Google Calendar。

問題出在我自己。我做了這些功能,卻還是得回頭翻聊天紀錄,找「查看我的偏好」或「設定我的偏好」到底要怎麼說。

這讓我重新想到一件很基本的事:功能存在,不代表使用者找得到。

於是這次我沒有繼續替 Bot 增加能力,而是幫它補上一個一直看得見的入口——LINE Rich Menu

前面的 Datetime Picker 開發記錄在這篇:從記住偏好到選好時間:我用 Codex Luna 和 LINE Datetime Picker 把咖啡廳推薦變成真的行程。這次不是再延長功能清單,而是把那些已經做好的東西整理到眼前。

先做減法:四格就夠了

Rich Menu 很容易愈做愈滿。收藏、提醒、換一批、選時間、Google Maps、Calendar,看起來每個功能都值得一格。

但真正適合放在固定選單裡的,應該是「不管對話進行到哪裡都能使用」的入口。最後我只留下四格:

  • 找附近咖啡
  • 我的偏好
  • 設定偏好
  • 使用說明

「找附近咖啡」會帶出分享位置的 Quick Reply;「我的偏好」直接查詢目前設定;「設定偏好」則帶入我常用的安靜、有插座、適合工作,並沿用原本的確認機制。

Datetime Picker 沒有放進去。因為它必須知道我正在看哪一次搜尋、點的是哪間店;離開那個情境,單獨開一個時間選擇器沒有意義。它繼續留在咖啡廳推薦卡片上。

這次最大的設計決定,反而是哪些東西不要放。

Rich Menu 不是只有一張圖片

畫面看起來是四個按鈕,技術上其實是兩層:

  1. 一張 2500×1686 的 PNG。
  2. 一份告訴 LINE 每個位置要做什麼的 JSON。

例如左上角的「找附近咖啡」:

{
  "bounds": {
    "x": 0,
    "y": 0,
    "width": 1250,
    "height": 843
  },
  "action": {
    "type": "message",
    "label": "找附近咖啡",
    "text": "找附近咖啡"
  }
}

點擊後,LINE 會替使用者送出「找附近咖啡」,再由原本的 webhook 回覆分享位置按鈕。

圖片和座標必須完全一致。看起來像按了左上角,如果 JSON 寫成右上角的範圍,觸發的就會是另一個功能。Rich Menu 的設計不是把圖畫漂亮就結束,畫面和行為要一起對齊。

https://ithelp.ithome.com.tw/upload/images/20260825/20183556iLtOP5zvUb.jpg

我不想每次改版都重新查 API

手動到 LINE Developers Console 建立一次選單並不難。比較麻煩的是幾週後想換圖,還要重新想起建立、上傳、設成預設的順序。

所以這次順手做了一個管理 CLI。執行:

npm run deploy

程式會自動完成:

  1. 從 SVG 產生 PNG。
  2. 檢查圖片是否超過 LINE 的 1 MB 限制。
  3. 建立 Rich Menu definition。
  4. 上傳圖片。
  5. 設定為所有好友的預設選單。
  6. 記錄新選單與上一個預設選單的 ID。

如果中途上傳失敗,剛建立但沒有完成的選單會被清掉,原本的預設選單則不受影響。

另外也保留 statuslistcreateuploadset-defaultdelete。其中刪除一定要給完整的 Rich Menu ID,避免一句「清掉舊選單」不小心把正在使用的版本也刪掉。

這段程式不華麗,但它讓 Rich Menu 從「我成功設定過一次」,變成「以後可以放心再改」。

這個小功能,還是踩了幾個坑

尺寸正確,不代表畫面正確

第一版 PNG 是合法的 2500×1686,檔案也小於 1 MB,但咖啡杯和問號圖示直接壓在中文字上。

程式不會替美感報錯。要不是把圖片真的打開來看,所有自動檢查都會顯示成功。

我把圖示縮小並往左移,再把標題調到右側,第二次輸出才得到現在的版本。這也成了這次最具體的提醒:視覺功能一定要做視覺驗收。

npm cache 權限不對

第一次安裝套件時,npm 發現全域 cache 裡有權限不一致的檔案。它建議用 sudo 修正,但這個專案沒有必要改動整台電腦的設定。

最後改成使用專案自己的 .npm-cache,並放進 .gitignore。安裝環境彼此隔離,問題也就停在這個專案裡。

能用的套件,不一定要繼續用

原本用來把 SVG 轉 PNG 的套件,在 npm audit 出現了高風險的上游公告,而且當下沒有可直接升級的修正版。

即使這裡只處理自己寫的 SVG,我還是把它換成 @resvg/resvg-js。換完後重新跑圖片輸出、TypeScript build、測試和 audit,最後回到零已知漏洞。

我很喜歡這個過程,因為它不是等到安全問題真的發生才修,而是在選擇還很便宜的時候換掉依賴。

這次為什麼用 Sol?

這次的程式量不算大,但一開始沒有唯一答案:選單要放哪些入口?哪些功能必須留在對話脈絡裡?圖片要怎麼維護?換版失敗時要怎麼保住舊選單?遇到有風險的套件,要接受、隔離還是替換?

這些問題都不只是「把 API 接起來」,而是需要一路做取捨。

依照 OpenAI 官方模型文件,Sol 適合複雜、開放式,而且需要更多分析、判斷與打磨的工作。這次使用 Sol,我最有感的並不是它寫了多少程式,而是它能把 UI、部署、安全與現有 Bot 的行為放在同一張圖裡考慮。

前一篇 Datetime Picker 的完成條件很清楚,Luna 很適合快速拆解與驗證;這次則有更多「怎樣才算做得剛好」的問題,所以我回到 Sol。

第一次用 Codex App 做完整流程

之前我比較習慣從 Console/CLI 和 Codex 互動。這次改用 App,最明顯的差別不是 AI 突然換了一套能力,而是我看待整個開發過程的方式變了。

在 App 裡,對話、檔案、終端輸出、Git 變更和圖片預覽都留在同一個工作空間。第一版選單產生後,我可以直接看見圖示壓住文字,而不是只在終端讀到:

Generated rich-menu.png (405615 bytes)

這行只能證明檔案成功產生,不能證明它好看或好用。

根據 OpenAI 官方 Codex App 指令文件,App 提供檔案搜尋、檔案樹、review diff、內建 terminal 與 browser 等介面。對這種圖片和程式要來回調整的任務,視覺化工作區確實比較順手。

那 Console/CLI 版還適合什麼?

CLI 依然有它很舒服的地方。打開終端、進入專案目錄就能開始工作;如果要把任務接進 script 或 CI,也可以使用 codex exec 做非互動執行,輸出文字或 JSONL。相關用法可以參考 OpenAI 官方 Developer Commands

對我來說,兩邊的差別可以簡單理解成:

  • 想快速待在終端、操作遠端環境或做自動化,我會用 CLI。
  • 需要看圖片、檔案、diff,並在多次修改中掌握整體狀態,我會用 App。

使用 App 不代表不用指令。這次一樣執行了 npm、Git、gcloud 和 LINE API;只是這些操作的結果,不必散落在不同視窗裡。

一個不聰明,卻很有用的更新

Rich Menu 沒有使用 Gemini,也沒有增加新的推薦演算法。單看技術亮點,它甚至可能是最近幾次更新裡最樸素的一個。

但現在打開 Cafe Bot,我不用先想起正確句子,也不用翻聊天紀錄。我要找店、看偏好或重新設定,入口就在畫面底部。

有時候產品往前走,不是因為又多會一件事,而是原本會的那些事,終於不再需要被記住。

專案開源與完整程式碼

👉 GitHub:https://github.com/zonawang/line-cafe-rich-menu
👉 更多 LINE Bot 與 AI 實作紀錄: https://github.com/zonawang/zona-ai-learning-lab


上一篇
Day 13 - 從記住偏好到選好時間:我用 Codex Luna 和 LINE Datetime Picker 把咖啡廳推薦變成真的行程
下一篇
Day 15 - 推薦完就忘了?我替 LINE Cafe Bot 加上咖啡足跡,記住真正去過的店
系列文
這隻 LINE Bot 不是我寫的:30 天讓 Codex 從零幫我做到上線16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言