Compila una app de Google Chat detrás de un firewall con Pub/Sub

En esta página, se explica cómo crear una app de Chat mediante Pub/Sub. Este tipo de arquitectura de una app de Chat es útil si tu organización tiene un firewall, que puede impedir que Chat envíe mensajes a tu app de Chat o si esta usa la API de Eventos de Google Workspace. Sin embargo, esta arquitectura tiene las siguientes limitaciones debido a que estas apps de Chat solo pueden enviar y recibir mensajes asíncronos:

  • No se pueden usar diálogos en los mensajes. En su lugar, usa un mensaje de tarjeta.
  • No se pueden actualizar tarjetas individuales con una respuesta síncrona. En su lugar, actualiza todo el mensaje llamando al método patch.

En el siguiente diagrama, se muestra la arquitectura de una app de Chat compilada con Pub/Sub:

Arquitectura de una app de Chat implementada con Pub/Sub

En el diagrama anterior, un usuario que interactúa con una app de Chat de Pub/Sub tiene el siguiente flujo de información:

  1. Un usuario envía un mensaje en Chat a una app de Chat, ya sea en un mensaje directo o en un espacio de Chat, o bien un evento en un espacio de Chat en el que la app de Chat tiene una suscripción activa.

  2. Chat envía el mensaje a un tema de Pub/Sub.

  3. Un servidor de aplicaciones, que es un sistema local o en la nube que contiene la lógica de la app de Chat, se suscribe al tema de Pub/Sub para recibir el mensaje a través del firewall.

  4. De manera opcional, la app de Chat puede llamar a la API de Chat para publicar mensajes de forma asíncrona o realizar otras operaciones.

Requisitos previos

Java

Configure el entorno

Antes de usar las APIs de Google, debes activarlas en un proyecto de Google Cloud. Puedes activar una o más APIs en un solo proyecto de Google Cloud.
  • En la consola de Google Cloud, habilita la API de Google Chat y la API de Pub/Sub.

    Habilita las APIs

Cómo configurar Pub/Sub

  1. Crea un tema de Pub/Sub al que la API de Chat pueda enviar mensajes. Te recomendamos que uses un solo tema por app de Chat.

  2. Otorga a Chat permiso para publicar en el tema asignando el rol de Publicador de Pub/Sub a la siguiente cuenta de servicio:

    chat-api-push@system.gserviceaccount.com
    
  3. Crea una cuenta de servicio para que la app de Chat la autorice con Pub/Sub y Chat, y guarda el archivo de claves privadas en tu directorio de trabajo.

  4. Crea una suscripción de extracción al tema.

  5. Asigna la función de suscriptor de Pub/Sub en la suscripción para la cuenta de servicio que creaste antes.

Escribe el guion

Java

  1. En una CLI, proporciona credenciales de cuenta de servicio:

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. En el directorio de trabajo, crea un archivo llamado pom.xml.

  3. En el archivo pom.xml, pega el siguiente código:

    <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. En el directorio de trabajo, crea la estructura del directorio src/main/java.

  5. En el directorio src/main/java, crea un archivo llamado Main.java.

  6. En Main.java, pega el siguiente código:

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

    Reemplaza lo siguiente:

    • PROJECT_ID: el ID del proyecto de Google Cloud.
    • SUBSCRIPTION_ID: Es el ID de la suscripción de Pub/Sub que creaste antes.

Publica la app en Chat

  1. En la consola de Google Cloud, ve a Menú > APIs y servicios > APIs y servicios habilitados > API de Google Chat > Configuración.

    Ir a Configuración

  2. Configura la app de Chat para Pub/Sub:

    1. En Nombre de la app, ingresa Quickstart App.
    2. En URL del avatar, escribe https://developers.google.com/chat/images/quickstart-app-avatar.png.
    3. En Descripción, ingresa Quickstart app.
    4. En Funcionalidad, selecciona Recibir mensajes 1:1 y Unirse a espacios y conversaciones grupales.
    5. En Configuración de la conexión, selecciona Cloud Pub/Sub y pega el nombre del tema de Pub/Sub que creaste antes.
    6. En Visibilidad, selecciona Hacer que esta app de Google Chat esté disponible para personas y grupos específicos de tu dominio y, luego, ingresa tu dirección de correo electrónico.
    7. En Registros, selecciona Registrar errores en Logging.
  3. Haz clic en Guardar.

La app está lista para recibir y responder mensajes en Chat.

Ejecuta la secuencia de comandos:

En una CLI, cambia al directorio de trabajo y ejecuta la secuencia de comandos:

Java

mvn compile exec:java -Dexec.mainClass=Main

Cuando ejecutas el código, la aplicación comienza a escuchar los mensajes publicados en el tema de Pub/Sub.

Prueba tu app de Chat

Para probar tu app de Chat, envía un mensaje directo a la app:

  1. Abre Google Chat.
  2. Para enviar un mensaje directo a la app, haz clic en Iniciar un chat y, en la ventana que aparece, haz clic en Buscar apps.
  3. En el cuadro de diálogo Buscar apps, busca “App de inicio rápido”.
  4. Para abrir un mensaje directo con la app, busca la Guía de inicio rápido de App y haz clic en Agregar > Chat.
  5. En el mensaje directo, escribe Hello y presiona enter. La app de Chat te repetirá el mensaje.

Si quieres agregar verificadores de confianza y obtener más información para probar las funciones interactivas, consulta Prueba las funciones interactivas en las apps de Google Chat.

Solución de problemas

Cuando una app de Google Chat o una tarjeta devuelve un error, la interfaz de Chat muestra un mensaje que dice “Se produjo un error” o “No se pudo procesar tu solicitud”. A veces, la IU de Chat no muestra ningún mensaje de error, pero la app o la tarjeta de Chat producen un resultado inesperado; por ejemplo, es posible que no aparezca un mensaje de tarjeta.

Si bien es posible que no se muestre un mensaje de error en la IU de Chat, hay mensajes de error descriptivos y datos de registro disponibles que te ayudarán a corregir errores cuando se activa el registro de errores para las apps de Chat. Si necesitas ayuda para ver, depurar y corregir errores, consulta Cómo solucionar problemas de Google Chat.