← 所有文章
教學

將 Webhook 文件丟畀 Devin Desktop,20 分鐘搞掂 LINE 訊息接收服務

Devin Desktop——2026 年初由 Windsurf 更名而嚟,搭載嘅 SWE-1.6 模型輸出速度去到 950 tokens/s——係目前我身邊開發者討論得最多嘅 AI 編碼工具。用法好直接:將參考文件丟畀佢,描述你想要乜嘢,迭代到跑得起為止。我想試吓呢套流程用喺 webhook API 上面效果點。

任務:寫一個 Python 服務,接收 LINE 訊息嘅 webhook 事件,結構化日誌輸出——嗰種細團隊會部署喺訊息佇列或工單系統前面嘅 handler。唔使 LINE 官方帳號、唔使 Messaging API 憑證、唔使管理 channel access token。只要入站訊息以標準化 JSON 嘅形式到達就得。

最終成品

一個 FastAPI 服務(大約 40 行代碼):

  1. 接收 UnifyPort 統一 webhook 嘅 message.received 事件
  2. signing_secret 做 HMAC-SHA256 簽章驗證
  3. 將每條訊息以結構化 JSON 記錄日誌——provider、sender、text、timestamp
  4. 所有事件回傳 200,簽章無效回傳 401

耗時:約 20 分鐘。你需要一個 UnifyPort 工作區並連接 LINE 帳號(LINE App 掃碼就得),以及 Python 3.10+。

點解唔直接用 LINE Messaging API?

官方路徑:

  1. 建立 LINE 官方帳號——需要 LINE Business ID,需要企業或實名個人認證
  2. 喺 LINE Developers 控制台啟用 Messaging API——設定 channel、生成 channel access token、設定 webhook URL
  3. 解析 LINE 嘅 webhook payload:事件結構係 {"events": [{"type": "message", "message": {"type": "text", "text": "..."}, "source": {"userId": "U..."}}]}——巢狀嘅、LINE 專有格式
  4. 驗證簽章:用 channel secret 校驗 x-line-signature——LINE 用嘅係 Base64 編碼嘅 HMAC-SHA256,唔係 hex
  5. 處理 reply token:每個事件帶一個有效期 30 秒嘅 replyToken,遲咗回覆就靜默失敗

代碼跑得起,但淨係跑到 LINE。webhook payload 格式、簽章方式、reply token 機制——冇一樣可以複用去 WhatsApp 或 Telegram。加第二個平台就要由頭開始做第二套整合。

UnifyPort 嘅非官方介面連接個人 LINE 帳號——掃碼連接,唔使官方帳號——將每條入站訊息標準化為 message.received 事件,同 WhatsApp、Telegram、TikTok、Zalo、X 嘅格式一模一樣。一個 handler 覆蓋六個平台。

準備:將 API 文件餵畀 Devin Desktop

開啟 Devin Desktop,建立新工作階段。第一次提問之前,畀佢上下文:

  1. 貼上 UnifyPort webhook 文件入面嘅 message.received 事件 payload:
    {
      "event": "message.received",
      "account_id": "acct_7kQnWx",
      "provider": "line",
      "from": "user_a3f82c",
      "text": "可以改到星期五嗎?",
      "timestamp": 1751270400,
      "message_id": "line_msg_5e9d21"
    }
  2. 貼上 HMAC-SHA256 簽章驗證部分——x-unifyport-signature header、用 signing_secret 對原始請求主體計算 hex digest

Devin 會將貼上嘅內容作為工作階段上下文索引。SWE-1.6 Fast 以 950 tok/s 處理,文件消化只需幾秒。Cursor、Claude Code、Copilot 都一樣——貼上或附加就得。工具可以換,文件先係重點。

建構過程

第一個 prompt——handler 骨架:

根據我啱啱貼上嘅 UnifyPort webhook 文件,寫一個 FastAPI 服務,POST /webhook 路由。當事件係 “message.received” 時,用 Python 嘅 logging 模組以結構化 JSON 記錄 provider、from、text 同 timestamp。所有事件回傳 200。用 uvicorn 啟動。

Devin 讀取上下文並生成:

from fastapi import FastAPI, Request
import logging, json

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("inbound")

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    evt = await request.json()
    if evt.get("event") == "message.received":
        logger.info(json.dumps({
            "provider": evt["provider"],
            "from": evt["from"],
            "text": evt["text"],
            "timestamp": evt["timestamp"],
            "message_id": evt["message_id"],
        }))
    return {"status": "ok"}

十二行 handler。冇 LINE 專用 import,冇 linebot SDK,冇 channel access token。事件結構夠扁平,唔使額外解析。

第二個 prompt——簽章驗證:

加上 HMAC-SHA256 簽章驗證。header 係 x-unifyport-signature,secret 從環境變數 UNIFYPORT_SIGNING_SECRET 讀取,HMAC 對原始請求主體 bytes 計算——唔係重新序列化嘅 JSON。驗證失敗回傳 401。用 timing-safe 比較。

第三個 prompt——合併整理:

將簽章驗證合併入 webhook handler。驗證通過之後先將 body 解析為 JSON。加一個 GET /health 健康檢查。加上 uvicorn.run,port 8000。

完整嘅 server.py

from fastapi import FastAPI, Request, HTTPException
import hmac, hashlib, os, json, logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("inbound")

SIGNING_SECRET = os.environ["UNIFYPORT_SIGNING_SECRET"]

app = FastAPI()

@app.get("/health")
async def health():
    return {"status": "ok"}

@app.post("/webhook")
async def webhook(request: Request):
    body = await request.body()
    sig = request.headers.get("x-unifyport-signature", "")
    expected = hmac.new(
        SIGNING_SECRET.encode(), body, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(sig, expected):
        raise HTTPException(status_code=401, detail="invalid signature")

    evt = json.loads(body)
    if evt.get("event") == "message.received":
        logger.info(json.dumps({
            "provider": evt["provider"],
            "from": evt["from"],
            "text": evt["text"],
            "timestamp": evt["timestamp"],
            "message_id": evt["message_id"],
        }))
    return {"status": "ok"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

唔到 40 行。簽章驗證、結構化日誌、可以直接部署喺任何反向代理後面。

跑起嚟,睇訊息入嚟

喺 UnifyPort 工作區連接 LINE 帳號——LINE 支援掃碼認證,開啟 LINE App 掃一下就連上。唔使 LINE 官方帳號、唔使 Business ID、唔使設定 Messaging API。

註冊一個 webhook 端點指向你嘅伺服器,設定 subscribed_events: ["message.received"]signing_secret。啟動服務:

UNIFYPORT_SIGNING_SECRET=your_secret python server.py

叫朋友傳一條測試訊息到你嘅 LINE。事件到達:

{
  "event": "message.received",
  "account_id": "acct_3Xk1wL",
  "provider": "line",
  "from": "user_a3f82c",
  "text": "可以改到星期五嗎?",
  "timestamp": 1751270400,
  "message_id": "line_msg_5e9d21"
}

伺服器驗證簽章,輸出結構化日誌。如果簽章驗證失敗回傳 401——檢查 UNIFYPORT_SIGNING_SECRET 係咪同 webhook 端點設定嘅一致。

加 WhatsApp,一行代碼都唔使改

喺同一個工作區連接 WhatsApp 帳號,訂閱同一個 webhook 端點。WhatsApp 訊息到達時,格式完全一樣:

{
  "event": "message.received",
  "account_id": "acct_7kQnWx",
  "provider": "whatsapp",
  "from": "user_d4f29a",
  "text": "訂單確認,星期一出貨",
  "timestamp": 1751270460,
  "message_id": "wa_msg_8b3e71"
}

同一個 handler,同樣嘅結構化日誌。provider 欄位由 "line" 變成 "whatsapp",代碼路徑完全一致。LINE Messaging API 嘅 handler 要加 WhatsApp 就要由頭開始做一套完全唔同嘅整合——唔同嘅 webhook payload、唔同嘅簽章格式、唔同嘅 SDK。呢個 handler 覆蓋六個平台,因為事件結構唔變。

呢個就係成套流程:將 UnifyPort API 文件貼畀 Devin Desktop,描述你要嘅 handler,迭代到跑得起。工具寫代碼,標準化 webhook 令代碼適用於所有平台。換成 Cursor、Claude Code 或 Copilot 都一樣——文件唔變,handler 唔超過 40 行。