Recibe y responde eventos de Google Chat

En esta página, se describe cómo tu app de Google Chat puede recibir, procesar y responder eventos de Google Chat.

Cuando los usuarios interactúan con las apps de Google Chat, la app recibe de forma síncrona y puede responder a un evento. Algunos ejemplos de tipos de eventos incluyen los mensajes (incluidos los comandos de barra y las @menciones), los clics en tarjetas y la manera en que se agregan o quitan elementos de un espacio.

Las apps de chat pueden responder a estos eventos de muchas maneras. Por ejemplo, las apps de chat pueden enviar un mensaje de texto o un mensaje de tarjeta, cada uno de los cuales está representado como un objeto JSON.

Mensajes de texto

Los mensajes de texto son ideales para recibir notificaciones simples. Admiten @menciones y formatos básicos como negrita, cursiva y code.

Por ejemplo, una app podría usar un mensaje de texto para notificar a los desarrolladores de software que se acerca la suspensión de código:

Ejemplo de mensaje de texto en Google Chat que anuncia la suspensión del código
Figura 1: Un mensaje de texto alerta a un espacio de Chat sobre una suspensión del código.

Para obtener más información, consulta Enviar un mensaje de texto.

Mensajes de la tarjeta

Los mensajes de tarjeta admiten un diseño definido, elementos de IU interactivos, como botones, y rich media, como imágenes. Usa los mensajes de tarjeta para presentar información detallada, recopilar información de los usuarios y guiarlos para dar el siguiente paso. Los mensajes de tarjeta pueden aparecer en un flujo de conversación como un mensaje por sí solo o anexado a un mensaje de texto, o como ventanas de diálogo que se abren en una conversación.

Por ejemplo, una app puede usar un mensaje de tarjeta para ejecutar una encuesta:

Ejecutar una encuesta en un espacio de Chat con un mensaje de tarjeta
Figura 2: El mensaje de una tarjeta permite que las personas en un espacio de Chat voten en una encuesta.

Para ayudar a los usuarios a completar procesos de varios pasos, como completar los datos del formulario, las tarjetas se pueden unir de manera secuencial en un diálogo. Se abren diálogos en ventanas que permiten que las apps interactúen con un usuario directamente.

Por ejemplo, una app puede iniciar un diálogo para recopilar detalles de contacto:

Un cuadro de diálogo con una variedad de widgets diferentes.
Figura 3: Un diálogo abierto que le solicita al usuario que agregue un contacto.

Para obtener más información, consulta Cómo enviar un mensaje de tarjeta.

Tipos de extremos

Para recibir y responder eventos de Google Chat, debes especificar un extremo del servicio en la configuración de tu app de Google Chat. Puedes usar cualquiera de los siguientes tipos de extremos:

  • Los extremos HTTPS presentan tu aplicación como un servicio web. Debes configurar un servidor web para usarlo como interfaz para la implementación de tu app. Tu app puede responder a estos eventos de forma síncrona o asíncrona.
  • Los extremos de Google Cloud Pub/Sub usan un tema en Google Cloud Pub/Sub para retransmitir un evento a la implementación de tu app. Esto es útil cuando tu implementación está detrás de un firewall. Las apps que usan extremos de Pub/Sub solo pueden responder de forma asíncrona y requieren una cuenta de servicio.
  • Los extremos de DialogFlow permiten que tu app use las capacidades de procesamiento de lenguaje natural (PLN) de DialogFlow. Para obtener más detalles, consulta la documentación de DialogFlow.

Para una arquitectura simple y directa, intenta implementar una aplicación con un extremo HTTPS (un servicio web, básicamente) que responda de manera síncrona y que siempre retenga su carga útil en la respuesta HTTPS POST. Este enfoque no implica autorización, por lo que no necesita una cuenta de servicio. Consulta la sección Implementación simple de apps a continuación para ver un ejemplo de este estilo.

Las aplicaciones que responden de forma asíncrona, lo que incluye todas las aplicaciones en los extremos de Pub/Sub, requieren una cuenta de servicio para autorizar con Google Chat. Es posible que debas adoptar un enfoque más complejo si tu app está detrás de un firewall o envía mensajes no solicitados, como alarmas y otras notificaciones a Google Chat.

Una implementación de app muy sencilla

El siguiente código implementa una aplicación simple en Python con el marco de trabajo web Flask.

#!/usr/bin/env python3
"""Example app that returns a synchronous response."""

from flask import Flask, request, json


app = Flask(__name__)


@app.route('/', methods=['POST'])
def on_event():
  """Handles an event from Google Chat."""
  event = request.get_json()
  if event['type'] == 'ADDED_TO_SPACE' and not event['space']['singleUserBotDm']:
    text = 'Thanks for adding me to "%s"!' % (event['space']['displayName'] if event['space']['displayName'] else 'this chat')
  elif event['type'] == 'MESSAGE':
    text = 'You said: `%s`' % event['message']['text']
  else:
    return
  return json.jsonify({'text': text})


if __name__ == '__main__':
  app.run(port=8080, debug=True)

Debido a que se trata de un servicio web, la aplicación presenta un extremo HTTPS y no necesita usar Cloud Pub/Sub para retransmitir eventos a él. Y, como siempre muestra su carga útil de respuesta dentro de la respuesta JSON, no necesita autenticarse con una cuenta de servicio.

Controla eventos desde Google Chat

En esta sección, se describe cómo recibir y procesar eventos que tu app recibe de Google Chat.

Registra la app

Antes de que tu app pueda recibir eventos de Google Chat, debes especificar su extremo en la pestaña de configuración de la API de Chat cuando publiques tu app.

Una vez que registres el extremo y publiques tu app, Google Chat reconocerá los eventos dirigidos a tu app y los enviará al extremo especificado.

Verifica la autenticidad de la app

Una vez que registraste la app HTTPS, necesitas un método para que la implementación verifique si la solicitud proviene realmente de Google.

Google Chat incluye un token del portador en el encabezado Authorization de cada solicitud HTTPS a una app. Por ejemplo:

POST
Host: yourappurl.com
Authorization: Bearer AbCdEf123456
Content-Type: application/json
User-Agent: Google-Dynamite

La string AbCdEf123456 en el ejemplo anterior es el token de autorización del portador. Este es un token criptográfico producido por Google. Puedes verificar el token del portador con una biblioteca cliente de código abierto de la API de Google:

Todos los tokens del portador enviados con las solicitudes de Google Chat tendrán chat@system.gserviceaccount.com como el emisor, con el campo audience que especifica el número de proyecto de la app de destino desde Google Cloud Console. Por ejemplo, si la solicitud es para una app con el número de proyecto 1234567890, el público es 1234567890.

Debes verificar que la solicitud provenga de Google y que está destinada a la app de destino. Si el token no se verifica, la app debe responder a la solicitud con un código de respuesta HTTPS 401 (Unauthorized).

Java

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.auth.oauth2.GooglePublicKeysManager;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;

/** Tool for verifying JWT Tokens for Apps in Google Chat. */
public class JWTVerify {
  // Bearer Tokens received by apps will always specify this issuer.
  static String CHAT_ISSUER = "chat@system.gserviceaccount.com";

  // Url to obtain the public certificate for the issuer.
  static String PUBLIC_CERT_URL_PREFIX =
      "https://www.googleapis.com/service_accounts/v1/metadata/x509/";

  // Intended audience of the token, which will be the project number of the app.
  static String AUDIENCE = "1234567890";

  // Get this value from the request's Authorization HTTPS header.
  // For example, for "Authorization: Bearer AbCdEf123456" use "AbCdEf123456"
  static String BEARER_TOKEN = "AbCdEf123456";

  public static void main(String[] args) throws GeneralSecurityException, IOException {
    JsonFactory factory = new JacksonFactory();

    GooglePublicKeysManager.Builder keyManagerBuilder =
        new GooglePublicKeysManager.Builder(new ApacheHttpTransport(), factory);

    String certUrl = PUBLIC_CERT_URL_PREFIX + CHAT_ISSUER;
    keyManagerBuilder.setPublicCertsEncodedUrl(certUrl);

    GoogleIdTokenVerifier.Builder verifierBuilder =
        new GoogleIdTokenVerifier.Builder(keyManagerBuilder.build());
    verifierBuilder.setIssuer(CHAT_ISSUER);
    GoogleIdTokenVerifier verifier = verifierBuilder.build();

    GoogleIdToken idToken = GoogleIdToken.parse(factory, BEARER_TOKEN);
    if (idToken == null) {
      System.out.println("Token cannot be parsed");
      System.exit(-1);
    }

    // Verify valid token, signed by CHAT_ISSUER.
    if (!verifier.verify(idToken)
        || !idToken.verifyAudience(Collections.singletonList(AUDIENCE))
        || !idToken.verifyIssuer(CHAT_ISSUER)) {
      System.out.println("Invalid token");
      System.exit(-1);
    }

    // Token originates from Google and is targeted to a specific client.
    System.out.println("The token is valid");
  }
}

Python

import sys

from oauth2client import client

# Bearer Tokens received by apps will always specify this issuer.
CHAT_ISSUER = 'chat@system.gserviceaccount.com'

# Url to obtain the public certificate for the issuer.
PUBLIC_CERT_URL_PREFIX = 'https://www.googleapis.com/service_accounts/v1/metadata/x509/'

# Intended audience of the token, which will be the project number of the app.
AUDIENCE = '1234567890'

# Get this value from the request's Authorization HTTPS header.
# For example, for 'Authorization: Bearer AbCdEf123456' use 'AbCdEf123456'.
BEARER_TOKEN = 'AbCdEf123456'

try:
  # Verify valid token, signed by CHAT_ISSUER, intended for a third party.
  token = client.verify_id_token(
      BEARER_TOKEN, AUDIENCE, cert_uri=PUBLIC_CERT_URL_PREFIX + CHAT_ISSUER)

  if token['iss'] != CHAT_ISSUER:
    sys.exit('Invalid issuee')
except:
  sys.exit('Invalid token')

# Token originates from Google and is targeted to a specific client.
print 'The token is valid'

Carga útil del evento

Cuando tu app recibe un evento de Google Chat, el evento incluye un cuerpo de solicitud: esta es la carga útil de JSON que representa el evento. El cuerpo de la solicitud siempre incluye la siguiente información:

  • type: una string que especifica el tipo de evento.
  • eventTime: una string que contiene la marca de tiempo del evento.

La información adicional que se incluye en el cuerpo de la solicitud depende del tipo de evento. En el siguiente ejemplo, se muestra una carga útil posible:

{
  "type": "MESSAGE",
  "eventTime": "2017-03-02T19:02:59.910959Z",
  "space": {
    "name": "spaces/AAAAAAAAAAA",
    "displayName": "Best Dogs Discussion Space",
    "type": "ROOM"
  },
  "message": {
    "name": "spaces/AAAAAAAAAAA/messages/CCCCCCCCCCC",
    "sender": {
      "name": "users/12345678901234567890",
      "displayName": "Chris Corgi",
      "avatarUrl": "https://lh3.googleusercontent.com/.../photo.jpg",
      "email": "chriscorgi@example.com"
    },
    "createTime": "2017-03-02T19:02:59.910959Z",
    "text": "I mean is there any good reason their legs should be longer?",
    "thread": {
      "name": "spaces/AAAAAAAAAAA/threads/BBBBBBBBBBB"
    }
  }
}

Consulta la referencia de formatos de eventos para obtener detalles sobre los diferentes tipos de eventos y sus formatos de solicitud.

Procesa el evento

Cuando tu app recibe un evento de Google Chat, lo que hace con ese evento depende de tu implementación. La app puede buscar información de una fuente de datos, registrar la información del evento o casi cualquier otro elemento. Este comportamiento de procesamiento es básicamente lo que define la app.

Por lo general, las apps de Google Chat procesan la información que contienen los eventos y generan una respuesta a la conversación que los emitió. En el siguiente diagrama, se describe una interacción típica con una app en un espacio de Chat:

Arquitectura del procesamiento de eventos de una app

En el diagrama anterior, se muestran tres tipos de eventos: ADDED_TO_SPACE, MESSAGE y REMOVED_FROM_SPACE. Una app no puede responder después de que se la quita de un espacio, pero sí puede responder a los otros dos tipos.

Responde de forma síncrona

Una app puede responder a un evento de manera síncrona mostrando una carga útil de mensaje con formato JSON en la respuesta HTTPS. El plazo para una respuesta síncrona es de 30 segundos.

Una respuesta síncrona de una app siempre se publica en el subproceso que generó el evento.

Responde de forma asíncrona

Si una app necesita responder un mensaje de un usuario más allá del plazo de 30 segundos (por ejemplo, es posible que deba realizar un informe después de completar una tarea de larga duración), puede responder de manera asíncrona mediante la creación de un mensaje con la API de Google Chat.

Reintentar

Si una solicitud HTTPS a tu app falla (como un tiempo de espera, una falla temporal de la red o un código de estado HTTPS que no es 2xx), Google Chat reintenta la entrega dos veces, con un retraso de al menos diez segundos entre cada reintento. Como resultado, una app puede recibir el mismo mensaje hasta tres veces en determinadas situaciones. Si la solicitud se completa correctamente, pero muestra una carga útil de mensaje no válida, Google Chat no volverá a intentar la solicitud.