使用 Pub/Sub 构建在防火墙后的 Google Chat 应用

本页将介绍如何使用以下工具创建 Chat 扩展应用: Pub/Sub。这种类型的 Chat 应用的架构非常有用 如果贵组织设有防火墙,则可能会阻止 Chat 向 Chat 应用发送消息,或者 Chat 应用 使用 Google Workspace Events API。不过, 架构存在以下局限性 聊天应用只能收发消息 异步消息

  • 无法使用对话框 。而应使用 卡片消息
  • 无法使用同步响应更新个别卡片。请改为更新 调用 patch 方法。

下图显示了 使用 Pub/Sub 构建的聊天应用:

使用 Pub/Sub 实现的 Chat 应用的架构。

在上图中,用户与 Pub/Sub 进行交互 Chat 应用具有以下信息流:

  1. 用户在 Chat 中向 聊天应用(可通过私信或 Chat 聊天室,或 Chat 聊天室中发生的活动 且 Chat 应用已启用 订阅

  2. Chat 将消息发送到一个 Pub/Sub 主题。

  3. 一个应用服务器,可以是云系统或本地系统, 包含 Chat 应用逻辑,会订阅 Pub/Sub 主题以通过防火墙接收消息。

  4. (可选)Chat 应用可以调用 用于异步发布消息或执行其他任务的 操作。

前提条件

Java

设置环境

在使用 Google API 之前,您需要先在 Google Cloud 项目中启用这些 API。 您可以在单个 Google Cloud 项目中启用一个或多个 API。
  • 在 Google Cloud 控制台中,启用 Google Chat API 和 Pub/Sub API。

    启用 API

设置 Pub/Sub

  1. 创建 Pub/Sub 主题 Chat API 可向其发送消息的目标对象我们建议您使用 一个主题。

  2. 授予 Chat 发布权限Pub/Sub Publisher 角色分配给以下人员,从而与该主题相关联: 服务账号:

    chat-api-push@system.gserviceaccount.com
    
  3. 创建服务账号 让 Chat 应用通过 Pub/Sub 进行授权 聊天并将私钥文件保存到您的工作目录。

  4. 创建拉取订阅 与主题相关。

  5. 针对订阅分配 Pub/Sub Subscriber 角色 创建服务账号

编写脚本

Java

  1. 在 CLI 中,提供服务账号凭据

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. 在 CLI 中,提供 Google Cloud 项目 ID:

    export PROJECT_ID=PROJECT_ID
    
  3. 在 CLI 中,提供 之前创建的:

    export SUBSCRIPTION_ID=SUBSCRIPTION_ID
    
  4. 在您的工作目录中,创建一个名为 pom.xml 的文件。

  5. pom.xml 文件中,粘贴以下代码:

    java/pub-sub-app/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</groupId>
      <artifactId>pub-sub-app</artifactId>
      <version>0.1.0</version>
    
      <name>pub-sub-app-java</name>
    
      <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
      </properties>
    
      <dependencies>
        <!-- Google Chat GAPIC library -->
        <dependency>
          <groupId>com.google.api.grpc</groupId>
          <artifactId>proto-google-cloud-chat-v1</artifactId>
          <version>0.8.0</version>
        </dependency>
        <dependency>
          <groupId>com.google.api</groupId>
          <artifactId>gax</artifactId>
          <version>2.48.1</version>
        </dependency>
        <dependency>
          <groupId>com.google.cloud</groupId>
          <artifactId>google-cloud-chat</artifactId>
          <version>0.1.0</version>
        </dependency>
        <!-- Google Cloud Pub/Sub library -->
        <dependency>
          <groupId>com.google.cloud</groupId>
          <artifactId>google-cloud-pubsub</artifactId>
        <version>1.125.8</version>
        </dependency>
        <!-- JSON utilities -->
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.14.2</version>
        </dependency>
      </dependencies>
    
    </project>
  6. 在您的工作目录中,创建目录结构 src/main/java

  7. src/main/java 目录中,创建一个名为 Main.java 的文件。

  8. Main.java 中,粘贴以下代码:

    java/pub-sub-app/src/main/java/Main.java
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.api.gax.core.FixedCredentialsProvider;
    import com.google.auth.oauth2.GoogleCredentials;
    import com.google.chat.v1.ChatServiceClient;
    import com.google.chat.v1.ChatServiceSettings;
    import com.google.chat.v1.CreateMessageRequest;
    import com.google.chat.v1.CreateMessageRequest.MessageReplyOption;
    import com.google.chat.v1.Message;
    import com.google.chat.v1.Thread;
    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.ProjectSubscriptionName;
    import com.google.pubsub.v1.PubsubMessage;
    import java.io.FileInputStream;
    import java.util.Collections;
    
    public class Main {
    
      public static final String PROJECT_ID_ENV_PROPERTY = "PROJECT_ID";
      public static final String SUBSCRIPTION_ID_ENV_PROPERTY = "SUBSCRIPTION_ID";
      public static final String CREDENTIALS_PATH_ENV_PROPERTY = "GOOGLE_APPLICATION_CREDENTIALS";
    
      public static void main(String[] args) throws Exception {
        ProjectSubscriptionName subscriptionName =
            ProjectSubscriptionName.of(
                System.getenv(Main.PROJECT_ID_ENV_PROPERTY),
                System.getenv(Main.SUBSCRIPTION_ID_ENV_PROPERTY));
    
        // 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("Subscriber is listening to events...");
        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";
    
      private static final String ADDED_RESPONSE = "Thank you for adding me!";
    
      ChatServiceClient chatServiceClient;
    
      EchoApp() throws Exception {
        GoogleCredentials credential =
            GoogleCredentials.fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH))
                .createScoped(Collections.singleton(GOOGLE_CHAT_API_SCOPE));
    
        // Create the ChatServiceSettings with the app credentials
        ChatServiceSettings chatServiceSettings =
            ChatServiceSettings.newBuilder()
                .setCredentialsProvider(FixedCredentialsProvider.create(credential))
                .build();
    
        // Set the Chat service client
        chatServiceClient = ChatServiceClient.create(chatServiceSettings);
      }
    
      // 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();
        }
      }
    
      // Send message to Google Chat based on the type of event.
      public void handle(JsonNode eventJson) throws Exception {
        CreateMessageRequest createMessageRequest;
        switch (eventJson.get("type").asText()) {
          case "ADDED_TO_SPACE":
            // 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")) {
              createMessageRequest =
                  CreateMessageRequest.newBuilder()
                      .setParent(eventJson.get("space").get("name").asText())
                      .setMessage(Message.newBuilder().setText(ADDED_RESPONSE).build())
                      .build();
              break;
            }
          case "MESSAGE":
            // In case of message, post the response in the same thread.
            createMessageRequest =
                CreateMessageRequest.newBuilder()
                    .setParent(eventJson.get("space").get("name").asText())
                    .setMessageReplyOption(MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD)
                    .setMessage(
                        Message.newBuilder()
                            .setText(
                                "You said: `" + eventJson.get("message").get("text").asText() + "`")
                            .setThread(
                                Thread.newBuilder()
                                    .setName(
                                        eventJson.get("message").get("thread").get("name").asText())
                                    .build())
                            .build())
                    .build();
            break;
          case "REMOVED_FROM_SPACE":
          default:
            // Do nothing
            return;
        }
    
        // Post the response to Google Chat.
        chatServiceClient.createMessage(createMessageRequest);
      }
    }

将应用发布到 Chat

  1. 在 Google Cloud 控制台中,点击菜单 &gt; API 和服务 &gt; 已启用的 API 和服务 &gt; Google Chat API &gt; 配置

    前往“配置”

  2. 配置适用于 Pub/Sub 的 Chat 应用:

    1. 应用名称中,输入 Quickstart App
    2. 头像网址中输入 https://developers.google.com/chat/images/quickstart-app-avatar.png
    3. 说明中,输入 Quickstart app
    4. 功能下方,选择接收 1 对 1 消息加入聊天室和群组对话
    5. 连接设置下,选择 Cloud Pub/Sub,并将 您之前创建的 Pub/Sub 主题的名称。
    6. 公开范围下方,选择将此 Google Chat 应用设为可供网域中的特定人员和群组使用,然后输入您的电子邮件地址。
    7. 日志下,选择将错误记录到 Logging
  3. 点击保存

此应用可以在 Chat 中接收和回复消息了。

运行脚本

在 CLI 中,切换到您的工作目录并运行脚本:

Java

mvn compile exec:java -Dexec.mainClass=Main

当您运行代码时,应用会开始监听已发布的消息 Pub/Sub 主题

测试您的 Chat 应用

如需测试您的 Chat 应用,请使用如下应用打开私信聊天室: Chat 应用并发送消息:

  1. 使用您用于登录的 Google Workspace 账号打开 Google Chat 在您添加为可信测试员时提供的凭据。

    前往 Google Chat

  2. 点击 发起新对话
  3. 添加一人或多人字段中,输入 Chat 应用。
  4. 从结果中选择您的 Chat 应用。直接客户 消息会打开。

  5. 在与该应用的新私信对话中,输入“Hello”,然后按 enter

如需添加可信测试员,并详细了解如何测试互动功能,请参阅 测试适用于以下应用的互动功能: Google Chat 应用

问题排查

当 Google Chat 应用或 card 会返回错误, 聊天界面会显示一条内容为“出了点问题”的消息。 或“无法处理您的请求”。有时,Chat 界面 不会显示任何错误消息,但 Chat 应用或 卡片会产生意外结果;例如,卡片消息 。

虽然 Chat 界面中可能不会显示错误消息, 提供描述性错误消息和日志数据,以帮助您修正错误 启用 Chat 应用的错误日志记录时。如需观看方面的帮助, 请参阅 排查并修正 Google Chat 错误

清理

为避免系统因 我们建议您删除 Cloud 项目中。

  1. 在 Google Cloud 控制台中,前往管理资源页面。点击 菜单 &gt; IAM 和管理员 &gt; 管理资源

    <ph type="x-smartling-placeholder"></ph> 前往 Resource Manager

  2. 在项目列表中,选择要删除的项目,然后点击 删除
  3. 在对话框中输入项目 ID,然后点击关停以删除项目 项目。