Menerima dan merespons acara Google Chat

Halaman ini menjelaskan cara aplikasi Google Chat dapat menerima, memproses, dan merespons peristiwa dari Google Chat.

Saat pengguna berinteraksi dengan aplikasi Google Chat, aplikasi akan secara sinkron menerima dan dapat merespons peristiwa. Contoh jenis peristiwa mencakup pesan (termasuk perintah garis miring dan @sebutan), klik kartu, serta ditambahkan ke atau dihapus dari ruang.

Aplikasi chat dapat merespons acara ini dengan berbagai cara. Misalnya, aplikasi Chat dapat mengirim pesan teks atau pesan kartu, yang masing-masing direpresentasikan sebagai objek JSON.

SMS

SMS sangat cocok untuk notifikasi sederhana. Format tersebut mendukung @sebutan dan pemformatan dasar seperti tebal, miring, dan code.

Misalnya, aplikasi mungkin menggunakan SMS untuk memberi tahu developer software bahwa pembekuan kode semakin dekat:

Contoh SMS di Google Chat yang mengumumkan pembekuan kode
Gambar 1: Pesan teks akan memberi tahu ruang Chat tentang pembekuan kode.

Untuk mempelajari lebih lanjut, lihat Mengirim SMS.

Pesan kartu

Pesan kartu mendukung tata letak yang ditentukan, elemen UI interaktif seperti tombol, dan multimedia seperti gambar. Gunakan pesan kartu untuk menyajikan informasi detail, mengumpulkan informasi dari pengguna, dan memandu pengguna untuk melakukan langkah berikutnya. Pesan kartu dapat muncul dalam aliran percakapan sebagai pesan itu sendiri atau ditambahkan ke pesan teks, atau sebagai jendela dialog yang terbuka melalui percakapan.

Misalnya, aplikasi mungkin menggunakan pesan kartu untuk melakukan polling:

Menjalankan polling di ruang Chat dengan pesan kartu
Gambar 2: Pesan kartu memungkinkan orang di ruang Chat memberikan suara dalam polling.

Untuk membantu pengguna menyelesaikan proses multi-langkah, seperti mengisi data formulir, kartu dapat dirangkai secara berurutan dalam dialog. Dialog terbuka di jendela yang memungkinkan aplikasi berinteraksi dengan pengguna secara langsung.

Misalnya, aplikasi mungkin memulai dialog untuk mengumpulkan detail kontak:

Dialog yang menampilkan berbagai widget yang berbeda.
Gambar 3: Dialog terbuka yang meminta pengguna untuk menambahkan kontak.

Untuk mempelajari lebih lanjut, lihat Mengirim pesan kartu.

Jenis endpoint

Untuk menerima dan merespons peristiwa Google Chat, Anda perlu menentukan endpoint layanan di konfigurasi aplikasi Google Chat. Anda dapat menggunakan salah satu jenis endpoint berikut:

  • Endpoint HTTPS menampilkan aplikasi Anda sebagai layanan web. Anda harus menyiapkan server web yang akan digunakan sebagai antarmuka untuk penerapan aplikasi Anda. Aplikasi Anda dapat merespons peristiwa secara sinkron atau secara asinkron.
  • Endpoint Google Cloud Pub/Sub menggunakan topik di Google Cloud Pub/Sub untuk menyampaikan peristiwa ke penerapan aplikasi Anda. Hal ini berguna saat penerapan Anda berada di belakang firewall. Aplikasi yang menggunakan endpoint pub/sub hanya dapat merespons secara asinkron dan memerlukan akun layanan.
  • Endpoint DialogFlow memungkinkan aplikasi Anda menggunakan kemampuan pemrosesan bahasa alami (NLP) DialogFlow. Untuk mengetahui detailnya, lihat dokumentasi DialogFlow.

Untuk arsitektur aplikasi yang sederhana dan simpel, coba implementasikan aplikasi menggunakan endpoint HTTPS (pada dasarnya, layanan web) yang merespons secara sinkron, selalu menyertakan payload-nya dalam respons POST HTTPS. Pendekatan ini tidak melibatkan otorisasi, sehingga tidak memerlukan akun layanan. Lihat bagian penerapan aplikasi sederhana di bawah untuk mengetahui contoh gaya aplikasi ini.

Aplikasi yang merespons secara asinkron, yang mencakup semua aplikasi di endpoint pub/sub, memerlukan akun layanan untuk memberi otorisasi dengan Google Chat. Anda mungkin perlu mengambil pendekatan yang lebih kompleks jika aplikasi Anda di belakang firewall atau mengirim pesan yang tidak diinginkan seperti alarm atau notifikasi lainnya ke Google Chat.

Implementasi aplikasi yang sangat sederhana

Kode berikut menerapkan aplikasi sederhana dengan Python menggunakan framework 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)

Karena merupakan layanan web, aplikasi menampilkan endpoint HTTPS dan tidak perlu menggunakan Cloud Pub/Sub untuk menyampaikan peristiwa ke dalamnya. Dan karena selalu menampilkan payload responsnya dalam respons JSON, payload tidak perlu melakukan autentikasi menggunakan akun layanan.

Menangani peristiwa dari Google Chat

Bagian ini menjelaskan cara menerima dan memproses peristiwa yang diterima aplikasi Anda dari Google Chat.

Mendaftarkan aplikasi

Sebelum aplikasi dapat menerima peristiwa dari Google Chat, Anda harus menentukan endpoint-nya di tab konfigurasi Chat API saat memublikasikan aplikasi.

Setelah Anda mendaftarkan endpoint dan memublikasikan aplikasi, Google Chat akan mengenali peristiwa yang ditujukan ke aplikasi Anda dan mengirimkannya ke endpoint yang ditentukan.

Memverifikasi keaslian aplikasi

Setelah mendaftarkan aplikasi HTTPS, Anda memerlukan cara agar penerapan dapat memverifikasi bahwa permintaan tersebut benar-benar berasal dari Google.

Google Chat menyertakan token pemilik di header Authorization dari setiap Permintaan HTTPS ke aplikasi. Misalnya:

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

String AbCdEf123456 pada contoh di atas adalah token otorisasi pemilik. Ini adalah token kriptografi yang dihasilkan oleh Google. Anda dapat memverifikasi token pemilik menggunakan library klien Google API open source:

Semua token pemilik yang dikirim dengan permintaan dari Google Chat akan memiliki chat@system.gserviceaccount.com sebagai masalah, dengan kolom audience yang menentukan nomor project aplikasi target dari Google Cloud Console. Misalnya, jika permintaan ditujukan untuk aplikasi dengan nomor project 1234567890, audiensnya adalah 1234567890.

Anda harus memverifikasi bahwa permintaan tersebut berasal dari Google dan ditujukan untuk aplikasi target. Jika token tidak diverifikasi, aplikasi harus merespons permintaan tersebut dengan kode respons 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'

Payload peristiwa

Saat aplikasi Anda menerima peristiwa dari Google Chat, peristiwa tersebut akan menyertakan isi permintaan: ini adalah payload JSON yang mewakili peristiwa tersebut. Isi permintaan selalu menyertakan informasi berikut:

  • type: String yang menentukan jenis peristiwa.
  • eventTime: String yang berisi stempel waktu peristiwa.

Informasi tambahan yang terdapat dalam isi permintaan bergantung pada jenis peristiwa. Contoh berikut menunjukkan kemungkinan payload:

{
  "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"
    }
  }
}

Lihat referensi format peristiwa untuk mengetahui detail berbagai jenis peristiwa dan format permintaannya.

Memproses peristiwa

Saat aplikasi Anda menerima peristiwa dari Google Chat, hal yang dilakukannya dengan peristiwa tersebut bergantung pada penerapan Anda. Aplikasi ini dapat mencari beberapa informasi dari sumber data, mencatat informasi peristiwa, atau apa saja. Perilaku pemrosesan ini pada dasarnya adalah hal yang mendefinisikan aplikasi.

Aplikasi Google Chat biasanya memproses informasi yang terdapat dalam peristiwa dan membuat respons kembali ke thread yang mengeluarkan peristiwa tersebut. Diagram berikut menjelaskan interaksi umum dengan aplikasi di ruang Chat:

Arsitektur pemrosesan peristiwa aplikasi.

Ada tiga jenis peristiwa yang ditampilkan dalam diagram di atas: ADDED_TO_SPACE, MESSAGE, dan REMOVED_FROM_SPACE. Aplikasi tidak dapat merespons setelah dihapus dari ruang, tetapi dapat merespons dua jenis lainnya.

Merespons secara sinkron

Aplikasi dapat merespons peristiwa secara sinkron dengan menampilkan payload pesan berformat JSON dalam respons HTTPS. Batas waktu respons sinkron adalah 30 detik.

Respons sinkron dari aplikasi selalu diposting di thread yang menghasilkan peristiwa ke aplikasi.

Merespons secara asinkron

Jika aplikasi perlu merespons pesan pengguna setelah batas waktu 30 detik (misalnya, aplikasi mungkin perlu melaporkan kembali setelah menyelesaikan tugas yang berjalan lama), aplikasi dapat merespons secara asinkron dengan membuat pesan dengan Google Chat API.

Coba lagi

Jika permintaan HTTPS ke aplikasi Anda gagal (seperti waktu tunggu, kegagalan jaringan sementara, atau kode status HTTPS non-2xx), Google Chat akan mencoba kembali pengiriman dua kali, dengan penundaan setidaknya sepuluh detik di antara setiap percobaan ulang. Akibatnya, aplikasi mungkin menerima pesan yang sama hingga tiga kali dalam situasi tertentu. Jika permintaan berhasil diselesaikan, tetapi menampilkan payload pesan yang tidak valid, Google Chat tidak akan mencoba lagi permintaan tersebut.