将 Google Chat 应用构建为网络钩子

本页面介绍了如何设置网络钩子,以使用外部触发器向 Chat 聊天室发送异步消息。例如,您可以配置监控应用,在服务器出现故障时通知 Chat 值班人员。如需使用 Chat 应用发送同步消息,请参阅发送消息

借助此类架构设计,用户无法与 webhook 或连接的外部应用进行交互,因为通信是单向的。网络钩子不是对话性的。但无法回复或接收来自用户的消息或 Chat 应用互动事件。如需回复消息,请构建 Chat 应用,而不是网络钩子。

虽然网络钩子从技术上来讲并不是 Chat 应用(网络钩子使用标准 HTTP 请求连接应用),但为了简单起见,本页面将其称为 Chat 应用。每个网络钩子只能在注册它的 Chat 聊天室中有效。传入的网络钩子适用于私信,但前提是所有用户都已启用 Chat 扩展应用。您无法将网络钩子发布到 Google Workspace Marketplace。

下图显示了连接到 Chat 的 webhook 的架构:

用于向 Chat 发送异步消息的传入网络钩子的架构。

在上图中,Chat 应用具有以下信息流:

  1. Chat 应用逻辑从外部第三方服务(例如项目管理系统或票务工具)接收信息。
  2. Chat 应用逻辑托管在云系统或本地系统中,该系统可以使用网络钩子网址向特定 Chat 聊天室发送消息。
  3. 用户可以在该特定 Chat 聊天室中接收来自 Chat 应用的消息,但无法与 Chat 应用互动。

前提条件

Python

  • 一个有权访问 ChatGoogle Workspace 帐号。您的 Google Workspace 组织必须允许用户添加和使用传入的网络钩子
  • Python 3.10.7 或更高版本。
  • httplib2 库。如有必要,请运行以下命令行界面 (CLI) 命令,使用 pip 安装该库:

    pip install httplib2
    
  • 现有的 Chat 聊天室。

Node.js

Java

Apps 脚本

创建网络钩子

如需创建网络钩子,请在要接收消息的 Chat 聊天室中注册网络钩子,然后编写一个脚本来发送消息。

注册传入的网络钩子

  1. 在浏览器中,打开 Chat。 无法通过 Chat 移动应用配置网络钩子。
  2. 前往您要添加网络钩子的聊天室。
  3. 点击聊天室标题旁边的“展开”箭头,然后点击应用和集成
  4. 点击 Add webhooks

  5. Name 字段中,输入 Quickstart Webhook

  6. 头像网址字段中,输入 https://developers.google.com/chat/images/chat-product-icon.png

  7. 点击保存

  8. 如需复制网络钩子网址,请点击 更多,然后点击复制链接

编写 webhook 脚本

示例网络钩子脚本通过向网络钩子网址发送 POST 请求,向在其中注册网络钩子的空间发送消息。Chat API 会返回一个 Message 实例作为响应。

选择一种语言,了解如何创建网络钩子脚本:

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 网址。

Node.js

  1. 在您的工作目录中,创建一个名为 index.js 的文件。

  2. index.js 中,粘贴以下代码:

    节点/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 网址。

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 网址。

Apps 脚本

  1. 在浏览器中,前往 Apps 脚本

  2. 点击 New Project

  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 网址。

运行 webhook 脚本

在 CLI 中,运行以下脚本:

Python

  python3 quickstart.py

Node.js

  node index.js

Java

  mvn compile exec:java -Dexec.mainClass=App

Apps 脚本

  • 点击运行

当您运行代码时,webhook 会向您在其中注册它的空间发送消息。

发起或回复消息串

  1. 指定 spaces.messages.thread.threadKey 作为消息请求正文的一部分。根据您是启动还是回复线程,对 threadKey 使用以下值:

    • 如果要启动某个线程,请将 threadKey 设置为任意字符串,但记下此值以向该线程发布回复。

    • 如果要回复某个线程,请指定该线程启动时设置的 threadKey。例如,如需向初始消息使用 MY-THREAD 的线程发布回复,请设置 MY-THREAD

  2. 定义找不到指定的 threadKey 时的线程行为:

    • 回复消息串或发起新消息串。将 messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD 参数添加到 webhook 网址。传递此网址参数会导致 Chat 使用指定的 threadKey 查找现有会话。如果找到了消息串,则会将该消息作为回复发布到该会话。如果未找到,则该消息会启动与该 threadKey 对应的新线程。

    • 回复消息串或不执行任何操作。将 messageReplyOption=REPLY_MESSAGE_OR_FAIL 参数添加到 webhook 网址。传递此网址参数会导致 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 脚本

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);
}