Creating interactive cards

When your bot sends a card message to Hangouts Chat, the space (chat room or direct message) displays a card. The card can contain various UI elements, and you can attach onClick events to these elements to make a card interactive.

Included in the onClick event is a specification of what should happen when the card element receives a click:

  • openLink lets you specify a URL to display.
  • action lets you pass app-specific information back to the bot implementation to implement arbitrary behavior. This behavior can include updating cards that are already displayed in the chat, as shown in the above example.

The following paragraphs introduce the main concepts of interactive cards.

Click to open URL

Opening a URL is simple: the card message simply defines the URL that should be opened when the click occurs:

"textButton": {
  "text": "VISIT WEBSITE",
  "onClick": {
    "openLink": { "url": "http://site.com" }
  }
}

After sending the card message, the bot has no further involvement; Hangouts Chat takes care of opening the specified URL when the click occurs.

Click to perform app action

If you want your bot to do something when a click occurs, use the action object to define information that will be sent back to the bot.

"textButton": {
  "text": "Click Me",
  "onClick": {
    "action": { ...action specification... }
  }
}

In this case, the specified information is sent to the bot when the click occurs, and the bot can then take appropriate action in response to the click.

Displaying an interactive button

To display an interactive button, a bot sends a card message that includes an onClick object with corresponding action as shown in the following example:

"buttons": [
  {
    "textButton": {
      "text": "Click Me",
      "onClick": {
        "action": {
          "actionMethodName": "snooze",
          "parameters": [
            {
              "key": "time",
              "value": "1 day"
            },
            {
              "key": "id",
              "value": "123456"
            }
          ]
        }
      }
    }
  }
]

HTTP and Cloud PubSub bots

Cloud PubSub and HTTP bots must specify action.actionMethodName to identify the action. They can also use action.parameters to pass key/value pairs containing other arbitrary information back to themselves. These key/value pairs are optional.

Apps Script bots

Apps Script bots must also specify action.actionMethodName, and optionally can use action.parameters. To support interactive cards, bots must also define an onCardClick(event) method to handle user clicks. This method will receive a callback when the user clicks on the card.

Updating an interactive card

When your bot handles an onClick.action, in many cases you'll want to update the card that's already displayed (rather than adding a new message to the space).

For example, a bug system may post a new bug into a Chat room and have a button at the bottom that says "Assign to me". When the user clicks it, the card updates to say "Bug was assigned to username".

The following paragraphs discuss how your bot receives user click information, and how to respond to it either by injecting a new card or by updating the existing one.

Receiving user click information

When the user clicks an object in the card that has an onClick.action defined, Hangouts Chat sends a message to the bot describing the event.

A CARD_CLICKED event is passed back to the developer with the action payload that was specified above. Your bot should handle this event and respond accordingly. You can use action.actionMethodName and action.parameters to add special-case clicks to different buttons for your bot.

{
  type:"CARD_CLICKED",
  eventTime:"2017-11-14T01:44:58.521823Z",
  message:{
    name:"spaces/AAAAtZLKDkk/messages/e3fCf-i1PXE.8OGDcWT2HwI",
    sender:{
      name:"users/118066814328248020034",
      displayName:"Test Bot",
      avatarUrl:"https://lh6.googleusercontent.com/...",
      type:"BOT"
    },
    createTime:"2017-11-14T01:44:58.521823Z",
    space:{
      name:"spaces/AAAAtZLKDkk",
      type:"ROOM",
      displayName:"Testing Room"
    },
    thread:{
      name:"spaces/AAAAtZLKDkk/threads/e3fCf-i1PXE",
      retentionSettings:...
    }
  },
  user:{
    name:"users/102651148563033885715",
    displayName:"Geordi La Forge",
    avatarUrl:"https://dev2-lighthouse.sandbox.google.com/...",
    type:"HUMAN"
  },
  space:{
    name:"spaces/AAAAtZLKDkk",
    type:"ROOM",
    displayName:"Testing Room"
  },
  action:{
    actionMethodName:"upvote",
    parameters:[
      {
        "key":"count",
        "value":"7"
      },
      {
        "key":"id",
        "value":"123456"
      }
    ]
  }
}

Responding to clicks with a new or updated message

Bots can respond to a request by updating the original message, or by creating a new message. They do this by specifying the actionResponse.type in the JSON response.

  • UPDATE_MESSAGE — Responding with this type updates the original message already shown in the thread.

  • NEW_MESSAGE — Responding with this type sends a new message in the same thread.

The following code snippet shows both of these response types:

// The bot can respond by updating the message...
{
  "actionResponse":{
    "type":"UPDATE_MESSAGE"
  },
  "cards":[
    {
      "header":{
        "title":"Hello World!"
      },
      "sections": ...
    }
  ]
}
.
.
.
// ...or respond by adding a new message
{
  "actionResponse":{
    "type":"NEW_MESSAGE"
  },
  "cards":[
    {
      "header":{
        "title":"Hello World!"
      },
      "sections": ...
    }
  ]
}

JavaScript example: Vote Bot

Below is an example of an interactive bot that keeps track of a vote count. When the user clicks "Upvote", the bot updates the vote count on the original card. When the user clicks "New Vote", the bot posts a new card.

/**
 * Google Cloud Function that responds to events sent from a
 * Hangouts Chat room.
 *
 * @param {Object} req Request sent from Hangouts Chat room
 * @param {Object} res Response to send back
 */
exports.processEvent = function processEvent(req, res) {
  var message;

  if (req.body.type == "ADDED_TO_SPACE" || req.body.type == "MESSAGE") {
    // Start a new vote when this bot is added or mentioned.
    message = createMessage("nobody", 0, false);
  }

  if (req.body.type == "CARD_CLICKED") {
    // Update the card in place when the "UPVOTE" button is clicked.
    if (req.body.action.actionMethodName == "upvote") {
      var count = parseInt(req.body.action.parameters[0].value);
      message = createMessage(req.body.user.displayName, count + 1, true);
    }

    // Create a new vote when the "NEW VOTE" button is clicked.
    if (req.body.action.actionMethodName == "newvote") {
      message = createMessage(req.body.user.displayName, 0, false);
    }
  }

  res.send(message);
};

/**
 * Creates a card encouraging users to vote.
 * @param voter The last person to vote.
 * @param count The current vote count.
 * @param update Whether to update the existing message or post a new message.
 */
function createMessage(voter, count, update) {
  return {
    "actionResponse": { "type": update ? "UPDATE_MESSAGE" : "NEW_MESSAGE" },
    "cards": [{
      "header": { "title": `Last vote by ${voter}!` },
      "sections": [{
        "widgets": [{
          "textParagraph": { "text": `${count} votes!` }
         }, {
          "image": { "imageUrl": IMAGES[count % IMAGES.length] }
         }, {
          "buttons": [{
            "textButton": {
              "text": "UPVOTE",
              "onClick": {
                "action": {
                  "actionMethodName": "upvote",
                  "parameters": [
                    {
                      "key": "count",
                      "value": `${count}`
                    }
                  ]
                }
              }
            }
          }, {
            "textButton": {
              "text": "NEW VOTE",
              "onClick": {
                "action": {
                  "actionMethodName": "newvote"
                }
              }
            }
          }]
        }]
      }]
    }]
  };
}

IMAGES = [
  "https://media2.giphy.com/media/3oEjHK3aw2LcB1V3QQ/giphy.gif",
  "https://media3.giphy.com/media/l0HlUIHlH4AKadXzy/giphy.gif",
  "https://media0.giphy.com/media/3otPorfb8Lu7wjKllm/giphy.gif",
  "https://media3.giphy.com/media/xT9IgFLBcm3Wi6l6qA/giphy.gif",
];

Send feedback about...

Hangouts Chat API
Hangouts Chat API