When your app sends a card
message to Google Chat, the
space (chat space 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 app 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 app has no further involvement; Google Chat takes care of opening the specified URL when the click occurs.
Click to perform app action
If you want your app to do something when a click occurs, use the action
object to define information that will be sent back to the app.
"textButton": {
"text": "Click Me",
"onClick": {
"action": { ...action specification... }
}
}
In this case, the specified information is sent to the app when the click occurs, and the app can then take appropriate action in response to the click.
Displaying an interactive button
To display an interactive button, an app 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 apps
Cloud PubSub and HTTP apps 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 apps
Apps Script apps must also specify action.actionMethodName
, and optionally
can use action.parameters
. To support interactive cards, apps 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 app 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 space 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 app 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,
Google Chat sends a message to the app describing the event.
A CARD_CLICKED
event is passed back to the developer with the action payload
that was specified above. Your app 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 app.
{
type:"CARD_CLICKED",
eventTime:"2017-11-14T01:44:58.521823Z",
message:{
name:"spaces/AAAAtZLKDkk/messages/e3fCf-i1PXE.8OGDcWT2HwI",
sender:{
name:"users/118066814328248020034",
displayName:"Test App",
avatarUrl:"https://lh6.googleusercontent.com/...",
type:"BOT"
},
createTime:"2017-11-14T01:44:58.521823Z",
space:{
name:"spaces/AAAAtZLKDkk",
type:"ROOM",
displayName:"Testing Space"
},
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 Space"
},
action:{
actionMethodName:"upvote",
parameters:[
{
"key":"count",
"value":"7"
},
{
"key":"id",
"value":"123456"
}
]
}
}
Responding to clicks with a new or updated message
Apps 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 app 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 App
Below is an example of an interactive app that keeps track of a vote count. When the user clicks "Upvote", the app updates the vote count on the original card. When the user clicks "New Vote", the app posts a new card.
/**
* Google Cloud Function that responds to events sent from a
* Google Chat space.
*
* @param {Object} req Request sent from Google Chat space
* @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 app 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",
];