このページでは、Pub/Sub を使用して Chat アプリを作成する方法について説明します。Chat アプリ向けのこのタイプのアーキテクチャは、組織にファイアウォールがあり、Chat から Chat アプリにメッセージが送信されない場合や、Chat アプリが Google Workspace Events API を使用している場合に役立ちます。ただし、Chat アプリは非同期メッセージのみを送受信できるため、このアーキテクチャには次の制限があります。
- メッセージでダイアログは使用できません。代わりにカード メッセージを使用してください。
- 同期レスポンスで個々のカードを更新することはできません。代わりに、
patch
メソッドを呼び出してメッセージ全体を更新します。
次の図は、Pub/Sub を使用してビルドされた Chat アプリのアーキテクチャを示しています。
上の図では、Pub/Sub Chat アプリを操作しているユーザーには次のような情報フローがあります。
ユーザーが Chat 内のメッセージをダイレクト メッセージまたは Chat スペース内で Chat アプリに送信するか、Chat アプリに有効なサブスクリプションがある Chat スペースでイベントが発生します。
Chat はメッセージを Pub/Sub トピックに送信します。
アプリケーション サーバー(Chat アプリのロジックを含むクラウドまたはオンプレミス システム)は、ファイアウォール経由でメッセージを受信するために、Pub/Sub トピックをサブスクライブします。
必要に応じて、Chat アプリは Chat API を呼び出して、メッセージを非同期で投稿したり、その他のオペレーションを実行したりできます。
前提条件
Java
- Google Chat へのアクセス権を持つ Google Workspace アカウント。
- Google Cloud プロジェクト。
- Cloud プロジェクトの課金がオンになっていることを確認します。詳しくは、プロジェクトの課金ステータスを確認する方法をご覧ください。
- Java 11 以降。
- Apache Maven
環境を設定する
Google API を使用する前に、Google Cloud プロジェクトで API を有効にする必要があります。1 つの Google Cloud プロジェクトで 1 つ以上の API を有効にできます。Google Cloud コンソールで、Google Chat API と Pub/Sub API を有効にします。
Pub/Sub を設定する
Chat API がメッセージを送信できる Pub/Sub トピックを作成します。Chat アプリごとに 1 つのトピックを使用することをおすすめします。
次のサービス アカウントに Pub/Sub パブリッシャーのロールを割り当てて、トピックにパブリッシュする権限を Chat に付与します。
chat-api-push@system.gserviceaccount.com
Chat アプリが Pub/Sub と Chat で承認するためのサービス アカウントを作成し、秘密鍵ファイルを作業ディレクトリに保存します。
トピックへの pull サブスクリプションを作成します。
以前に作成したサービス アカウントのサブスクリプションに Pub/Sub サブスクライバー ロールを割り当てます。
スクリプトを作成する
Java
CLI で、サービス アカウントの認証情報を指定します。
export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
作業ディレクトリに、
pom.xml
という名前のファイルを作成します。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.pubsub</groupId> <artifactId>java-pubsub-app</artifactId> <version>0.1.0</version> <name>java-pubsub-app</name> <properties> <maven.compiler.target>11</maven.compiler.target> <maven.compiler.source>11</maven.compiler.source> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.google.cloud</groupId> <artifactId>libraries-bom</artifactId> <version>26.26.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.9.1</version> </dependency> <dependency> <groupId>com.google.api-client</groupId> <artifactId>google-api-client</artifactId> <version>1.32.1</version> </dependency> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-pubsub</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> </plugins> </pluginManagement> </build> </project>
作業ディレクトリに、ディレクトリ構造
src/main/java
を作成します。src/main/java
ディレクトリにMain.java
という名前のファイルを作成します。Main.java
に、次のコードを貼り付けます。import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.ByteArrayContent; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpTransport; import com.google.cloud.pubsub.v1.AckReplyConsumer; import com.google.cloud.pubsub.v1.MessageReceiver; import com.google.cloud.pubsub.v1.Subscriber; import com.google.pubsub.v1.PubsubMessage; import com.google.pubsub.v1.ProjectSubscriptionName; import java.io.FileInputStream; import java.util.Collections; public class Main { public static final String CREDENTIALS_PATH_ENV_PROPERTY = "GOOGLE_APPLICATION_CREDENTIALS"; // Google Cloud Project ID public static final String PROJECT_ID = PROJECT_ID; // Cloud Pub/Sub Subscription ID public static final String SUBSCRIPTION_ID = SUBSCRIPTION_ID public static void main(String[] args) throws Exception { ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(PROJECT_ID, SUBSCRIPTION_ID); // Instantiate app, which implements an asynchronous message receiver. EchoApp echoApp = new EchoApp(); // Create a subscriber for <var>SUBSCRIPTION_ID</var> bound to the message receiver final Subscriber subscriber = Subscriber.newBuilder(subscriptionName, echoApp).build(); System.out.println("Starting subscriber..."); subscriber.startAsync(); // Wait for termination subscriber.awaitTerminated(); } } / ** * A demo app which implements {@link MessageReceiver} to receive messages. It simply echoes the * incoming messages. */ class EchoApp implements MessageReceiver { // Path to the private key JSON file of the service account to be used for posting response // messages to Google Chat. // In this demo, we are using the same service account for authorizing with Cloud Pub/Sub to // receive messages and authorizing with Google Chat to post messages. If you are using // different service accounts, please set the path to the private key JSON file of the service // account used to post messages to Google Chat here. private static final String SERVICE_ACCOUNT_KEY_PATH = System.getenv(Main.CREDENTIALS_PATH_ENV_PROPERTY); // Developer code for Google Chat API scope. private static final String GOOGLE_CHAT_API_SCOPE = "https://www.googleapis.com/auth/chat.bot"; // Response URL Template with placeholders for space id. private static final String RESPONSE_URL_TEMPLATE = "https://chat.googleapis.com/v1/__SPACE_ID__/messages"; // Response echo message template. private static final String RESPONSE_TEMPLATE = "You said: `__MESSAGE__`"; private static final String ADDED_RESPONSE = "Thank you for adding me!"; GoogleCredential credential; HttpTransport httpTransport; HttpRequestFactory requestFactory; EchoApp() throws Exception { credential = GoogleCredential.fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH)) .createScoped(Collections.singleton(GOOGLE_CHAT_API_SCOPE)); httpTransport = GoogleNetHttpTransport.newTrustedTransport(); requestFactory = httpTransport.createRequestFactory(credential); } // Called when a message is received by the subscriber. @Override public void receiveMessage(PubsubMessage pubsubMessage, AckReplyConsumer consumer) { System.out.println("Id : " + pubsubMessage.getMessageId()); // handle incoming message, then ack/nack the received message try { ObjectMapper mapper = new ObjectMapper(); JsonNode dataJson = mapper.readTree(pubsubMessage.getData().toStringUtf8()); System.out.println("Data : " + dataJson.toString()); handle(dataJson); consumer.ack(); } catch (Exception e) { System.out.println(e); consumer.nack(); } } public void handle(JsonNode eventJson) throws Exception { JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(false); ObjectNode responseNode = jsonNodeFactory.objectNode(); // Construct the response depending on the event received. String eventType = eventJson.get("type").asText(); switch (eventType) { case "ADDED_TO_SPACE": responseNode.put("text", ADDED_RESPONSE); // An app can also be added to a space by @mentioning it in a message. In that case, we fall // through to the MESSAGE case and let the app respond. If the app was added using the // invite flow, we just post a thank you message in the space. if(!eventJson.has("message")) { break; } case "MESSAGE": responseNode.put("text", RESPONSE_TEMPLATE.replaceFirst( "__MESSAGE__", eventJson.get("message").get("text").asText())); // In case of message, post the response in the same thread. ObjectNode threadNode = jsonNodeFactory.objectNode(); threadNode.put("name", eventJson.get("message").get("thread").get("name").asText()); responseNode.put("thread", threadNode); break; case "REMOVED_FROM_SPACE": default: // Do nothing return; } // Post the response to Google Chat. String URI = RESPONSE_URL_TEMPLATE.replaceFirst( "__SPACE_ID__", eventJson.get("space").get("name").asText()); GenericUrl url = new GenericUrl(URI); HttpContent content = new ByteArrayContent("application/json", responseNode.toString().getBytes("UTF-8")); HttpRequest request = requestFactory.buildPostRequest(url, content); com.google.api.client.http.HttpResponse response = request.execute(); } }
次のように置き換えます。
PROJECT_ID
: Google Cloud プロジェクト ID。SUBSCRIPTION_ID
: 前に作成した Pub/Sub サブスクリプションのサブスクリプション ID。
アプリを Chat に公開する
Google Cloud コンソールで、メニュー > [API とサービス] > [有効な API とサービス] > [Google Chat API] > [構成] に移動します。
Pub/Sub 用に Chat アプリを構成します。
- [アプリ名] に「
Quickstart App
」と入力します。 - [アバターの URL] に「
https://developers.google.com/chat/images/quickstart-app-avatar.png
」と入力します。 - [説明] に「
Quickstart app
」と入力します。 - [機能] で、[1:1 のメッセージを受信する] と [スペースとグループの会話に参加する] を選択します。
- [接続設定] で [Cloud Pub/Sub] を選択し、以前に作成した Pub/Sub トピックの名前を貼り付けます。
- [公開設定] で、[ドメイン内の特定のユーザーとグループがこの Google Chat アプリを利用できるようにする] をオンにして、メールアドレスを入力します。
- [ログ] で、[エラーを Logging に記録] を選択します。
- [アプリ名] に「
[保存] をクリックします。
これで、Chat でメッセージを受信して応答できるようになりました。
スクリプトを実行する
CLI で作業ディレクトリに切り替えて、スクリプトを実行します。
Java
mvn compile exec:java -Dexec.mainClass=Main
コードを実行すると、アプリケーションは Pub/Sub トピックに公開されたメッセージのリッスンを開始します。
Chat アプリをテストする
Chat アプリをテストするには、Chat アプリでダイレクト メッセージ スペースを開き、メッセージを送信します。
Trusted Tester として自身を追加したときに指定した Google Workspace アカウントを使用して Google Chat を開きます。
- [ 新しいチャット] をクリックします。
- [1 人以上のユーザーを追加] フィールドに、Chat アプリの名前を入力します。
検索結果から Chat アプリを選択します。ダイレクト メッセージが開きます。
- このアプリとの新しいダイレクト メッセージに「
Hello
」と入力して、enter
を押します。
Trusted Tester を追加し、インタラクティブ機能のテストの詳細については、Google Chat アプリのインタラクティブ機能をテストするをご覧ください。
トラブルシューティング
Google Chat アプリまたはカードからエラーが返されると、Chat インターフェースに「エラーが発生しました」というメッセージが表示されます。または「リクエストを処理できません」と表示されます。Chat UI にエラー メッセージが表示されていなくても、Chat アプリまたはカードから予期しない結果(カード メッセージが表示されないなど)が生成される場合があります。
エラー メッセージは Chat UI に表示されない場合がありますが、Chat アプリのエラーロギングがオンになっている場合は、エラー メッセージとログデータを使用してエラーを修正できます。エラーの表示、デバッグ、修正については、Google Chat のエラーのトラブルシューティングと修正をご覧ください。
クリーンアップ
このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、Cloud プロジェクトを削除することをおすすめします。
- Google Cloud コンソールで、[リソースの管理] ページに移動します。メニュー > [IAM と管理] > [リソースの管理] をクリックします。
- プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
- ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。
関連トピック
Chat アプリに機能を追加するには、以下をご覧ください。