为您的组织创建应用时,组织使用的可能是防火墙,这可能会导致 Google Chat 无法将消息发送到您的应用。为此,Google Chat 与 Cloud Pub/Sub 集成。此集成可让您的应用与 Cloud Pub/Sub 建立连接并接收消息。
设置 Cloud Pub/Sub
本部分介绍如何设置 Cloud Pub/Sub 主题并为您的应用订阅该主题。
创建启用了 Pub/Sub 的 Google Cloud 项目
要将 Cloud Pub/Sub 用于您的应用,您需要创建一个已启用 Pub/Sub API 的 Google Cloud Platform 项目。
- 在 Google Cloud 控制台中创建新项目。
- 在控制台的左侧窗格中,选择 Pub/Sub,然后选择启用 API。
创建 Pub/Sub 主题
接下来,创建一个主题,Chat API 应向该主题发送消息。如需了解如何创建主题,请参阅 Pub/Sub 控制台快速入门。
启用 Google Chat API
请务必启用 Google Chat API,并将其设为上一步中创建的主题。
- 在控制台的左侧窗格中,选择 API 和服务,然后启用 Google Chat API。
- 配置 API 时,请确保将“连接设置”设置为 Cloud Pub/Sub,并提供上一步中指定的主题。
- 填写示例应用指南中详述的其他字段。
针对您的主题授予发布权限
为了让 Google Chat 向您的主题发布消息,Google Chat 必须具有该主题的发布权限。如需授予 Google Chat 这些权限,请将 Pub/Sub Publisher 角色分配给以下服务帐号:
chat-api-push@system.gserviceaccount.com
这将授权 Google Chat 围绕您的主题发布内容。
创建服务账号
为了使应用代码使用 Cloud Pub/Sub 和 Google Chat 进行授权,需要使用服务帐号。如需设置服务帐号,请参阅使用服务帐号通过 Chat API 授权和身份验证。
创建订阅
按照 Cloud Pub/Sub 订阅者指南,对您创建的主题设置订阅。将订阅类型配置为“webhook 拉取”。为您在上一步中创建的服务帐号添加订阅权限,并为其指定“Pub/Sub 订阅者”角色。
发布 Cloud Pub/Sub 应用
如需详细了解如何发布应用,请参阅发布应用指南。对于 Cloud Pub/Sub 应用,请选择适用于 Cloud Pub/Sub 的选项,并输入所创建主题的完全限定名称。主题名称必须采用以下格式:
projects/PROJECT_ID/topics/TOPIC_ID
例如 projects/pubsub-demo-2/topics/test-topic
。
以这种方式发布应用后,用户即可使用该应用,并且该应用将在您配置的 Pub/Sub 主题上接收消息。
下一部分中的示例将介绍如何创建和运行实现这些对象的简单应用。
应用实现示例
以下示例代码展示了一个使用 Cloud Pub/Sub 接收传入消息的简单应用。若要试用此应用,您需要:
- 在 Main 类中修改项目 ID 和订阅 ID 值
按照身份验证使用入门指南中的说明提供服务帐号凭据。
导出 GOOGLE_APPLICATIONCREDENTIALS=<path_to_service_account_file>
如需运行此代码,您的类路径中必须包含以下依赖项:
package com.google.chat;
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 = "my-project-id";
// Cloud Pub/Sub Subscription ID
public static final String SUBSCRIPTION_ID = "my-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 "my-subscription-id" 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();
}
}
限制和注意事项
请注意以下 Pub/Sub 限制和注意事项:
- 打开、提交或取消对话框需要来自具有
DialogEventType
的 Chat 应用的同步响应。因此,使用异步架构(如 Cloud Pub/Sub)构建的聊天应用也不支持对话框。作为权宜之计,请考虑使用卡片消息而非对话框。 - 使用 Cloud Pub/Sub 构建的聊天应用无法使用同步响应更新个别卡片。如需更新卡片,请调用 Patch Message API 方法来更新整条消息。
排查问题
当 Google Chat 应用或卡片返回错误时,Chat 界面会显示“出了点问题”或“无法处理您的请求”。有时,Chat 界面中不显示任何错误消息,但 Chat 应用或卡片产生了意外结果;例如,卡片消息可能不会显示。
为 Chat 应用启用错误日志记录功能后,虽然 Chat 界面中可能不会显示错误消息,但借助描述性错误消息和日志数据,您可以修复相关错误。如需获取查看、调试和修正错误方面的帮助,请参阅排查和修正 Google Chat 错误。