构建 HTTP Google Chat 应用

本页介绍了如何创建 HTTP Chat 应用。实现此架构的方法有很多种。在 Google Cloud 上,您可以使用 Cloud Run 和 App Engine。在本快速入门中,您 将编写并部署一个 Cloud Run 函数 ,Chat 应用将使用该函数来响应用户的消息。

借助此架构,您可以配置 Chat 以使用 HTTP 与 Google Cloud 或本地服务器集成,如下图所示:

使用本地服务器中的 Web 服务的 Chat 应用的架构。

在上图中,与 HTTP Chat 应用互动的用户具有以下信息流:

  1. 用户在 Chat 中向 Chat 应用发送消息,可以是私信,也可以是在 Chat 聊天室中发送。
  2. 系统会向 Web 服务器发送 HTTP 请求,该服务器可以是包含 Chat 应用逻辑的云端系统或本地系统。
  3. (可选)Chat 应用逻辑可以与 Google Workspace 服务(例如日历和表格)、其他 Google 服务(例如地图、YouTube 和 Vertex AI)或其他 Web 服务(例如项目管理系统或工单工具)集成。
  4. Web 服务器会将 HTTP 响应发送回 Chat 中的 Chat 应用服务。
  5. 响应会传递给用户。
  6. (可选)Chat 应用可以调用 Chat API 以异步发布消息或执行其他操作。

此架构让您可以灵活地使用系统中已有的现有库和组件,因为这些 Chat 应用可以使用不同的编程语言进行设计。

目标

  • 设置环境。
  • 创建和部署 Cloud Run 函数。
  • 将应用发布到 Chat。
  • 测试应用。

前提条件

设置环境

在使用 Google API 之前,您需要在 Google 云项目中启用它们。您可以在单个 Google Cloud 项目中启用一个或多个 API。
  • 在 Google API 控制台中,启用 Google Chat API、Cloud Build API、Cloud Functions API, Cloud Pub/Sub API、Cloud Logging API、Artifact Registry API 和 Cloud Run API。

    启用 API

创建和部署 Cloud Run 函数

创建并部署一个 Cloud Run 函数,该函数会生成一个 Chat 卡片,其中包含发送者的显示名称和头像图片。当 Chat 应用收到消息时,它会运行该函数并使用卡片进行响应。

如需为 Chat 应用创建和部署函数,请完成以下步骤:

Node.js

  1. 在 Google API 控制台中,前往 Cloud Run 页面:

    前往 Cloud Run

    确保已选择 Chat 应用的项目。

  2. 点击编写函数

  3. 创建服务 页面上,设置您的函数:

    1. 服务名称 字段中,输入 quickstartchatapp
    2. 区域 列表中,选择一个区域。
    3. 运行时 列表中,选择最新版本的 Node.js。
    4. 身份验证 部分中,选择需要进行身份验证
    5. 点击创建,然后等待 Cloud Run 创建服务。 控制台会将您重定向到来源 标签页。
  4. 来源 标签页中:

    1. 入口点 中,删除默认文本并输入 avatarApp
    2. index.js 的内容替换为以下代码:

      node/avatar-app/index.js
      const functions = require('@google-cloud/functions-framework');
      
      // Command IDs (configure these in Google Chat API)
      const ABOUT_COMMAND_ID = 1; // ID for the "/about" slash command
      const HELP_COMMAND_ID = 2; // ID for the "Help" quick command
      
      /**
       * Google Cloud Function that handles HTTP requests from Google Chat.
       *
       * @param {Object} req - The HTTP request object sent from Google Chat.
       * @param {Object} res - The HTTP response object.
       */
      functions.http('avatarApp', (req, res) => {
        const event = req.body;
      
        if (event.appCommandMetadata) {
          handleAppCommands(event, res);
        } else {
          handleRegularMessage(event, res);
        }
      });
      
      /**
       * Handles slash and quick commands.
       *
       * @param {Object} event - The Google Chat event.
       * @param {Object} res - The HTTP response object.
       */
      function handleAppCommands(event, res) {
        const {appCommandId, appCommandType} = event.appCommandMetadata;
      
        switch (appCommandId) {
          case ABOUT_COMMAND_ID:
            return res.send({
              privateMessageViewer: event.user,
              text: 'The Avatar app replies to Google Chat messages.'
            });
          case HELP_COMMAND_ID:
            return res.send({
              privateMessageViewer: event.user,
              text: 'The Avatar app replies to Google Chat messages.'
            });
        }
      }
      
      /**
       * Handles regular messages (not commands).
       *
       * @param {Object} event - The Google Chat event.
       * @param {Object} res - The HTTP response object.
       */
      function handleRegularMessage(event, res) {
        const messageData = createMessage(event.user);
        res.send(messageData);
      }
      
      /**
       * Creates a card message with the user's avatar.
       *
       * @param {Object} user - The user who sent the message.
       * @param {string} user.displayName - The user's display name.
       * @param {string} user.avatarUrl - The URL of the user's avatar.
       * @return {Object} - The card message object.
       */
      function createMessage({displayName, avatarUrl}) {
        return {
          text: 'Here\'s your avatar',
          cardsV2: [{
            cardId: 'avatarCard',
            card: {
              name: 'Avatar Card',
              header: {
                title: `Hello ${displayName}!`,
              },
              sections: [{
                widgets: [
                  {textParagraph: {text: 'Your avatar picture:'}},
                  {image: {imageUrl: avatarUrl}},
                ],
              }],
            },
          }],
        };
      }

    3. 点击保存并重新部署

Python

  1. 在 Google API 控制台中,前往 Cloud Run 页面:

    前往 Cloud Run

    确保已选择 Chat 应用的项目。

  2. 点击编写函数

  3. 创建服务 页面上,设置您的函数:

    1. 服务名称 字段中,输入 quickstartchatapp
    2. 区域 列表中,选择一个区域。
    3. 运行时 列表中,选择最新版本的 Python。
    4. 身份验证 部分中,选择需要进行身份验证
    5. 点击创建,然后等待 Cloud Run 创建服务。 控制台会将您重定向到来源 标签页。
  4. 来源 标签页中:

    1. 入口点 中,删除默认文本并输入 avatar_app
    2. main.py 的内容替换为以下代码:

      python/avatar-app/main.py
      from typing import Any, Mapping
      
      import flask
      import functions_framework
      
      # Command IDs (configure these in Google Chat API)
      ABOUT_COMMAND_ID = 1  # ID for the "/about" slash command
      HELP_COMMAND_ID = 2  # ID for the "Help" quick command
      
      
      @functions_framework.http
      def avatar_app(req: flask.Request) -> Mapping[str, Any]:
          """Google Cloud Function that handles HTTP requests from Google Chat.
      
          Args:
              flask.Request: the request
      
          Returns:
              Mapping[str, Any]: the response
          """
          event = req.get_json(silent=True)
      
          if event and "appCommandMetadata" in event:
              return handle_app_commands(event)
          else:
              return handle_regular_message(event)
      
      
      def handle_app_commands(event: Mapping[str, Any]) -> Mapping[str, Any]:
          """Handles slash and quick commands.
      
          Args:
              Mapping[str, Any] event: The Google Chat event.
      
          Returns:
              Mapping[str, Any]: the response
          """
          app_command_id = event["appCommandMetadata"]["appCommandId"]
      
          if app_command_id == ABOUT_COMMAND_ID:
              return {
                  "privateMessageViewer": event["user"],
                  "text": "The Avatar app replies to Google Chat messages.",
              }
          elif app_command_id == HELP_COMMAND_ID:
              return {
                  "privateMessageViewer": event["user"],
                  "text": "The Avatar app replies to Google Chat messages.",
              }
          return {}
      
      
      
      
      def handle_regular_message(event: Mapping[str, Any]) -> Mapping[str, Any]:
          """Handles regular messages (not commands).
      
          Args:
              Mapping[str, Any] event: The Google Chat event.
      
          Returns:
              Mapping[str, Any]: the response
          """
      
          if not event or "user" not in event:
              return "Invalid request."
      
          message_data = create_message(event["user"])
          return message_data
      
      
      def create_message(user: Mapping[str, Any]) -> Mapping[str, Any]:
          """Creates a card message with the user's avatar.
      
          Args:
              Mapping[str, Any] user: The user who sent the message.
      
          Returns:
              Mapping[str, Any]: a card with the user's avatar.
          """
          display_name = user.get("displayName", "")
          avatar_url = user.get("avatarUrl", "")
      
          return {
              "text": "Here's your avatar",
              "cardsV2": [
                  {
                      "cardId": "avatarCard",
                      "card": {
                          "name": "Avatar Card",
                          "header": {"title": f"Hello {display_name}!"},
                          "sections": [
                              {
                                  "widgets": [
                                      {"textParagraph": {"text": "Your avatar picture:"}},
                                      {"image": {"imageUrl": avatar_url}},
                                  ]
                              }
                          ],
                      },
                  }
              ],
          }

    3. 点击保存并重新部署

Java

  1. 在 Google API 控制台中,前往 Cloud Run 页面:

    前往 Cloud Run

    确保已选择 Chat 应用的项目。

  2. 点击编写函数

  3. 创建服务 页面上,设置您的函数:

    1. 服务名称 字段中,输入 quickstartchatapp
    2. 区域 列表中,选择一个区域。
    3. 运行时 列表中,选择最新版本的 Java。
    4. 身份验证 部分中,选择需要进行身份验证
    5. 点击创建,然后等待 Cloud Run 创建服务。 控制台会将您重定向到来源 标签页。
  4. 来源 标签页中:

    1. 入口点 中,删除默认文本并输入 App
    2. src/main/java/com/example/Example.java 重命名为 src/main/java/AvatarApp.java
    3. AvatarApp.java 的内容替换为以下代码:

      java/avatar-app/src/main/java/AvatarApp.java
      import com.google.api.services.chat.v1.model.CardWithId;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Card;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1CardHeader;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Image;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Section;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1TextParagraph;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Widget;
      import com.google.api.services.chat.v1.model.Message;
      import com.google.api.services.chat.v1.model.User;
      import com.google.cloud.functions.HttpFunction;
      import com.google.cloud.functions.HttpRequest;
      import com.google.cloud.functions.HttpResponse;
      import com.google.gson.Gson;
      import com.google.gson.JsonObject;
      import java.util.List;
      
      public class AvatarApp implements HttpFunction {
        private static final Gson gson = new Gson();
      
        // Command IDs (configure these in Google Chat API)
        private static final int ABOUT_COMMAND_ID = 1; // ID for the "/about" slash command
        private static final int HELP_COMMAND_ID = 2; // ID for the "Help" quick command
      
        @Override
        public void service(HttpRequest request, HttpResponse response) throws Exception {
          JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);
      
          if (event.has("appCommandMetadata")) {
            handleAppCommands(event, response);
          } else {
            handleRegularMessage(event, response);
          }
        }
      
        /**
         * Handles slash and quick commands.
         *
         * @param event    The Google Chat event.
         * @param response The HTTP response object.
         */
        private void handleAppCommands(JsonObject event, HttpResponse response) throws Exception {
          int appCommandId = event.getAsJsonObject("appCommandMetadata").get("appCommandId").getAsInt();
      
          switch (appCommandId) {
            case ABOUT_COMMAND_ID:
              Message aboutMessage = new Message();
              aboutMessage.setText("The Avatar app replies to Google Chat messages.");
              aboutMessage.setPrivateMessageViewer(new User()
                  .setName(event.getAsJsonObject("user").get("name").getAsString()));
              response.getWriter().write(gson.toJson(aboutMessage));
              return;
            case HELP_COMMAND_ID:
              Message helpMessage = new Message();
              helpMessage.setText("The Avatar app replies to Google Chat messages.");
              helpMessage.setPrivateMessageViewer(new User()
                  .setName(event.getAsJsonObject("user").get("name").getAsString()));
              response.getWriter().write(gson.toJson(helpMessage));
              return;
          }
        }
      
        /**
         * Handles regular messages (not commands).
         *
         * @param event    The Google Chat event.
         * @param response The HTTP response object.
         */
        private void handleRegularMessage(JsonObject event, HttpResponse response) throws Exception {
      
          if (!event.has("user")) {
            response.getWriter().write("Invalid request.");
            return;
          }
      
          JsonObject user = event.getAsJsonObject("user");
          String displayName = user.has("displayName") ? user.get("displayName").getAsString() : "";
          String avatarUrl = user.has("avatarUrl") ? user.get("avatarUrl").getAsString() : "";
          Message message = createMessage(displayName, avatarUrl);
          response.getWriter().write(gson.toJson(message));
        }
      
        /**
         * Creates a card message with the user's avatar.
         *
         * @param displayName The user's display name.
         * @param avatarUrl   The URL of the user's avatar.
         * @return The card message object.
         */
        private Message createMessage(String displayName, String avatarUrl) {
          return new Message()
              .setText("Here's your avatar")
              .setCardsV2(List.of(new CardWithId()
                  .setCardId("avatarCard")
                  .setCard(new GoogleAppsCardV1Card()
                      .setName("Avatar Card")
                      .setHeader(new GoogleAppsCardV1CardHeader()
                          .setTitle(String.format("Hello %s!", displayName)))
                      .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(
                          new GoogleAppsCardV1Widget()
                              .setTextParagraph(new GoogleAppsCardV1TextParagraph()
                                  .setText("Your avatar picture:")),
                          new GoogleAppsCardV1Widget()
                              .setImage(new GoogleAppsCardV1Image().setImageUrl(avatarUrl)))))))));
        }
      }

    4. pom.xml 的内容替换为以下代码:

      java/avatar-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/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
      
        <groupId>gcfv2</groupId>
        <artifactId>avatar-app</artifactId>
        <version>0.0.1</version>
        <name>Avatar App</name>
      
        <properties>
          <maven.compiler.release>21</maven.compiler.release>
        </properties>
      
        <dependencies>
          <dependency>
            <groupId>com.google.cloud.functions</groupId>
            <artifactId>functions-framework-api</artifactId>
            <version>1.1.4</version>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
          <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.12.1</version>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/com.google.apis/google-api-services-chat -->
          <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-chat</artifactId>
            <version>v1-rev20250116-2.0.0</version>
          </dependency>
      
        </dependencies>
      
      </project>

    5. 点击保存并重新部署

授权 Google Chat 调用您的函数

如需授权 Google Chat 调用您的函数,请添加具有 Cloud Run Invoker 角色的 Google Chat 服务账号。

  1. 在 Google API 控制台中,前往 Cloud Run 页面:

    前往 Cloud Run

  2. 在 Cloud Run 服务列表中,选中接收 函数旁边的复选框。(请勿点击函数本身。)

  3. 点击权限 。此时会打开权限 面板。

  4. 点击添加主账号

  5. 新主账号 中,输入 chat@system.gserviceaccount.com

  6. 选择角色 中,选择 Cloud Run > Cloud Run Invoker

  7. 点击保存

配置 Chat 应用

部署 Cloud Run 函数后,请按照以下步骤将其转换为 Google Chat 应用:

  1. 在 Google API 控制台中,前往 Cloud Run 页面:

    前往 Cloud Run

    确保已选择启用了 Cloud Run 的项目。

  2. 在服务列表中,点击 quickstartchatapp

  3. 服务详情 页面上,复制该函数的 网址

  4. 搜索“Google Chat API”,然后点击 Google Chat API,再点击 管理

    前往 Chat API

  5. 点击配置 并设置 Google Chat 应用:

    1. 清除将此 Chat 扩展应用构建为 Google Workspace 插件 。系统会打开一个对话框,要求您确认。在该对话框中,点击停用
    2. 应用名称 中,输入 Quickstart App
    3. 头像网址中,输入 https://developers.google.com/chat/images/quickstart-app-avatar.png
    4. 说明 中,输入 Quickstart app
    5. 功能 下,选择加入聊天室和群组对话
    6. 连接设置 下,选择 HTTP 端点网址
    7. 触发器下,选择为所有 触发器使用通用 HTTP 端点网址,并将 Cloud Run 函数触发器的网址粘贴到 框中。
    8. 可见性下,选择 面向您网域中的特定人员和 群组提供此 Chat 扩展应用,然后输入您的电子邮件地址。
    9. 日志 下,选择将错误记录到 Logging
  6. 点击保存

Chat 应用已准备好接收和回复 Chat 上的消息。

测试 Chat 应用

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

  1. 使用您在添加自己作为受信任的测试人员时提供的 Google Workspace 账号打开 Google Chat。

    前往 Google Chat

  2. 点击 发起新聊天
  3. 添加 1 位或多位用户 字段中,输入 Chat 应用的名称。
  4. 从结果中选择 Chat 应用。系统会打开私信。

  5. 在与该应用的新私信中,输入 Hello 并按 enter 键。

Chat 应用的响应包含一条 卡片消息,其中 显示了发送者的姓名和头像图片,如下图 所示:

聊天应用以卡片形式回复,其中包含发送者的显示名称和头像图片

如需添加受信任的测试人员并详细了解如何测试互动功能,请参阅 测试 Google Chat 应用的互动功能

问题排查

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

虽然 Chat 界面中可能不会显示错误消息, 但当 Chat 应用的错误日志记录功能开启后,系统会提供描述性错误消息和日志数据,帮助您修复错误。如需查看、 调试和修复错误方面的帮助,请参阅 排查和修复 Google Chat 错误

清理

为避免因本教程中使用的 资源导致您的 Google Cloud 账号产生费用,我们建议您删除 云项目。

  1. 在 Google API 控制台中,前往管理资源 页面。依次点击 菜单 > IAM 和管理 > 管理资源

    前往 Resource Manager

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