FastComments.com


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

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


ウェブフックとは Internal Link


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


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

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

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

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

テスト Internal Link

Webhooks 管理画面には各イベントタイプ(作成、更新、削除)に対して Send Test Payload ボタンがあります。作成イベントと更新イベントはダミーの WebhookComment オブジェクトを送信しますが、削除イベントをテストする場合は ID のみを含むダミーのリクエストボディが送信されます。

ペイロードの検証

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

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

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

テスト用ツール

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

イベントタイプ

  • 作成イベント: 新しいコメントが作成されたときにトリガーされます。デフォルトのメソッド: PUT
  • 更新イベント: コメントが編集されたときにトリガーされます。デフォルトのメソッド: PUT
  • 削除イベント: コメントが削除されたときにトリガーされます。デフォルトのメソッド: DELETE

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


データ構造 Internal Link

唯一ウェブフック経由で送信される構造は、以下の 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 /** 「最も関連性の高い」ソート方向でのコメントのページ番号。 **/
44 pageNumber: number
45 /** 「古い順」ソート方向でのコメントのページ番号。 **/
46 pageNumberOF: number
47 /** 「新しい順」ソート方向でのコメントのページ番号。 **/
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 メソッド

管理パネルで各ウェブフックイベントタイプの HTTP メソッドを設定できます:

  • Create Event: POST または PUT (デフォルト: PUT)
  • Update Event: POST または PUT (デフォルト: PUT)
  • Delete Event: DELETE、POST、または PUT (デフォルト: DELETE)

すべてのリクエストが ID を含むため、Create および Update 操作はデフォルトで冪等(PUT)です。同じ Create または Update リクエストを繰り返しても、あなたの側でオブジェクトが重複して作成されることはないはずです。

リクエストヘッダー

各ウェブフックリクエストには次のヘッダーが含まれます:

ヘッダー 説明
Content-Type application/json
token あなたの API シークレット
X-FastComments-Timestamp リクエストが署名された Unix タイムスタンプ(秒)
X-FastComments-Signature HMAC-SHA256 署名(sha256=<hex>

詳細な HMAC 署名の検証方法は セキュリティと API トークン を参照してください。

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


FastComments webhook requests include multiple authentication mechanisms for security.

Headers Sent

Header Description
token Your API Secret (for backwards compatibility)
X-FastComments-Timestamp Unix timestamp (seconds) when the request was signed
X-FastComments-Signature HMAC-SHA256 signature of the payload

We strongly recommend verifying the HMAC signature to ensure webhook payloads are authentic and haven't been tampered with.

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

How the signature is computed:

  1. Concatenate: timestamp + "." + JSON_payload_body
  2. Compute HMAC-SHA256 using your API Secret as the key
  3. Hex-encode the result

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

The token header containing your API Secret is still sent for backwards compatibility. However, we recommend migrating to HMAC verification for improved security as it protects against replay attacks.



結論

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

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

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