Google Chat アプリを Webhook として作成する

このページでは、外部トリガーを使用して非同期メッセージを Chat スペースに送信するように Webhook を設定する方法について説明します。たとえば、サーバーがダウンしたときに Chat で待機中の担当者に通知するようにモニタリング アプリケーションを構成できます。Chat 用アプリで同期メッセージを送信するには、メッセージを送信するをご覧ください。

このタイプのアーキテクチャ設計では、通信は一方向であるため、ユーザーは webhook や接続された外部アプリケーションとやり取りできません。Webhook は会話型ではありません。ユーザーや Chat 用アプリのインタラクション イベントからのメッセージに応答したり、メッセージを受信したりすることはできません。メッセージに返信するには、Webhook ではなく Chat 用アプリを構築します。

Webhook は厳密には Chat 用アプリではありませんが(標準の HTTP リクエストを使用してアプリを接続します)、このページではわかりやすくするために Chat 用アプリと呼んでいます。各 Webhook は、登録されている Chat スペースでのみ機能します。受信ウェブフックはダイレクト メッセージで機能しますが、すべてのユーザーで Chat 用アプリが有効になっている場合に限ります。Webhook を Google Workspace Marketplace に公開することはできません。

次の図は、Chat に接続された Webhook のアーキテクチャを示しています。

非同期メッセージを Chat に送信する着信 Webhook のアーキテクチャ。

上の図では、Chat 用アプリの情報フローは次のようになっています。

  1. Chat 用アプリのロジックは、プロジェクト管理システムやチケット発行ツールなどの外部のサードパーティ サービスから情報を受け取ります。
  2. Chat 用アプリのロジックは、Webhook URL を使用して特定の Chat スペースにメッセージを送信できるクラウド システムまたはオンプレミス システムでホストされます。
  3. ユーザーは特定の Chat スペースで Chat 用アプリからメッセージを受信できますが、Chat 用アプリとやり取りすることはできません。

前提条件

Python

  • Google Chat へのアクセス権を持つ Business または Enterprise の Google Workspace アカウント。Google Workspace 組織で、ユーザーが着信 Webhook を追加して使用できるようにする必要があります。
  • Python 3.6 以降
  • pip パッケージ管理ツール
  • httplib2 ライブラリ。ライブラリをインストールするには、コマンドライン インターフェースで次のコマンドを実行します。

    pip install httplib2
  • Google Chat のスペース。Google Chat API を使用して作成するには、スペースを作成するをご覧ください。Chat で作成するには、ヘルプセンターのドキュメントをご覧ください。

Node.js

Java

Apps Script

Webhook を作成する

Webhook を作成するには、メッセージを受信する Chat スペースに登録してから、メッセージを送信するスクリプトを作成します。

着信 Webhook を登録する

  1. ブラウザで Chat を開きます。Chat モバイルアプリから Webhook を構成することはできません。
  2. Webhook を追加するスペースに移動します。
  3. スペースのタイトルの横にある 展開矢印をクリックし、[アプリとインテグレーション] をクリックします。
  4. [Webhook を追加] をクリックします。

  5. [名前] フィールドに「Quickstart Webhook」と入力します。

  6. [アバターの URL] フィールドに「https://developers.google.com/chat/images/chat-product-icon.png」と入力します。

  7. [保存] をクリックします。

  8. Webhook URL をコピーするには、 [その他] をクリックし、 [リンクをコピー] をクリックします。

Webhook スクリプトを作成する

Webhook のサンプル スクリプトは、Webhook URL に POST リクエストを送信することで、Webhook が登録されているスペースにメッセージを送信します。Chat API は Message のインスタンスで応答します。

Webhook スクリプトの作成方法については、以下の言語を選択してください。

Python

  1. 作業ディレクトリに quickstart.py という名前のファイルを作成します。

  2. quickstart.py に、次のコードを貼り付けます。

    python/webhook/quickstart.py
    from json import dumps
    from httplib2 import Http
    
    # Copy the webhook URL from the Chat space where the webhook is registered.
    # The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included
    # when you copy the webhook URL.
    
    def main():
        """Google Chat incoming webhook quickstart."""
        url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN"
        app_message = {"text": "Hello from a Python script!"}
        message_headers = {"Content-Type": "application/json; charset=UTF-8"}
        http_obj = Http()
        response = http_obj.request(
            uri=url,
            method="POST",
            headers=message_headers,
            body=dumps(app_message),
        )
        print(response)
    
    
    if __name__ == "__main__":
        main()
  3. url 変数の値を、Webhook の登録時にコピーした Webhook URL に置き換えます。

Node.js

  1. 作業ディレクトリに index.js という名前のファイルを作成します。

  2. index.js に、次のコードを貼り付けます。

    node/webhook/index.js
    /**
     * Sends asynchronous message to Google Chat
     * @return {Object} response
     */
    async function webhook() {
      const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages"
      const res = await fetch(url, {
        method: "POST",
        headers: {"Content-Type": "application/json; charset=UTF-8"},
        body: JSON.stringify({text: "Hello from a Node script!"})
      });
      return await res.json();
    }
    
    webhook().then(res => console.log(res));
  3. url 変数の値を、Webhook の登録時にコピーした Webhook URL に置き換えます。

Java

  1. 作業ディレクトリに pom.xml という名前のファイルを作成します。

  2. pom.xml で、次の内容をコピーして貼り付けます。

    java/webhook/pom.xml
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.google.chat.webhook</groupId>
      <artifactId>java-webhook-app</artifactId>
      <version>0.1.0</version>
    
      <name>java-webhook-app</name>
      <url>https://github.com/googleworkspace/google-chat-samples/tree/main/java/webhook</url>
    
      <properties>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.compiler.source>11</maven.compiler.source>
      </properties>
    
      <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.9.1</version>
        </dependency>
      </dependencies>
    
      <build>
        <pluginManagement>
          <plugins>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
  3. 作業ディレクトリに、次のディレクトリ構造 src/main/java を作成します。

  4. src/main/java ディレクトリに App.java という名前のファイルを作成します。

  5. App.java に、次のコードを貼り付けます。

    java/webhook/src/main/java/com/google/chat/webhook/App.java
    import com.google.gson.Gson;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.util.Map;
    import java.net.URI;
    
    public class App {
      private static final String URL = "https://chat.googleapis.com/v1/spaces/AAAAGCYeSRY/messages";
      private static final Gson gson = new Gson();
      private static final HttpClient client = HttpClient.newHttpClient();
    
      public static void main(String[] args) throws Exception {
        String message = gson.toJson(Map.of("text", "Hello from Java!"));
    
        HttpRequest request = HttpRequest.newBuilder(URI.create(URL))
          .header("accept", "application/json; charset=UTF-8")
          .POST(HttpRequest.BodyPublishers.ofString(message)).build();
    
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    
        System.out.println(response.body());
      }
    }
  6. URL 変数の値を、Webhook の登録時にコピーした Webhook URL に置き換えます。

Apps Script

  1. ブラウザで Apps Script にアクセスします。

  2. [新しいプロジェクト] をクリックします。

  3. 次のコードを貼り付けます。

    apps-script/webhook/webhook.gs
    function webhook() {
      const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages"
      const options = {
        "method": "post",
        "headers": {"Content-Type": "application/json; charset=UTF-8"},
        "payload": JSON.stringify({"text": "Hello from Apps Script!"})
      };
      const response = UrlFetchApp.fetch(url, options);
      console.log(response);
    }
  4. url 変数の値を、Webhook の登録時にコピーした Webhook URL に置き換えます。

Webhook スクリプトを実行する

CLI で次のスクリプトを実行します。

Python

  python3 quickstart.py

Node.js

  node index.js

Java

  mvn compile exec:java -Dexec.mainClass=App

Apps Script

  • [実行] をクリックします。

コードを実行すると、webhook は登録したスペースにメッセージを送信します。

メッセージ スレッドを開始する、メッセージ スレッドに返信する

  1. メッセージ リクエストの本文の一部として spaces.messages.thread.threadKey を指定します。スレッドを開始するか、スレッドに返信するかに応じて、threadKey に次の値を使用します。

    • スレッドを開始する場合は、threadKey を任意の文字列に設定しますが、スレッドに返信を投稿するためにこの値をメモしておきます。

    • スレッドに返信する場合は、スレッドの開始時に設定された threadKey を指定します。たとえば、最初のメッセージで MY-THREAD が使用されたスレッドに返信を投稿するには、MY-THREAD を設定します。

  2. 指定された threadKey が見つからない場合のスレッドの動作を定義します。

    • スレッドに返信するか、新しいスレッドを開始します。Webhook URL に messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD パラメータを追加します。この URL パラメータを渡すと、Chat は指定された threadKey を使用して既存のスレッドを探します。見つかった場合は、そのスレッドへの返信としてメッセージが投稿されます。見つからない場合、メッセージは threadKey に対応する新しいスレッドを開始します。

    • スレッドに返信するか、何も行いません。Webhook URL に messageReplyOption=REPLY_MESSAGE_OR_FAIL パラメータを追加します。この URL パラメータを渡すと、Chat は指定された threadKey を使用して既存のスレッドを探します。見つかった場合は、そのスレッドへの返信としてメッセージが投稿されます。見つからない場合は、メッセージは送信されません。

    詳しくは、messageReplyOption をご覧ください。

次のコードサンプルは、メッセージ スレッドを開始または返信します。

Python

python/webhook/thread-reply.py
from json import dumps
from httplib2 import Http

# Copy the webhook URL from the Chat space where the webhook is registered.
# The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included
# when you copy the webhook URL.
#
# Then, append messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD to the
# webhook URL.


def main():
    """Google Chat incoming webhook that starts or replies to a message thread."""
    url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"
    app_message = {
        "text": "Hello from a Python script!",
        # To start a thread, set threadKey to an arbitratry string.
        # To reply to a thread, specify that thread's threadKey value.
        "thread": {"threadKey": "THREAD_KEY_VALUE"},
    }
    message_headers = {"Content-Type": "application/json; charset=UTF-8"}
    http_obj = Http()
    response = http_obj.request(
        uri=url,
        method="POST",
        headers=message_headers,
        body=dumps(app_message),
    )
    print(response)


if __name__ == "__main__":
    main()

Node.js

node/webhook/thread-reply.js
/**
 * Sends asynchronous message to Google Chat
 * @return {Object} response
 */
async function webhook() {
  const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"
  const res = await fetch(url, {
    method: "POST",
    headers: {"Content-Type": "application/json; charset=UTF-8"},
    body: JSON.stringify({
      text: "Hello from a Node script!",
      thread: {threadKey: "THREAD_KEY_VALUE"}
    })
  });
  return await res.json();
}

webhook().then(res => console.log(res));

Apps Script

apps-script/webhook/thread-reply.gs
function webhook() {
  const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"
  const options = {
    "method": "post",
    "headers": {"Content-Type": "application/json; charset=UTF-8"},
    "payload": JSON.stringify({
      "text": "Hello from Apps Script!",
      "thread": {"threadKey": "THREAD_KEY_VALUE"}
    })
  };
  const response = UrlFetchApp.fetch(url, options);
  console.log(response);
}

エラーを処理する

Webhook リクエストは、次のようなさまざまな理由で失敗することがあります。

  • リクエストが無効です。
  • Webhook または Webhook をホストしているスペースが削除された。
  • ネットワーク接続や割り当て上限などの断続的な問題。

Webhook を構築する際は、次の方法でエラーを適切に処理する必要があります。

Google Chat API は、エラーを google.rpc.Status として返します。これには、発生したエラーの種類(クライアント エラー(400 シリーズ)またはサーバーエラー(500 シリーズ))を示す HTTP エラー code が含まれます。すべての HTTP マッピングを確認するには、google.rpc.Code をご覧ください。

{
    "code": 503,
    "message": "The service is currently unavailable.",
    "status": "UNAVAILABLE"
}

HTTP ステータス コードの解釈方法とエラーの処理方法については、エラーをご覧ください。

制限事項と考慮事項

  • Google Chat API でウェブフックを使用してメッセージを作成する場合、レスポンスにメッセージ全体が含まれません。レスポンスには name フィールドと thread.name フィールドのみが入力されます。
  • Webhook には、spaces.messages.create のスペースあたりの割り当て(1 秒あたり 1 件のリクエスト。スペース内のすべての Webhook で共有)が適用されます。同じスペースで 1 秒あたり 1 件を超えるクエリを含む Webhook リクエストも拒否されることがあります。Chat API の割り当てについて詳しくは、使用制限をご覧ください。