← 所有文章
教學

用 GitHub Copilot 建一個 TikTok DM Webhook 接收器

做 TikTok 私訊客服,第一步唔應該係叫 AI 估 payload,而係將真實 API 契約放入 GitHub Copilot 上下文。UnifyPort 會將 TikTok 入站訊息整理成統一 message.received 事件,並用 X-Device-Signature 對 raw request body 做 HMAC-SHA256 簽名。

重點

  • TikTok 官方開發者文件入面,Direct Messages 主要出現喺 Data Portability scope 同 data types,唔係通用即時客服 webhook。
  • Copilot 要依據 UnifyPort reference 寫:POST /v1/webhook-endpointssubscribed_eventssigning_secretX-Device-TimestampX-Device-Signature
  • 驗簽一定要用 raw body;先 parse JSON 再 stringify 會改變 bytes。
  • 對香港或東南亞營運團隊,最好先存事件,再將 inbound message 分流去 Slack、CRM 或 AI worker。

如果你仍然研究 TikTok DM API 是否適合,先睇 TikTok DM API:點解冇官方通用端點。如果你已經需要 live intake,可以接住睇 TikTok Data Portability vs live DMs

最終 demo

Demo 係一個 Node.js 服務,只有 /webhook route。它收 UnifyPort event,驗證 X-Device-TimestampX-Device-Signature,解析 message.received,保存必要欄位,然後回 200

開發時請對住 webhook delivery and signature verification 文件。呢頁描述咗簽名字串、headers、retry 同 idempotency,是 Copilot 生成代碼嘅依據。

Prompt 1:先寫 receiver

Build a minimal Express service for a UnifyPort webhook receiver.
Use express.raw({ type: 'application/json' }). Verify X-Device-Signature as hex HMAC-SHA256 over X-Device-Timestamp + '.' + raw request body using WEBHOOK_SIGNING_SECRET.
Only process event.type === 'message.received'. Store provider, account_id, conversation.id, sender.id, message.id, message.text, message.direction, and occurred_at.
Return 200 after storing; return 401 on invalid signature.

核心代碼應該保留 raw body:

import crypto from 'crypto';
import express from 'express';

const app = express();
const secret = process.env.WEBHOOK_SIGNING_SECRET;
const queue = [];

function verifySignature(req) {
  const timestamp = req.get('X-Device-Timestamp') || '';
  const signature = req.get('X-Device-Signature') || '';
  const expected = crypto.createHmac('sha256', secret)
    .update(timestamp + '.')
    .update(req.body)
    .digest('hex');
  return signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();
  const event = JSON.parse(req.body.toString('utf8'));
  if (event.type !== 'message.received') return res.status(200).end();
  queue.push({ provider: event.provider, account_id: event.account_id,
    conversation_id: event.data.conversation.id, sender_id: event.data.sender.id,
    message_id: event.data.message.id, text: event.data.message.text || '',
    direction: event.data.message.direction, occurred_at: event.occurred_at });
  res.status(200).end();
});

註冊 webhook endpoint

部署 HTTPS 後,呼叫 POST /v1/webhook-endpointssubscribed_events 設為 message.receivedsigning_secret 會啟用簽名。

curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
  -H "X-Api-Key: dk_live_example" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://inbox.example.com/webhook",
  "status": "active",
  "subscribed_events": ["message.received"],
  "signing_secret": "whsec_6f5b1c9d4e7a2b8c"
}'

想睇同類 AI coding 流程,可參考 AI coding agent 自動回覆 bot 教程

加上可靠性

第二個 prompt 可要求 Copilot 使用 X-Device-Event-Id 做 retry 去重,保存 raw event,並只將 direction === 'inbound' 嘅訊息送入 triage queue。之後才接 Slack、CRM 或 AI 分類。

限制

如產品需要官方內容發布、登入、研究工具或資料匯出,應使用 TikTok 官方 API。UnifyPort 的非官方接口適合現有帳號嘅即時入站訊息;它提供可驗證事件流,不代表 TikTok 官方 product scope。

FAQ

Copilot 可唔可以做晒整個 inbox?

可幫你生成 receiver、測試同隊列代碼,但驗簽、secret 管理、部署同 storage 行為仍要人手 review。

Data Portability 係咪 live DMs?

唔係。官方文件描述資料導出範圍;客服通常需要即時 event stream。

應該訂閱邊個 event?

只做入站訊息就用 message.received;全量收集先用 ["*"]

Sources checked on 2026-08-29

UnifyPort API

令訊息接入變成一條穩定嘅產品管線。

先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。