Xây dựng ứng dụng Google Chat phía sau tường lửa bằng Pub/Sub

Trang này giải thích cách tạo ứng dụng Chat bằng Pub/Sub. Loại cấu trúc này của ứng dụng Chat sẽ hữu ích nếu tổ chức của bạn có tường lửa, điều này có thể ngăn Chat gửi tin nhắn đến ứng dụng Chat hoặc nếu ứng dụng Chat sử dụng API Sự kiện của Google Workspace. Tuy nhiên, cấu trúc này có một số hạn chế sau đây do các ứng dụng trong Chat này chỉ có thể gửi và nhận thông báo không đồng bộ:

  • Không thể dùng hộp thoại trong tin nhắn. Thay vào đó, hãy sử dụng tin nhắn thẻ.
  • Không thể cập nhật từng thẻ bằng phản hồi đồng bộ. Thay vào đó, hãy cập nhật toàn bộ thông báo bằng cách gọi phương thức patch.

Sơ đồ sau đây cho thấy cấu trúc của một ứng dụng Chat tạo bằng Pub/Sub:

Cấu trúc của một ứng dụng Chat được triển khai bằng Pub/Sub.

Trong biểu đồ trước, một người dùng tương tác với ứng dụng Pub/Sub Chat có luồng thông tin sau:

  1. Người dùng gửi tin nhắn trong Chat đến một ứng dụng Chat, bằng tin nhắn trực tiếp hoặc trong phòng Chat, hoặc một sự kiện xảy ra trong phòng Chat mà ứng dụng Chat đó có một gói thuê bao đang hoạt động.

  2. Ứng dụng Chat sẽ gửi tin nhắn đến một chủ đề Pub/Sub.

  3. Máy chủ ứng dụng là hệ thống trên đám mây hoặc tại cơ sở hạ tầng riêng có chứa logic ứng dụng của Chat, đăng ký chủ đề Pub/Sub để nhận thông báo qua tường lửa.

  4. Ứng dụng Chat có thể gọi API Chat để đăng tin nhắn không đồng bộ hoặc thực hiện các thao tác khác (không bắt buộc).

Điều kiện tiên quyết

Java

Thiết lập môi trường

Trước khi sử dụng các API của Google, bạn cần bật những API đó trong một dự án trên Google Cloud. Bạn có thể bật một hoặc nhiều API trong một dự án trên Google Cloud.
  • Trong bảng điều khiển Google Cloud, hãy bật API Google Chat và API Pub/Sub.

    Bật API

Thiết lập Pub/Sub

  1. Tạo một chủ đề Pub/Sub mà API Chat có thể gửi tin nhắn đến. Bạn nên sử dụng một chủ đề duy nhất cho mỗi ứng dụng trong Chat.

  2. Cấp quyền cho Chat phát hành chủ đề bằng cách chỉ định vai trò Nhà xuất bản Pub/Sub cho tài khoản dịch vụ sau:

    chat-api-push@system.gserviceaccount.com
    
  3. Tạo tài khoản dịch vụ cho ứng dụng Chat để uỷ quyền với Pub/Sub và Chat, đồng thời lưu tệp khoá riêng tư vào thư mục đang hoạt động của bạn.

  4. Tạo gói thuê bao kéo cho chủ đề.

  5. Chỉ định Vai trò người đăng ký Pub/Sub cho gói thuê bao cho tài khoản dịch vụ mà bạn đã tạo trước đây.

Viết tập lệnh

Java

  1. Trong CLI, hãy cung cấp thông tin đăng nhập vào tài khoản dịch vụ:

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. Trong thư mục đang làm việc, hãy tạo một tệp có tên pom.xml.

  3. Trong tệp pom.xml, dán mã sau:

    <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>
    
  4. Trong thư mục đang làm việc, hãy tạo cấu trúc thư mục src/main/java.

  5. Trong thư mục src/main/java, hãy tạo một tệp có tên Main.java.

  6. Trong Main.java, dán mã sau:

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

    Thay thế các đoạn mã sau:

    • PROJECT_ID: Mã dự án trên Google Cloud.
    • SUBSCRIPTION_ID: mã nhận dạng gói thuê bao của gói thuê bao Pub/Sub mà bạn đã tạo trước đó.

Xuất bản ứng dụng lên Chat

  1. Trong bảng điều khiển Google Cloud, hãy chuyển đến Trình đơn > API và dịch vụ > API và dịch vụ đã bật > API Google Chat > Cấu hình.

    Chuyển đến trang Cấu hình

  2. Định cấu hình ứng dụng Chat cho Pub/Sub:

    1. Trong Tên ứng dụng, hãy nhập Quickstart App.
    2. Trong URL hình đại diện, hãy nhập https://developers.google.com/chat/images/quickstart-app-avatar.png.
    3. Trong phần Mô tả, hãy nhập Quickstart app.
    4. Trong phần Chức năng, hãy chọn Nhận tin nhắn 1:1 rồi chọn Tham gia không gian và cuộc trò chuyện nhóm.
    5. Trong phần Cài đặt kết nối, hãy chọn Cloud Pub/Sub rồi dán tên của chủ đề Pub/Sub mà bạn đã tạo trước đó.
    6. Trong phần Chế độ hiển thị, hãy chọn Cung cấp ứng dụng Google Chat này cho những người và nhóm cụ thể trong miền của bạn rồi nhập địa chỉ email.
    7. Trong phần Logs (Nhật ký), hãy chọn Log error to Logging (Ghi nhật ký lỗi vào nhật ký).
  3. Nhấp vào Lưu.

Ứng dụng đã sẵn sàng nhận và trả lời tin nhắn trên Chat.

Chạy tập lệnh

Trong CLI, hãy chuyển vào thư mục đang làm việc và chạy tập lệnh:

Java

mvn compile exec:java -Dexec.mainClass=Main

Khi bạn chạy mã, ứng dụng sẽ bắt đầu theo dõi các thông báo đã xuất bản lên chủ đề Pub/Sub.

Kiểm thử ứng dụng Chat

Để kiểm tra ứng dụng Chat, hãy gửi tin nhắn trực tiếp cho ứng dụng đó theo cách sau:

  1. Mở Google Chat.
  2. Để gửi tin nhắn trực tiếp đến ứng dụng, hãy nhấp vào biểu tượng Bắt đầu trò chuyện . Trong cửa sổ xuất hiện, hãy nhấp vào Tìm ứng dụng.
  3. Trong hộp thoại Find apps (Tìm ứng dụng), hãy tìm "Quickstart App" (Bắt đầu nhanh ứng dụng).
  4. Để mở một tin nhắn trực tiếp bằng ứng dụng này, hãy tìm Quickstart App (Ứng dụng khởi động nhanh) rồi nhấp vào Add (Thêm) > Chat.
  5. Trong tin nhắn trực tiếp, hãy nhập Hello rồi nhấn enter. Ứng dụng Chat sẽ đọc to tin nhắn cho bạn.

Để thêm người kiểm thử đáng tin cậy và tìm hiểu thêm về kiểm thử các tính năng tương tác, hãy xem phần Kiểm thử tính năng tương tác cho ứng dụng Google Chat.

Khắc phục sự cố

Khi ứng dụng Google Chat hoặc thẻ trả về lỗi, giao diện Chat sẽ hiển thị thông báo "Đã xảy ra lỗi." hoặc "Không thể xử lý yêu cầu của bạn". Đôi khi, giao diện người dùng của Chat không hiển thị thông báo lỗi nào, nhưng ứng dụng hoặc thẻ Chat cho ra kết quả không mong muốn; ví dụ: thông báo thẻ có thể không xuất hiện.

Mặc dù thông báo lỗi có thể không hiển thị trong giao diện người dùng Chat, nhưng chúng tôi cung cấp dữ liệu nhật ký và thông báo lỗi mô tả để giúp bạn khắc phục lỗi khi bật tính năng ghi nhật ký lỗi cho các ứng dụng trong Chat. Để được trợ giúp về việc xem, gỡ lỗi và sửa lỗi, hãy xem phần Khắc phục sự cố và sửa lỗi trên Google Chat.

Dọn dẹp

Để tài khoản Google Cloud của bạn không bị tính phí cho các tài nguyên dùng trong hướng dẫn này, bạn nên xoá dự án trên Google Cloud đó.

  1. Trong bảng điều khiển Google Cloud, hãy chuyển đến trang Quản lý tài nguyên. Nhấp vào biểu tượng Trình đơn > IAM & Admin (IAM và quản trị viên) > Quản lý tài nguyên.

    Chuyển đến Trình quản lý tài nguyên

  2. Trong danh sách dự án, hãy chọn dự án bạn muốn xoá rồi nhấp vào biểu tượng Xoá .
  3. Trong hộp thoại, hãy nhập mã dự án rồi nhấp vào Shut Down (Tắt) để xoá dự án.