An introduction to building Google Hangouts Chat bots with Google Apps Script

This post is designed to give an overview of Google Hangouts Chat bot development with Google Apps Script and is designed to complement the Totally Unscripted episode – Building Google Hangouts Chat bots with Google Apps Script.

Background

Google announced the general availability for G Suite users of Google Hangouts Chat in February 2018:

Hangouts Chat makes it easy for teams to be able to get their work done in one place. From direct messages to group conversations, Chat helps teams collaborate easily and efficiently. With dedicated, virtual rooms to house projects over time — plus threaded conversations — Chat makes it simple to track progress and follow up tasks.

More recently Google have announced that all G Suite users will be migrated away from the chat available in classic Hangouts to Hangouts Chat by October 2019, and consumer accounts (gmail.com users) will start transitioning after (Google 9to5Google report).

Along with the general availability announcement, Google also announced the availability of the Google Hangouts Chat API. With this Google highlighted:

developers can use any language, any stack, or any cloud to create and host their bot implementations. Bots only need to be able to accept HTTP POST requests coming from the Hangouts Chat service to function. [see source]

As part of the Google Hangouts Chat API launch native support was included for Google Apps Script, providing a quick and simple way for developers to create bots to interact with users, Google highlighting that:

You don’t need to worry about running and managing servers, ongoing maintenance or operational costs, or even downloading and setting up a development environment.

To help you decide if using Google Apps Script is going to be the best platform for developing your project Google have provided guidance on various bot architectures.

Apps Script bot concepts

To help you get started with your Google Hangouts Chat bot below are some key concepts taken mostly from the official Developing bots with Apps Script documentation.

Events

There are four EventType you can hook into within your code:

/**
 * Responds to an ADDED_TO_SPACE event in Hangouts Chat.
 * @param {Object} event the event object from Hangouts Chat
 */
function onAddToSpace(event) {
  ...
}

/**
 * Responds to a REMOVED_FROM_SPACE event in Hangouts Chat.
 * @param {Object} event the event object from Hangouts Chat
 */
function onRemoveFromSpace(event) {
  ...
}

/**
 * Responds to a MESSAGE event in Hangouts Chat.
 * @param {Object} event the event object from Hangouts Chat
 */
function onMessage(event) {
  ...
}
/**
 * Responds to a CARD_CLICKED event in Hangouts Chat.
 * @param {Object} event the event object from Hangouts Chat
 */
function onCardClick(event) {
  ...
}

Unlike Gmail Add-on development where the manifest file is used to define the function names to run, bot development uses the defined function names … so if you have a typo in onAddToSpace, onRemoveFromSpace, onMessage, onCardClick these they won’t be triggered. All these methods are also optional so you might have a scenario where you don’t want to handle a
onAddToSpace event so it can just be omitted.

The event object that is returned by each EventType varies and the documentation has an overview of the different event objects. In the case of a MESSAGE event it is useful to remember that as well as the message text there is a lot of other useful data including information about the sender as well as data extracted from the message. An example MESSAGE event object is included below:

{
  "type": "MESSAGE",
  "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": "[email protected]"
    },
    "createTime": "2017-03-02T19:02:59.910959Z",
    "text": "@TestBot Violence is my last option.",
    "argumentText": " Violence is my last option.",
    "thread": {
      "name": "spaces/AAAAAAAAAAA/threads/BBBBBBBBBBB"
    },
    "annotations": [
      {
        "length": 8,
        "startIndex": 0,
        "userMention": {
          "type": "MENTION",
          "user": {
            "avatarUrl": "https://.../avatar.png",
            "displayName": "TestBot",
            "name": "users/1234567890987654321",
            "type": "BOT"
          }
        },
        "type": "USER_MENTION"
      }
    ],
  },
  "user": {
    "name": "users/12345678901234567890",
    "displayName": "Chuck Norris",
    "avatarUrl": "https://lh3.googleusercontent.com/.../photo.jpg",
    "email": "[email protected]"
  }
}

For more information on CARD_CLICKED events you need to jump into a different part of the documentation to read about Interactive Cards. If you are creating a card that performs an app action then the important thing to remember is your card should include an action.actionMethodName, and optionally action.parameters, which are then included in the CARD_CLICKED event object which you handle in your onCardClick(event) method. An example
CARD_CLICKED event object is included below:

{
  type:"CARD_CLICKED",
  eventTime:"2017-11-14T01:44:58.521823Z",
  message:{
    ...
    },
    createTime:"2017-11-14T01:44:58.521823Z",
    space:{
      ...
    },
    thread:{
      ..
    }
  },
  user:{
    ...
  },
  space:{
    ...
  },
  action:{
    actionMethodName:"upvote",
    parameters:[
      {
        "key":"count",
        "value":"7"
      },
      {
        "key":"id",
        "value":"123456"
      }
    ]
  }
}

External event triggers

Something you might want to do in your Hangouts Chat bot is respond to an external event trigger. For example, Google Apps Script has a number of built-in event triggers including when a Google Sheet is edited/changed, a Google Form is submitted or when a Calendar event is updated. There are also Time-driven triggers which you might want to use so that your bot posts a message at a certain time.

There is currently no native support for hooking into these triggers within your Hangouts Chat bot but the documentation says this is planned. In the meantime the documentation includes an example of how you can use the external HTTP API with a service account to post a message to your bot (see Async Messages for example code).

Cards and text messages

If you have worked on Gmail Add-ons the use of ‘cards’ for interfacing your Hangouts Chat bot will be familiar. All interfacing has to be done within the cards framework or simple text messaging and using HTMLService is not an option. Using text messaging you can add basic formatting to the message text, including bold, italic, and strikethrough. This is done within your message text with the markdown options.

The cards interface is constructed using JSON objects. There are various components you can use to construct your cards including text paragraphs, images and buttons all detailed in the Card Formatting documentation. As a bare minimum a card must contain at least one section and a section must contain at least one of the component widgets:

{
  "cards": [
    {
      "sections": [
        {
          "widgets": [
            { ... },
            { ... }
          ]
        },
        { ... }
      ]
    }
  ]
}

The documentation includes an example card which combines image and button widgets:

{
  "cards": [
    {
      "sections": [
        {
          "widgets": [
            {
              "image": { "imageUrl": "https://..." }
            },
            {
              "buttons": [
                {
                  "textButton": {
                    "text": "OPEN IN GOOGLE MAPS",
                    "onClick": {
                      "openLink": {
                        "url": "https://..."
                      }
                    }
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Which renders like this:

I’m not aware of any tools yet that can help easily design cards by letting you pull UI components/widgets onto a design canvas and generating the required code, but with App Maker perhaps something will be developed. In the meantime you’ve got to hand code your JSON objects and test they are working through trial and error.

One approach you might consider to make handling card design easier is to template some of your card components as global variables and using this to create your cards. An example of this approach is included in the Hangouts Chat codelab:

var DEFAULT_IMAGE_URL = 'https://goo.gl/bMqzYS';
var HEADER = {
  header: {
    title : 'Attendance Bot',
    subtitle : 'Log your vacation time',
    imageUrl : DEFAULT_IMAGE_URL
  }
};

/**
 * Creates a card-formatted response.
 * @param {object} widgets the UI components to send
 * @return {object} JSON-formatted response
 */
function createCardResponse(widgets) {
  return {
    cards: [HEADER, {
      sections: [{
        widgets: widgets
      }]
    }]
  };
}

Manifest file

Unlike Gmail Add-on manifest files which require a long list of required properties, Hangouts Chat bots currently only require one property, "chat": {}. I’m highlighting this because if you forget it your bot won’t work. One way to avoid forgetting is to use the Hangouts Chat bot template which includes everything you need in the manifest file and adds the event methods.

If you want to manually create your manifest file an example from the Hangouts Chat manifest documentation is below:

{
  "timeZone": "America/Los_Angeles",
  "dependencies": {},
  "chat": {},
  "exceptionLogging": "STACKDRIVER"
}

Deployment, testing and debugging

For your first couple of Hangouts Chat bot deployments I recommend following the Hangouts Chat codelab guide to publish and test the bot. The documentation includes some notes of things to watch out for when deploying collected below:

To register an Apps Script bot, you must be an Owner or have Can Edit permissions on the underlying Apps Script project itself. Note: If you attempt to enable the Hangouts Chat API with an @gmail.com account you will see the error: The API “chat.googleapis.com” doesn’t exist or you don’t have permission to access it[1]

Ensure you are logged in with a G Suite account and/or use the account switcher at the top right of the Cloud Console to switch to a G Suite account. If you created your script with an @gmail.com account you will need to share it with your G Suite account as well.[2]

Warning: You can use the HEAD deployment for simple testing during development. But bots with HEAD deployments cannot be shared across a domain, and cannot be accessed by users unless they have read access to your script. Therefore for general testing and release you must create a versioned deployment.[3]

When you are testing your deployed Hangouts Chat bot if there is an issue you will only see a generic error message:

This is where Stackdriver Logging and Error reporting can really help you workout what’s going wrong.

Conversational design

So far we covered the technical aspect of developing Hangouts Chat bots with Google Apps Script, but it’s also worth considering the user experience. Fortunately Google have prepared some design guidelines to help you make decisions about how users will interact with your bot. Another consideration when you are designing your bot is how you manage inputs from the user. You can design cards with button inputs, which makes it easy for users to select the appropriate action, but you might also want to consider how to handle the intent from user’s text input. To help I’ve also produced an introduction to building conversational interfaces with Dialogflow in Google Apps Script powered Google Hangouts Chat bots.

Community contributions

Besides the official documentation there have also been some great community contributions to support developers interested in developing Hangouts Chat bots and I’ve annotated some of my favourites below (ping me if you find others I should add):

Happy scripting!

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.