FastComments.com


使用 FastComments,可以在評論被新增、更新或從我們的系統中移除時呼叫一個 API 端點。

我們透過 HTTP/HTTPS 的非同步 webhooks 來達成這個目的。


什麼是 Webhooks(網路回呼) Internal Link

Webhook 是一種機制,或是兩個系統之間的整合,其中的 "producer" (FastComments) 觸發一個事件 而 "consumer" (您) 則透過 API 呼叫來接收並處理該事件。

支援的事件與資源 Internal Link


FastComments 僅支援 Comment 資源的 webhooks。

我們支援針對留言的建立、移除與更新的 webhooks。

這些在我們系統中皆被視為獨立事件,因此在 webhook 事件的語義與結構上各有不同。


測試 Internal Link

在 Webhooks 管理介面中,每種事件類型(Create、Update、Delete)都有 Send Test Payload 按鈕。Create 和 Update 事件會發送一個測試用的 WebhookComment 物件,而測試 Delete 時只會發送僅含 ID 的測試請求主體。

驗證有效負載

在測試您的 webhook 整合時,請確認傳入請求包含以下標頭:

  1. token - 您的 API Secret
  2. X-FastComments-Timestamp - Unix 時間戳(秒)
  3. X-FastComments-Signature - HMAC-SHA256 簽名

使用 HMAC 簽名驗證來確保有效負載的真實性。

測試工具

在開發期間,您可以使用 webhook.sitengrok 等工具來檢查傳入的 webhook 有效負載。

事件類型

  • Create Event: 當建立新評論時觸發。預設方法:PUT
  • Update Event: 當評論被編輯時觸發。預設方法:PUT
  • Delete Event: 當評論被刪除時觸發。預設方法:DELETE

每個事件在請求主體中包含完整的評論資料(有關有效負載格式,請參見 資料結構)。

資料結構 Internal Link

透過 webhook 傳送的唯一結構是 WebhookComment 物件,以下以 TypeScript 說明。

WebhookComment 物件結構

「建立」事件結構

「create」事件的請求主體是一個 WebhookComment 物件。

「更新」事件結構

「update」事件的請求主體是一個 WebhookComment 物件。

「刪除」事件結構

「delete」事件的請求主體是一個 WebhookComment 物件。

Change as of Nov 14th 2023
Previously the "delete" event request body only contained the comment id. It now contains the full comment at the time of deletion.
WebhookComment 物件
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** 留言的 id。 **/
4 id: string
5 /** 識別留言串的 id 或 URL。已標準化。 **/
6 urlId: string
7 /** 指向留言位置的 URL。 **/
8 url?: string
9 /** 發表留言的使用者 id。若為 SSO,會加上租戶 id 前綴。 **/
10 userId?: string
11 /** 發表留言的使用者電子郵件。 **/
12 commenterEmail?: string
13 /** 在留言元件顯示的使用者名稱。SSO 情況下可能為 displayName。 **/
14 commenterName: string
15 /** 原始留言文字。 **/
16 comment: string
17 /** 經解析後的留言文字。 **/
18 commentHTML: string
19 /** 留言的外部 id。 **/
20 externalId?: string
21 /** 父留言的 id。 **/
22 parentId?: string | null
23 /** 留言發表的 UTC 日期。 **/
24 date: UTC_ISO_DateString
25 /** 投票的綜合評分 (贊成 - 反對)。 **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** 若使用者留言時已登入、或其評論已被驗證、或在留言時已驗證其會話,則為 true。 **/
30 verified: boolean
31 /** 留言被驗證的日期。 **/
32 verifiedDate?: number
33 /** 是否由管理員標記為已審閱。 **/
34 reviewed: boolean
35 /** 大頭照的位置或 base64 編碼。僅在透過 SSO 傳遞 base64 時才會是 base64。 **/
36 avatarSrc?: string
37 /** 留言是否被手動或自動標記為垃圾訊息? **/
38 isSpam: boolean
39 /** 留言是否被自動判定為垃圾訊息? **/
40 aiDeterminedSpam: boolean
41 /** 留言中是否包含圖片? **/
42 hasImages: boolean
43 /** 在「最相關」排序方向下,留言所在的頁碼。 **/
44 pageNumber: number
45 /** 在「最舊優先」排序方向下,留言所在的頁碼。 **/
46 pageNumberOF: number
47 /** 在「最新優先」排序方向下,留言所在的頁碼。 **/
48 pageNumberNF: number
49 /** 留言是否被自動或手動核准? **/
50 approved: boolean
51 /** 使用者撰寫留言時的地區代碼 (格式: en_us)。 **/
52 locale: string
53 /** 留言中成功解析出的 @提及。 **/
54 mentions?: CommentUserMention[]
55 /** 留言所屬的網域。 **/
56 domain?: string
57 /** 與此留言相關的可選審核群組 id 列表。 **/
58 moderationGroupIds?: string[]|null
59}
60

When users are tagged in a comment, the information is stored in a list called mentions. Each object in that list has the following structure.

Webhook 提及物件
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** 使用者 id。對於 SSO 使用者,會加上租戶 id 前綴。 **/
4 id: string
5 /** 最終的 @提及 標籤文字,包含 @ 符號。 **/
6 tag: string
7 /** 原始的 @提及 標籤文字,包含 @ 符號。 **/
8 rawTag: string
9 /** 被標註的使用者類型。user = FastComments.com 帳號。sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** 即使使用者選擇不接收通知,此欄位仍會設為 true。 **/
12 sent: boolean
13}
14

HTTP 方法

您可以在管理面板中為每種 webhook 事件類型設定 HTTP 方法:

  • Create Event: POST or PUT (default: PUT)
  • Update Event: POST or PUT (default: PUT)
  • Delete Event: DELETE, POST, or PUT (default: DELETE)

由於所有請求都包含 ID,Create 與 Update 操作預設為冪等 (PUT)。重複相同的 Create 或 Update 請求不應該在您的端造成重複的物件。

請求標頭

每個 webhook 請求會包含以下標頭:

Header Description
Content-Type application/json
token Your API Secret
X-FastComments-Timestamp Unix timestamp (seconds) when the request was signed
X-FastComments-Signature HMAC-SHA256 signature (sha256=<hex>)

See 安全性與 API 令牌 for information on verifying the HMAC signature.

安全性與 API 令牌 Internal Link

FastComments 的 webhook 請求包含多種驗證機制以確保安全性。

Headers Sent

Header Description
token 您的 API Secret(為了向後相容)
X-FastComments-Timestamp Unix 時間戳(秒),表示請求簽名的時間
X-FastComments-Signature 載荷的 HMAC-SHA256 簽名

我們強烈建議驗證 HMAC 簽名,以確保 webhook 載荷為真實且未被竄改。

Signature Format: sha256=<hex-encoded-signature>

How the signature is computed:

  1. 串接: timestamp + "." + JSON_payload_body
  2. 使用您的 API Secret 作為金鑰計算 HMAC-SHA256
  3. 將結果進行十六進位編碼

Example Verification (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(req, apiSecret) {
    const timestamp = req.headers['x-fastcomments-timestamp'];
    const signature = req.headers['x-fastcomments-signature'];

    if (!timestamp || !signature) {
        return false;
    }

    // 驗證時間戳記是否為近期(5 分鐘內)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // 防止重放攻擊
    }

    // 驗證簽名
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

    return signature === `sha256=${expectedSignature}`;
}

Example Verification (Python)

import hmac
import hashlib
import time
import json

def verify_webhook_signature(headers, body, api_secret):
    timestamp = headers.get('X-FastComments-Timestamp')
    signature = headers.get('X-FastComments-Signature')

    if not timestamp or not signature:
        return False

    # 驗證時間戳記是否為近期
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # 驗證簽名
    payload = json.dumps(body, separators=(',', ':'))
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        api_secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return signature == f"sha256={expected}"

Example Verification (PHP)

function verifyWebhookSignature($headers, $body, $apiSecret) {
    $timestamp = $headers['X-FastComments-Timestamp'] ?? null;
    $signature = $headers['X-FastComments-Signature'] ?? null;

    if (!$timestamp || !$signature) {
        return false;
    }

    // 驗證時間戳記是否為近期(5 分鐘內)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // 驗證簽名
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Legacy Authentication

包含您 API Secret 的 token 標頭仍然會為了向後相容而發送。然而,我們建議遷移到 HMAC 驗證以提升安全性,因為它可以防止重放攻擊。


結論

以上即為我們的 Webhooks 文件。

我們希望您覺得 FastComments 的 Webhook 整合容易理解且快速設定。

如果您發現我們的文件有任何遺漏,請在下方讓我們知道。