Używanie Cloud Pub/Sub jako punktu końcowego aplikacji

Gdy tworzysz aplikację dla swojej organizacji, być może Twoja organizacja korzysta z zapory sieciowej, która może uniemożliwiać Google Chat wysyłanie wiadomości do Twojej aplikacji. W tym celu Google Chat oferuje integrację z Cloud Pub/Sub. Ta integracja pozwala aplikacji na nawiązanie połączenia z Cloud Pub/Sub i odbieranie wiadomości.

Architektura integracji Cloud Pub/Sub z Google Chat.

Konfigurowanie Cloud Pub/Sub

Z tej sekcji dowiesz się, jak skonfigurować temat Cloud Pub/Sub i zasubskrybować go w swojej aplikacji.

Tworzenie projektu Google Cloud z włączoną usługą Pub/Sub

Aby używać Cloud Pub/Sub z aplikacją, musisz utworzyć projekt Google Cloud Platform, w którym włączony jest interfejs Pub/Sub API.

  1. Utwórz nowy projekt w konsoli Google Cloud.
  2. W panelu po lewej stronie konsoli wybierz Pub/Sub, a następnie Włącz API.

Tworzenie tematu Pub/Sub

Następnie utwórz temat, do którego interfejs Chat API ma wysyłać wiadomości. Aby dowiedzieć się, jak utworzyć temat, przeczytaj krótkie wprowadzenie do konsoli Pub/Sub.

Włączanie interfejsu Google Chat API

Pamiętaj, aby włączyć Google Chat API i ustawić go na temat utworzony w poprzednim kroku.

  1. W lewym panelu konsoli wybierz Interfejsy API i usługi i włącz Google Chat API.
  2. Podczas konfigurowania interfejsu API sprawdź, czy ustawienia połączenia są ustawione na Cloud Pub/Sub, i podaj ten sam temat z poprzedniego kroku.
  3. Wypełnij pozostałe pola zgodnie z opisem w przewodniku po przykładowej aplikacji.

Przyznawanie uprawnień do publikacji na dany temat

Aby usługa Google Chat mogła publikować wiadomości na dany temat, musi mieć prawa do publikowania na dany temat. Aby przyznać Google Chat te uprawnienia, przypisz rolę Publikujący w Pub/Sub do tego konta usługi:

chat-api-push@system.gserviceaccount.com

Umożliwi to Google Chat publikowanie postów na ten temat.

Tworzenie konta usługi

Aby kod aplikacji mógł być autoryzowany w Cloud Pub/Sub i Google Chat, musi ona korzystać z konta usługi. Aby skonfigurować konto usługi, przeczytaj artykuł Autoryzacja i uwierzytelnianie w interfejsie Chat API przy użyciu konta usługi.

Tworzenie subskrypcji

Postępuj zgodnie z przewodnikiem dla subskrybentów Cloud Pub/Sub, aby skonfigurować subskrypcję utworzonego tematu. Skonfiguruj typ subskrypcji jako pobieranie webhooka. Dodaj uprawnienia subskrypcji do konta usługi utworzonego w ostatnim kroku i nadaj mu rolę „Subskrybent Pub/Sub”.

Publikowanie aplikacji Cloud Pub/Sub

Szczegółowe informacje na temat sposobu publikowania aplikacji znajdziesz w przewodniku Publikowanie aplikacji. W przypadku aplikacji Cloud Pub/Sub wybierz opcję Cloud Pub/Sub i wpisz pełną i jednoznaczną nazwę utworzonego tematu. Nazwa tematu musi mieć taki format:

projects/PROJECT_ID/topics/TOPIC_ID

Na przykład: projects/pubsub-demo-2/topics/test-topic

Po opublikowaniu aplikacji w ten sposób użytkownicy będą mogli z niej korzystać, a aplikacja będzie otrzymywać wiadomości w skonfigurowanym przez Ciebie temacie Pub/Sub.

Z przykładu w następnej sekcji dowiesz się, jak utworzyć i uruchomić prostą aplikację, która implementuje te obiekty.

Przykładowa implementacja w aplikacji

Przykładowy kod poniżej pokazuje prostą aplikację, która używa Cloud Pub/Sub do odbierania wiadomości przychodzących. Aby wypróbować tę aplikację, musisz:

  • Edytuj wartości identyfikatora projektu i subskrypcji w klasie głównej
  • Podaj dane logowania do konta usługi zgodnie z opisem w przewodniku Pierwsze kroki z uwierzytelnianiem.

    eksport GOOGLE_APPLICATIONCREDENTIALS=<ścieżka_do_pliku_konta_usługi>

Aby uruchomić ten kod, potrzebujesz tych zależności w ścieżce klasy:

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

Ograniczenia i kwestie

Weź pod uwagę te limity Pub/Sub:

  • Otwarcie, przesłanie lub anulowanie okna wymaga synchronicznej odpowiedzi z aplikacji do obsługi czatu zawierającej DialogEventType. W związku z tym okna nie są obsługiwane przez aplikacje do obsługi czatu utworzone na podstawie architektury asynchronicznej, np. Cloud Pub/Sub. Aby obejść ten problem, użyj komunikatu z kartą zamiast okna.
  • Aplikacje do obsługi czatu utworzone przy użyciu Cloud Pub/Sub nie mogą aktualizować poszczególnych kart z odpowiedzią synchroniczną. Aby zaktualizować kartę, zaktualizuj całą wiadomość, wywołując metodę interfejsu API Patch Message.

Rozwiązywanie problemów

Gdy aplikacja lub karta do Google Chat zwróci błąd, w interfejsie czatu pojawi się komunikat „Coś poszło nie tak” lub „Nie udało się przetworzyć żądania”. Czasami w interfejsie Google Chat nie pojawia się żaden komunikat o błędzie, ale aplikacja lub karta Google Chat zwraca nieoczekiwany wynik, np. może się nie pojawić wiadomość na karcie.

Mimo że komunikat o błędzie może nie wyświetlać się w interfejsie Google Chat, dostępne są opisowe komunikaty o błędach i dane dziennika, które pomogą Ci w rozwiązywaniu problemów pojawiających się po włączeniu rejestrowania błędów w aplikacjach do obsługi czatu. Informacje o wyświetlaniu, debugowaniu i naprawianiu błędów znajdziesz w artykule Rozwiązywanie problemów z Google Chat i ich rozwiązywanie.