← 所有文章
教學

我將 Webhook 文件掉俾 AI 編碼助手,佢直接整咗個跑得起嘅自動回覆機械人 — UnifyPort

你一定熟悉呢套流程。待辦清單上擺住一個訊息整合嘅工作,你冇將文件由頭讀到尾,而係直接打開 Cursor——或者 Claude Code、Windsurf——開始打提示詞。2026 年,大多數人就係咁樣起一個專案。真正嘅問題唔係你識唔識用 AI「氛圍編程」(vibe-coding),而係你對接嗰個 API,有冇俾足夠嘅嘢個助手去將件事做得乾淨

呢份係開發實錄。睇完你會得到一個跑得起嘅自動回覆機械人:一個 webhook 接收端,對每一條入站訊息驗證簽章,再透過單一嘅傳送端點回覆——大約一個下晝嘅工作量,而且大部分係助手做嘅。佢之所以快,係因為 UnifyPort 嘅 webhook 係正規化嘅:得一種事件結構要學,得一個端點要回呼,助手冇乜好估。

準備:先將文件餵俾助手

最關鍵嗰一步,偏偏係大家最鍾意跳過嘅——喺你開口要程式碼之前,先將 API 文件塞入助手嘅脈絡度。今日有三種行得通嘅做法:

  • 直接貼上文件頁。message.received 嘅事件結構同 POST /v1/messages 傳送嗰段原樣貼入對話框。
  • 掛載文件。 Cursor 嘅 @Docs、Claude Code 嘅檔案脈絡、Windsurf 嘅文件面板——指向 UnifyPort 嘅介面文件,等助手按需讀取。
  • 用文件/MCP 連接器(如果你嘅軟件支援),等助手自己去攞文件。

回報好實在。一個真係讀過事件名嘅助手,唔會憑空整個 onMessage() 處理函式,或者一個根本唔存在嘅 sendText() SDK。佢會照住 message.receivedPOST /v1/messages 嚟寫,因為呢樣就係擺喺佢面前嘅嘢。餵入去嘅係垃圾脈絡,嘔出嚟嘅就係幻覺端點——所以花嗰兩分鐘,將真嘢放入去。

逐條提示詞,將機械人整起嚟

第一條提示詞——接收端:

用我俾你嘅 UnifyPort webhook 文件,寫一個 Express 伺服器,帶一個 POST /webhook 路由接收標準事件。先做到:當 eventmessage.received 嗰陣,印出 fromtext

因為事件結構係固定嘅,處理邏輯就係對一個欄位做分支判斷:

import express from "express";

const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
  const evt = req.body;
  if (evt.event === "message.received") {
    console.log(`${evt.provider} ${evt.from}: ${evt.text}`);
  }
  res.sendStatus(200);
});

app.listen(3000, () => console.log("listening on :3000"));

第二條提示詞——驗證簽章。 呢一步,求其嘅實作會跳過,而讀過文件嘅助手唔會:

每次投遞都用 webhook 嘅 signing_secret 做咗 HMAC-SHA256 簽章。喺信任請求主體之前先驗證。要用原始請求主體,並做時間恆定嘅比較。

呢度助手必須做啱嗰一點:HMAC 要計喺原始位元組上,而唔係重新序列化之後嘅 JSON。只要提示寫咗「用原始請求主體」,好嘅助手會接上 express.jsonverify 鉤子嚟捕捉 rawBody。如果佢漏咗,呢個就係你唯一要盯嘅複查點——而且第一次真實投遞回 401 嗰陣你就會發現。

第三條提示詞——透過傳送端點回覆:

message.received 到達嗰陣,用事件入面嘅 account_idfrom,透過 POST /v1/messages 回覆傳送者。驗證用環境變數入面嘅 Bearer API key。

機械人就齊喇。助手最終落定嘅完整檔案:

import express from "express";
import crypto from "crypto";

const { UNIFYPORT_API_KEY, UNIFYPORT_SIGNING_SECRET } = process.env;

const app = express();
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));

function isValid(req) {
  const sig = req.get("x-unifyport-signature") || "";
  const expected = crypto
    .createHmac("sha256", UNIFYPORT_SIGNING_SECRET)
    .update(req.rawBody)
    .digest("hex");
  return sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

app.post("/webhook", async (req, res) => {
  if (!isValid(req)) return res.sendStatus(401);

  const evt = req.body;
  if (evt.event === "message.received") {
    await fetch("https://api.unifyport.ai/v1/messages", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${UNIFYPORT_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        account_id: evt.account_id,
        to: evt.from,
        text: "收到喇!你嘅訊息我哋已經收到,稍後會有專人跟進。",
      }),
    });
  }
  res.sendStatus(200);
});

app.listen(3000, () => console.log("listening on :3000"));

跑起嚟,睇一條訊息入嚟

連一個帳號(Telegram 最快上手),註冊一個指向你伺服器嘅 webhook 端點,帶埋 signing_secretsubscribed_events: ["message.received"],然後俾你嘅機械人傳一條私訊。事件到達嗰陣係咁樣:

{
  "event": "message.received",
  "account_id": "acct_8Q2vK",
  "provider": "telegram",
  "from": "user_3f9c1a",
  "text": "請問週末有冇營業?",
  "timestamp": 1749427200,
  "message_id": "tg_msg_5d2b7e"
}

你嘅伺服器驗證簽章,呼叫 POST /v1/messages,回覆就出現喺對話入面。如果簽章驗證失敗,你會攞到 401——嗰個就係上面講嘅 rawBody 坑。修一次,呢個迴圈就穩。

再擴充

正規化 webhook 嘅價值就係喺度兌現。加第二個平台,唔係再做一次整合——而係新增處理程式碼。連一個 WhatsApp、LINE 或者 Zalo 帳號,訂閱同一個 webhook,完全一樣嘅 message.received 分支照樣跑,因為事件結構喺唔同平台之間唔會變。provider 欄位話你知訊息邊度嚟,而你程式碼嘅其餘部分根本唔使睇。

想機械人真係去答問題,而唔淨係回一句「收到」?再加一條提示詞就得:「回覆之前,將 text 傳俾一個大型語言模型,用佢嘅回覆做回覆內容。」 傳送呼叫助手已經寫好,你只係將一個字串換成模型嘅輸出。

呢個下晝嘅啟示,唔係「AI 幫你將機械人寫埋」。而係:AI 助手嘅上限,取決於你俾佢對接嗰個介面。一個事件名穩定、得一個傳送端點嘅正規化 webhook,正正係助手可以乾淨收尾嗰種介面——一種結構要學,冇嘢要亂作。將你嘅軟件指向 UnifyPort v1 API,貼入 message.received 嘅結構,你會喺呢個下晝完之前攞到一個識驗證、識回覆嘅機械人——然後將佢放去其餘五個渠道上面。