สร้างแอป Google Chat อยู่เบื้องหลังไฟร์วอลล์ด้วย Pub/Sub

หน้านี้อธิบายวิธีสร้างแอป Chat โดยใช้ Pub/Sub สถาปัตยกรรมประเภทนี้สำหรับแอป Chat จะมีประโยชน์หากองค์กรมีไฟร์วอลล์ซึ่งอาจป้องกันไม่ให้ Chat ส่งข้อความไปยังแอป Chat ของคุณได้ หรือหากแอป Chat ใช้ Google Workspace Events API อย่างไรก็ตาม สถาปัตยกรรมนี้มีข้อจำกัดดังต่อไปนี้เนื่องจากแอป Chat เหล่านี้จะส่งและรับได้เฉพาะข้อความแบบไม่พร้อมกันเท่านั้น

  • ใช้กล่องโต้ตอบ ในข้อความไม่ได้ โปรดใช้ข้อความในการ์ดแทน
  • อัปเดตการ์ดแต่ละใบด้วยการตอบกลับแบบพร้อมกันไม่ได้ แต่ให้อัปเดตข้อความทั้งหมดโดยการเรียกใช้เมธอด patch แทน

แผนภาพต่อไปนี้แสดงสถาปัตยกรรมของแอป Chat ที่สร้างด้วย Pub/Sub

สถาปัตยกรรมของแอป Chat ที่ติดตั้งใช้งานกับ Pub/Sub

ในแผนภาพก่อนหน้านี้ ผู้ใช้ที่โต้ตอบกับแอป Chat Pub/Sub จะมีโฟลว์ข้อมูลต่อไปนี้

  1. ผู้ใช้ส่งข้อความใน Chat ไปยังแอปใน Chat ทั้งในข้อความส่วนตัวหรือในพื้นที่ใน Chat หรือเมื่อมีเหตุการณ์เกิดขึ้นในพื้นที่ใน Chat ที่แอป Chat มีการสมัครใช้บริการที่ใช้งานอยู่

  2. Chat จะส่งข้อความไปยังหัวข้อ Pub/Sub

  3. แอปพลิเคชันเซิร์ฟเวอร์ที่เป็นระบบคลาวด์หรือระบบภายในองค์กรที่มีตรรกะของแอป Chat จะสมัครใช้บริการหัวข้อ Pub/Sub เพื่อรับข้อความผ่านไฟร์วอลล์

  4. (ไม่บังคับ) แอป Chat สามารถเรียกใช้ Chat API เพื่อโพสต์ข้อความแบบไม่พร้อมกันหรือดำเนินการอื่นๆ

ข้อกำหนดเบื้องต้น

Java

ตั้งค่าสภาพแวดล้อม

ก่อนใช้ Google APIs คุณต้องเปิดใช้ API ในโปรเจ็กต์ Google Cloud คุณเปิด API ได้มากกว่า 1 รายการในโปรเจ็กต์ Google Cloud เดียว
  • เปิดใช้ Google Chat API และ Pub/Sub API ในคอนโซล Google Cloud

    เปิดใช้ API

ตั้งค่า Pub/Sub

  1. สร้างหัวข้อ Pub/Sub ที่ Chat API จะส่งข้อความได้ เราขอแนะนำให้ใช้หัวข้อเดียวต่อแอป Chat 1 แอป

  2. ให้สิทธิ์ใน Chat ในการเผยแพร่หัวข้อด้วยการมอบหมายบทบาทผู้เผยแพร่ Pub/Sub ให้บัญชีบริการต่อไปนี้

    chat-api-push@system.gserviceaccount.com
    
  3. สร้างบัญชีบริการสำหรับแอป Chat เพื่อให้สิทธิ์กับ Pub/Sub และ Chat และบันทึกไฟล์คีย์ส่วนตัวไปยังไดเรกทอรีการทำงาน

  4. สร้างการสมัครใช้บริการพุล สำหรับหัวข้อดังกล่าว

  5. มอบหมายบทบาทผู้สมัครใช้บริการ Pub/Sub ในการสมัครใช้บริการ สำหรับบัญชีบริการที่คุณสร้างไว้ก่อนหน้านี้

เขียนสคริปต์

Java

  1. ใน CLI ให้ระบุข้อมูลเข้าสู่ระบบบัญชีบริการดังนี้

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. สร้างไฟล์ชื่อ pom.xml ในไดเรกทอรีการทำงาน

  3. ในไฟล์ 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.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. สร้างโครงสร้างไดเรกทอรี src/main/java ในไดเรกทอรีการทำงาน

  5. ในไดเรกทอรี src/main/java ให้สร้างไฟล์ชื่อ Main.java

  6. ใน Main.java ให้วางรหัสต่อไปนี้

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

    แทนที่รายการต่อไปนี้

    • PROJECT_ID: รหัสโปรเจ็กต์ Google Cloud
    • SUBSCRIPTION_ID: รหัสการสมัครใช้บริการ สำหรับการสมัครใช้บริการ Pub/Sub ที่คุณสร้างไว้ก่อนหน้านี้

เผยแพร่แอปไปยัง Chat

  1. ในคอนโซล Google Cloud ให้ไปที่เมนู > API และบริการ > API และบริการที่เปิดใช้ > Google Chat API > การกำหนดค่า

    ไปที่การกำหนดค่า

  2. กำหนดค่าแอป Chat สำหรับ Pub/Sub ดังนี้

    1. ในชื่อแอป ให้ป้อน Quickstart App
    2. ใน URL ของรูปโปรไฟล์ ให้ป้อน 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. ในส่วนบันทึก ให้เลือกบันทึกข้อผิดพลาดในการบันทึก
  3. คลิกบันทึก

แอปพร้อมรับและตอบกลับข้อความใน Chat แล้ว

เรียกใช้สคริปต์

ใน CLI ให้เปลี่ยนเป็นไดเรกทอรีการทำงานและเรียกใช้สคริปต์:

Java

mvn compile exec:java -Dexec.mainClass=Main

เมื่อคุณเรียกใช้โค้ด แอปพลิเคชันจะเริ่มฟังข้อความที่เผยแพร่ไปยังหัวข้อ Pub/Sub

ทดสอบแอป Chat

หากต้องการทดสอบแอป Chat ให้ส่งข้อความส่วนตัวถึงแอปโดยทำดังนี้

  1. เปิด Google Chat
  2. หากต้องการส่งข้อความส่วนตัวไปยังแอป ให้คลิกเริ่มแชท แล้วคลิกค้นหาแอปในหน้าต่างที่ปรากฏขึ้น
  3. ในกล่องโต้ตอบค้นหาแอป ให้ค้นหา "แอปเริ่มต้นใช้งานอย่างรวดเร็ว"
  4. หากต้องการเปิดข้อความส่วนตัวด้วยแอป ให้ค้นหา แอปการเริ่มต้นอย่างรวดเร็ว แล้วคลิกเพิ่ม > Chat
  5. ในข้อความส่วนตัว ให้พิมพ์ Hello แล้วกด enter แอป Chat จะพูดข้อความกลับมาให้คุณ

หากต้องการเพิ่มผู้ทดสอบที่เชื่อถือได้และดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบฟีเจอร์แบบอินเทอร์แอกทีฟ โปรดดูทดสอบฟีเจอร์แบบอินเทอร์แอกทีฟสำหรับแอป Google Chat

แก้ปัญหา

เมื่อแอป Google Chat หรือการ์ดแสดงผลข้อผิดพลาด อินเทอร์เฟซ Chat จะแสดงข้อความว่า "เกิดข้อผิดพลาด" หรือ "ดำเนินการตามคำขอของคุณไม่ได้" บางครั้ง UI ของ Chat ไม่แสดงข้อความแสดงข้อผิดพลาด แต่แอปหรือการ์ด Chat จะให้ผลลัพธ์ที่ไม่คาดคิด เช่น ข้อความการ์ดอาจไม่ปรากฏ

แม้ว่าข้อความแสดงข้อผิดพลาดอาจไม่แสดงใน UI ของ Chat แต่ก็จะมีข้อความแสดงข้อผิดพลาดที่สื่อความหมายและข้อมูลบันทึกที่จะช่วยคุณแก้ไขข้อผิดพลาดเมื่อเปิดการบันทึกข้อผิดพลาดสำหรับแอป Chat ไว้ หากต้องการความช่วยเหลือในการดู แก้ไขข้อบกพร่อง และแก้ไขข้อผิดพลาด โปรดดูหัวข้อแก้ปัญหาและแก้ไขข้อผิดพลาดของ Google Chat

ล้างข้อมูล

เราขอแนะนำให้คุณลบโปรเจ็กต์ Cloud เพื่อหลีกเลี่ยงการเรียกเก็บเงินในบัญชี Google Cloud สำหรับทรัพยากรที่ใช้ในบทแนะนำนี้

  1. ในคอนโซล Google Cloud ให้ไปที่หน้าจัดการทรัพยากร คลิก เมนู > IAM และผู้ดูแลระบบ > จัดการทรัพยากร

    ไปที่เครื่องมือจัดการทรัพยากร

  2. ในรายการโปรเจ็กต์ ให้เลือกโปรเจ็กต์ที่ต้องการลบ แล้วคลิกลบ
  3. ในกล่องโต้ตอบ ให้พิมพ์รหัสโปรเจ็กต์แล้วคลิกปิดเพื่อลบโปรเจ็กต์