FastComments.com

ウェブフック


FastComments では、コメントがシステムに追加、更新、または削除されるたびに API エンドポイントを呼び出すことができます。

これは HTTP/HTTPS を介した非同期 Webhook によって実現します。


ウェブフックとは Internal Link


Webhookは、2つのシステム間の仕組み、または統合であり、"producer" (FastComments) がイベントを発生させ "consumer" (あなた) がAPIコールでそれを受け取るものです。


対応イベントとリソース Internal Link

FastComments は Comment リソースに対してのみウェブフックをサポートしています。

コメントの作成、削除、更新時のウェブフックをサポートしています。

これらはそれぞれシステム内で別個のイベントと見なされるため、ウェブフックイベントのセマンティクスと構造が異なります。

テスト Internal Link

In the Webhooks admin there are Send Test Payload buttons for each event type (Create, Update, Delete). The Create and Update events send a dummy WebhookComment object, while testing Delete will send a dummy request body with just an ID.

ペイロードの検証

Webhook 統合をテストする際、受信リクエストに次のヘッダーが含まれていることを確認してください:

  1. token - あなたの API シークレット
  2. X-FastComments-Timestamp - Unix タイムスタンプ(秒)
  3. X-FastComments-Signature - HMAC-SHA256 署名

HMAC 署名の検証を使用して、ペイロードが正当であることを確認してください。

テストツール

開発中に受信する webhook ペイロードを検査するために、webhook.sitengrok などのツールを使用できます。

Event Types

  • Create Event: Triggered when a new comment is created. Default method: PUT
  • Update Event: Triggered when a comment is edited. Default method: PUT
  • Delete Event: Triggered when a comment is deleted. Default method: DELETE

各イベントはリクエストボディに完全なコメントデータを含みます(ペイロード形式については データ構造 を参照してください)。


データ構造 Internal Link

Webhookを介して送信される構造は、下記にTypeScriptで示した WebhookComment オブジェクトのみです。

WebhookComment オブジェクトの構造

"create" イベントの構造

"create" イベントのリクエストボディは WebhookComment オブジェクトです。

"update" イベントの構造

"update" イベントのリクエストボディは WebhookComment オブジェクトです。

"delete" イベントの構造

"delete" イベントのリクエストボディは WebhookComment オブジェクトです。

20231114日の変更
以前は "delete" イベントのリクエストボディはコメントIDのみを含んでいました。新しくなり、削除時の完全なコメントを含むようになりました。
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 /** 「Most Relevant」ソート順でのコメントのページ番号。 **/
44 pageNumber: number
45 /** 「Oldest First」ソート順でのコメントのページ番号。 **/
46 pageNumberOF: number
47 /** 「Newest First」ソート順でのコメントのページ番号。 **/
48 pageNumberNF: number
49 /** コメントが自動的にまたは手動で承認されたかどうか。 **/
50 approved: boolean
51 /** コメントが書かれたときのユーザーのロケールコード(形式: en_us)。 **/
52 locale: string
53 /** コメント内で書かれ、正常にパースされた@mentions。 **/
54 mentions?: CommentUserMention[]
55 /** コメントが投稿されたドメイン。 **/
56 domain?: string
57 /** このコメントに関連付けられたモデレーショングループIDの任意のリスト。 **/
58 moderationGroupIds?: string[]|null
59}
60

ユーザーがコメント内でタグ付けされると、その情報は mentions というリストに保存されます。そのリスト内の各オブジェクトは次の構造を持ちます。

Webhook メンションオブジェクト
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** ユーザーID。SSOユーザーの場合、テナントIDがプレフィックスされる。 **/
4 id: string
5 /** 最終的な@mentionタグのテキスト(@記号を含む)。 **/
6 tag: string
7 /** 元の@mentionタグのテキスト(@記号を含む)。 **/
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リクエストには以下のヘッダーが含まれます:

HeaderDescription
Content-Typeapplication/json
tokenあなたのAPIシークレット
X-FastComments-Timestampリクエストが署名された時刻のUnixタイムスタンプ(秒)
X-FastComments-SignatureHMAC-SHA256署名 (sha256=<hex>)

HMAC署名の検証に関する情報は セキュリティとAPIトークン を参照してください。

セキュリティとAPIトークン Internal Link

FastComments の webhook リクエストは、セキュリティのために複数の認証メカニズムを含みます。

送信されるヘッダー

ヘッダー説明
tokenAPI シークレット (下位互換性のため)
X-FastComments-Timestampリクエストが署名されたときの Unix タイムスタンプ(秒)
X-FastComments-Signatureペイロードの HMAC-SHA256 署名

HMAC 署名検証(推奨)

Webhook のペイロードが正当で改ざんされていないことを保証するために、HMAC 署名の検証を強く推奨します。

署名形式: sha256=<hex-encoded-signature>

署名の計算方法:

  1. 連結: timestamp + "." + JSON_payload_body
  2. API シークレットをキーとして HMAC-SHA256 を計算
  3. 結果を十六進エンコード

検証例 (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}`;
}

検証例 (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}"

検証例 (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);
}

レガシー認証

token ヘッダーは下位互換性のために引き続き送信されますが、リプレイ攻撃から保護するために、セキュリティ向上の観点から HMAC 検証への移行を推奨します。


結論

これで Webhooks ドキュメントは終了です。

FastComments の Webhook 統合がわかりやすく、迅速に設定できることを願っています。

ドキュメントに不備があると感じた場合は、下記からお知らせください。