Creating new bots

This page describes how you can create new bots that receive, process, and respond to events from Hangouts Chat:

  • Receive messages and other kinds of events generated by Hangouts Chat
  • Send event responses and other messages into Hangouts Chat

Endpoint types

Events from Hangouts Chat are delivered to your bot via an endpoint, of which there are different types:

  • HTTP endpoints present your bot as a web service. You'll need to set up a web server to use as an interface for your bot's implementation. Your bot can respond synchronously or asynchronously to these events.
  • Google Cloud Pub/Sub endpoints use a topic on Google Cloud Pub/Sub to relay an event to your bot's implementation. This is useful when your implementation is behind a firewall. Bots that use pub/sub endpoints can only respond asynchronously.

For a simple, straightforward bot architecture, try implementing a bot using an HTTP endpoint (a web service, essentially) that responds synchronously, always enclosing its payload in the HTTP POST response. This approach does not involve authorization, so it doesn't need a service account. See the simple bot implementation section below for an example of this style of bot.

You may need to take a more complex approach if your bot is behind a firewall or sends unsolicited messages such as alarms or other notifications to Hangouts Chat.

tl;dr... A very simple bot implementation

The following code implements a simple bot in Python using the Flask web framework.

#!/usr/bin/env python3
"""Example bot 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 Hangouts Chat."""
  event = request.get_json()
  if event['type'] == 'ADDED_TO_SPACE' and event['space']['type'] == 'ROOM':
    text = 'Thanks for adding me to "%s"!' % event['space']['displayName']
  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)

Because it's a web service, the bot presents an HTTP endpoint and doesn't need to use Cloud Pub/Sub to relay events to it. And because it always returns its response payload within the JSON response, it doesn't need to authenticate using a service account.

Handling events from Hangouts Chat

This section describes how to receive and process events that your bot receives from Hangouts Chat.

Registering the bot

Before your bot can receive events from Hangouts Chat, you must specify its endpoint in the Chat API configuration tab when you publish your bot.

Once you've registered the endpoint and published your bot, Hangouts Chat will recognize events addressed to your bot and dispatch them to the specified endpoint.

For security purposes, make sure that your endpoint URL conforms with the following properties:

  • It should be long
  • It should be hard to guess
  • It should be a secret between you and Google

For example, instead registering bot.myservice.com, you should register a more complex URL like this:

bot.myservice.com/Zjn53rTGRQwisaYFyT0XZyiOCh7rZUPGx1A

This helps to deter spoofing attacks on your service.

Verifying bot authenticity

Once you've registered your HTTP bot, you need a way for your implementation to verify that the request is actually coming from Google. You can do this using the verification token that appears in the Developer console. You should copy this token and store it in a safe place.

All requests coming from Google will contain this secret token. Before responding to a request, you should verify that it contains this secret token, as shown in the following JSON payload:

{
  "type": "MESSAGE",
  “token”: “E6jGWpS_mIoszAXSsGYPAtfgjiIbES1IhvHBYBJrNcg=”
  "eventTime": "2017-03-02T19:02:59.910959Z",
  "space": {
    "name": "spaces/AAAAAAAAAAA",
    "displayName": "Chuck Norris Discussion Room",
    "type": "ROOM"
  },
  "message": {
    "name": "spaces/AAAAAAAAAAA/messages/CCCCCCCCCCC",
    "sender": {
      "name": "users/12345678901234567890",
      "displayName": "Chuck Norris",
      "avatarUrl": "https://lh3.googleusercontent.com/.../photo.jpg",
      "email": "chuck@example.com"
    },
    "createTime": "2017-03-02T19:02:59.910959Z",
    "text": "Violence is my last option.",
    "thread": {
      "name": "spaces/AAAAAAAAAAA/threads/BBBBBBBBBBB"
    }
  }
}

Event payload

When your bot receives an event from Hangouts Chat, the event includes a request body: this is the JSON payload that represents the event. The request body always includes the following information:

  • type: A string that specifies the type of the event.
  • eventTime: A string containing the event timestamp.

Additional information contained in the request body depends on the event type. The following example shows a possible payload:

{
  "type": "MESSAGE",
  "eventTime": "2017-03-02T19:02:59.910959Z",
  "space": {
    "name": "spaces/AAAAAAAAAAA",
    "displayName": "Best Dogs Discussion Room",
    "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"
    }
  }
}

See the event formats reference for details of the different event types and their request formats.

Processing the event

When your bot receives an event from Hangouts Chat, what it does with that event is completely implementation dependent. The bot may look up some information from a data source, record the event information, or just about anything else. This processing behavior is essentially what defines the bot.

In most cases, a bot will not only process the information contained in the event, but will generate a response back to the thread that issued the event. The following diagram describes a typical interaction with a bot in a chat room:

There are three kinds of events shown in the above diagram: ADDED_TO_SPACE, MESSAGE, and REMOVED_FROM_SPACE. A bot can't respond after being removed from a room, but it can respond to the other two types.

Responding synchronously

A bot can respond to an event synchronously by returning a JSON-formatted message payload in the HTTP response. The deadline for a synchronous response is 30 seconds.

A synchronous response from a bot is always posted in the thread that generated the event to the bot.

Responding asynchronously

If a bot needs to respond to a user message beyond the 30-second deadline (for example, it may need to report back after completing a long-running task), it can respond asynchronously. This is exactly like sending a spontaneous message as described in the into an existing thread section.

Lightweight bots that don't use service accounts cannot respond asynchronously.

Retry

If an HTTP request to your bot fails (e.g. timeout, temporary network failure, or a non-2xx HTTP status code), Hangouts Chat will additionally retry delivery twice, with at least a ten-second delay between each retry. As a result, a bot may receive the same message up to three times in certain situations. No retry is attempted if the request completes successfully but returns an invalid message payload.

Bot-initiated messages

This section describes how bots can send arbitrary messages into a space.

Many bots send messages only in direct response to an event that they receive from Hangouts Chat. However, some bots might send messages when triggered by other things, for example:

  • A time-based alarm like a calendar event
  • A change in state of some relevant data
  • The completion of a remote process

This section describes how to send these messages from your app to Hangouts Chat.

Into an existing thread

To send a message as a reply in an existing thread, specify the thread's ID in the message payload as shown below:

{
  "text": "...",
  "thread": {
     "name": "spaces/SPACE_ID/threads/THREAD_ID"
  }
}

The specific THREAD_ID is available in the payload of MESSAGE events that your bot receives from Hangouts Chat. Keep track of this ID so that the bot can inject messages into the thread.

As a new thread

To send a message into Hangouts Chat as a new thread, your bot should omit the thread ID, as shown below:

https://chat.googleapis.com/v1/spaces/SPACE_ID/messages

Requests must specify Content-Type: application/json in the request header. See the Hangouts Chat API Message Format reference for the JSON format of Hangouts Chat messages. The following example shows a simple request using cURL:

curl -X POST \
    -H 'Content-Type: application/json' \
    'https://chat.googleapis.com/....' \
    -d '{"text": "Hello!"}'

Thread key

In many cases, bots may want to post multiple messages related to the same entity into the same thread. For example, a bug tracker integration may want to post all notification messages related to the same bug into the same thread.

To achieve this, bots can specify an arbitrary thread key in each request. Messages posted with the same thread key will be grouped into the same thread. For example, the example bug tracker integration above might use the bug ID as part of a consistent thread key. The first notification message for a bug will then create a new thread; all subsequent messages for the same bug will be posted into that same thread.

The thread key is specified in the threadKey query parameter in an inbound HTTP request. For instance:

https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?\
    threadKey=ARBITRARY_STRING

Thread keys are also scoped to a specific bot; if two different bots happen to both post messages using the same thread key, those two messages will not be grouped into the same thread.

Send feedback about...

Hangouts Chat API
Hangouts Chat API
Need help? Visit our support page.