在昨天的文章中,我們透過 LINE Postback Action 與 SQLite 關聯資料庫,為美食 Agent 打造了個人專屬的收藏夾,讓使用者能一鍵收藏心儀的店家。
回顧過去 19 天的所有功能,系統的互動模式全部屬於**「被動響應(Passive Reply)」**:只有在使用者主動發送文字、點擊按鈕或分享位置時,機器人才會拿著單次有效的 replyToken 進行即時回覆。
但一個真正稱職的貼身生活助理,應該要具備**「主動關懷(Proactive Engagement)」**的能力!
例如:每到工作日的上午 11:30,當大家肚子開始咕嚕咕嚕叫、準備思考午餐時,Agent 能主動敲響使用者的聊天室:「嗨!午餐時間快到囉,今天推薦這家附近的日式定食,要不要試試看?」。
今天 Day 20,我們將學習如何利用 n8n 的 Schedule Trigger(Cron 定時排程) 結合 LINE Messaging API 的 Push Message 端點,打造全自動化的定時推播工作流!
在設計主動推播前,必須先釐清 LINE Messaging API 兩種主要的發訊機制:
| 比較項目 | Reply API (/v2/bot/message/reply) |
Push API (/v2/bot/message/push) |
|---|---|---|
| 觸發情境 | 使用者主動傳訊或點擊按鈕 | 系統主動發起(定時排程、警報通知) |
| 必要參數 | replyToken(單次有效、數秒內過期) |
to(使用者的 LINE userId) |
| 發送限制 | 依賴使用者事件,無法由系統隨時觸發 | 只要知道使用者的 userId,隨時皆可主動發送 |
| 官方計費 | 完全免費,不計入官方免費訊息額度 | 計入 LINE 官方帳號的免費推播額度(目前輕量方案每月 200 則) |
💡 開發者秘訣:
個人開發與測試時,每個月 200 則 Push 額度相當夠用。只要目標對象是我們自己在 SQLite 中記錄的測試userId,每天推播一次一個月也才消耗 30 則左右!
app/routers/push.py)為了讓 n8n 知道該把午餐推播發給誰、以及每個使用者的預設喜好地區,我們在微服務建立一個專門查詢活躍用戶的 API。
建立 app/routers/push.py:
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from pydantic import BaseModel
from app.database import get_db
from app.models.memory import UserModel
router = APIRouter(
prefix="/api/v1/push",
tags=["Push Notification"]
)
class PushTargetUser(BaseModel):
user_id: str
favorite_cuisine: str
default_budget: int
preferred_location: str
@router.get("/subscribers", response_model=List[PushTargetUser])
async def get_push_subscribers(db: Session = Depends(get_db)):
"""
獲取同意接收主動推播的使用者名單與其偏好設定
"""
# 這裡先撈出資料庫中已建檔的使用者(未來可額外加入 is_push_enabled 布林開關)
users = db.query(UserModel).all()
subscribers = []
for u in users:
subscribers.append(
PushTargetUser(
user_id=u.line_user_id,
favorite_cuisine=u.favorite_cuisine or "全部",
default_budget=u.default_budget or 300,
preferred_location="板橋區" # 或由使用者歷史足跡推算
)
)
return subscribers
在 app/main.py 中掛載該路由:
from app.routers import push
app.include_router(push.router)
在 n8n 介面(http://localhost:5678 或你的雲端網址)點擊 Add workflow,建立名為 Daily Lunch Push Notification 的獨立工作流。
1.點擊 Add first step,搜尋並新增 Schedule Trigger。
2.觸發設定(Trigger Rules):
Trigger Interval:選擇 Weeks 或 Cron Expression。
Cron Expression:30 11 * * 1-5(代表每逢週一至週五的上午 11:30 準時觸發)。
時區確認:確認 n8n 設定中的時區為 Asia/Taipei(UTC+8)。
1.新增 HTTP Request 節點,命名為 Get Subscribers。
2.設定如下:
Method:GET
URL:http://host.docker.internal:8000/api/v1/push/subscribers(若在雲端則替換為 Render 網址)。
3.測試執行後,n8n 會取得一個包含多位使用者的 JSON 陣列。
針對每一位使用者,呼叫我們在 Day 18 寫好的隨機抽籤 API:
1.新增 HTTP Request 節點,命名為 Get Random Lunch。
2.設定如下:
Method:POST
URL:http://host.docker.internal:8000/api/v1/recommend/random
JSON Body:
{
"user_id": "={{ $json.user_id }}",
"location": "={{ $json.preferred_location }}",
"budget_max": {{ $json.default_budget }}
}
新增 Code 節點,命名為 Build Push Message,為午餐時段量身打造親切的問候排版:
const response = $input.first().json;
const subscriber = $('Get Subscribers').first().json;
if (!response.drawn_restaurant) {
return []; // 若該區域查無餐廳則略過不推播
}
const shop = response.drawn_restaurant;
const mapUri = `[https://www.google.com/maps/search/?api=1&query=$](https://www.google.com/maps/search/?api=1&query=$){encodeURIComponent(shop.location + ' ' + shop.name)}`;
const greetingText = `☀️ 叮咚!午餐時間快到囉!\n\n` +
`今天中午不知道吃什麼嗎?小幫手為你挑選了這家:\n` +
`🍴【${shop.name}】\n` +
`⭐️ Google 評分:${shop.rating}\n` +
`💰 均消:約 ${shop.price_avg} 元\n` +
`📍 地址:${shop.location}\n\n` +
`祝你有個愉快的午餐時光!✨`;
return {
target_user_id: subscriber.user_id,
messages: [
{
type: "text",
text: greetingText,
quickReply: {
items: [
{
type: "action",
action: {
type: "uri",
label: "🗺️ 查看地圖導航",
uri: mapUri
}
},
{
type: "action",
action: {
type: "message",
label: "🎲 換一間推薦",
text: "幫我隨便抽一間"
}
}
]
}
}
]
};
這是最關鍵的一步,我們使用 Push API 主動發送訊息給指定的 userId:
1.新增 HTTP Request 節點,命名為 LINE Push Message。
2.參數配置:
Method:POST
URL:https://api.line.me/v2/bot/message/push
Authentication:Header Auth
Name:Authorization
Value:Bearer <你的_LINE_CHANNEL_ACCESS_TOKEN>
Headers:Content-Type: application/json
Specify Body:Using JSON
JSON 內容:
"to": "={{ $json.target_user_id }}",
"messages": "={{ $json.messages }}"
}
在 n8n 畫布中直接點擊下方的 Test workflow 按鈕(無需等待 11:30),模擬排程觸發:
1.n8n 節點依序亮起綠燈。
2.拿起手機,在完全沒有傳送任何訊息的前提下,LINE 聊天室立刻彈出推播通知!
3.訊息完整包含店名、地址、均消,底部更附帶「🗺️ 查看地圖導航」與「🎲 換一間推薦」的 Quick Reply 按鈕。
在 n8n 畫布右上角將工作流切換為 Active。
每週一到週五的上午11:30,伺服器就會在雲端自動喚醒工作流,準時將今日午餐靈感推送到每一位用戶的手機中!
今日結語與明日預告
今天我們讓美食 Agent 跨出了重大的一步:
搞懂了 LINE Reply API 與 Push API 的架構與權限差異。
利用 n8n Schedule Trigger 實現無伺服器、免寫 Cron Daemon 的優雅排程管理。
整合了先前的隨機盲盒演算法與 Quick Reply,打造出極具人情味的主動式午餐小秘書!
至此,我們已經完成了功能面(定位、FSM 多輪對話、轉盤抽籤、收藏夾、主動推播)的全面升級!
但隨著系統開放給更多人使用,外部的惡意攻擊與流量爆發隨之而來。
明天 Day 21,我們將正式進入企業級資安與穩定性防護模組:在 FastAPI 中加入 Rate Limiting(慢速限流防護),防止惡意使用者高頻率刷流量燒乾 Google 與 LLM API 額度! 敬請期待!