Odpowiadanie na polecenia po ukośniku jako aplikacja Google Chat

Na tej stronie dowiesz się, jak skonfigurować polecenia po ukośniku w aplikacji Google Chat.

Polecenie ukośnika jest typowym sposobem wywoływania przez użytkowników polecenia i wchodzenia z nim w interakcję aplikacja Google Chat. Polecenia po ukośniku pomagają też użytkownikom korzystać z najważniejszych funkcji aplikacji Google Chat.

Aby użyć polecenia po ukośniku, użytkownik wpisze ukośnik (/), a następnie krótkie polecenie tekstowe. np. /about, aby uzyskać informacje o aplikacji Google Chat. Użytkownicy mogą wyświetlić dostępne polecenia po ukośniku, wpisując ukośnik w Google Chat, gdzie wyświetlane jest okno z listą dostępnych poleceń dla Aplikacja do obsługi czatu:

Okno polecenia po ukośniku
Rysunek 1. Okno wyświetlane po wpisaniu przez użytkownika ukośnika w Google Chat.

Gdy użytkownik wysyła wiadomość zawierającą polecenie po ukośniku, jest ona widoczne dla użytkownika i aplikacji Google Chat.

Aby zdecydować, czy warto skonfigurować polecenia po ukośniku, i dowiedzieć się, jak projektować interakcje użytkowników, zobaczyć Definiowanie wszystkich ścieżek użytkowników

Wymagania wstępne

Node.js

Aplikacja Google Chat, w której można korzystać z funkcji interaktywnych. Aby utworzyć interaktywna aplikacja do obsługi czatu przy użyciu usługi HTTP zapoznaj się z tym krótkim wprowadzeniem.

Google Apps Script

Aplikacja Google Chat, w której można korzystać z funkcji interaktywnych. Aby utworzyć z interaktywną aplikacją Google Chat w Apps Script, zapoznaj się z tym krótkim wprowadzeniem.

Python

Aplikacja Google Chat, w której można korzystać z funkcji interaktywnych. Aby utworzyć interaktywna aplikacja do obsługi czatu przy użyciu usługi HTTP zapoznaj się z tym krótkim wprowadzeniem.

Skonfiguruj polecenie po ukośniku

W tej sekcji opisano, jak skonfigurować ukośnik: polecenie:

  1. Utwórz nazwę polecenia po ukośniku.
  2. Skonfiguruj polecenie po ukośniku w interfejsie Google Chat API.

Nazwij polecenie po ukośniku

Nazwa polecenia po ukośniku to nazwa, którą użytkownicy wpisują w wiadomości w Google Chat aby wywołać aplikację Google Chat. Dodatkowo krótki opis jest wyświetlane pod nazwą, aby poinformować użytkowników o sposobie użycia polecenia:

Nazwa i opis polecenia po ukośniku
Rysunek 2. Nazwa i opis polecenia po ukośniku.

Wybierając nazwę i opis polecenia po ukośniku, weź pod uwagę następujące zalecenia:

  • Aby nazwać polecenie po ukośniku:

    • Używaj krótkich, opisowych i praktycznych słów lub wyrażeń, jest zrozumiałe i proste w użyciu. Na przykład zamiast mówić /createAReminder, użyj formy płatności /remindMe.
    • Jeśli polecenie zawiera więcej niż 1 słowo, pomóż użytkownikom przeczytać polecenie używając wyłącznie małych liter w pierwszym słowie, a następnie wielką literą dodatkowych słów. Na przykład zamiast /updatecontact, użyj metody /updateContact.
    • Zastanów się, czy w poleceniu użyć unikalnej czy potocznej nazwy. Jeśli to polecenie opisuje typową interakcję lub funkcję, powszechna nazwa rozpoznawalna i oczekiwana przez użytkowników, taka jak /settingslub /feedback W przeciwnym razie używaj unikalnych nazw poleceń, ponieważ jeśli jest taka sama jak w innych aplikacjach do obsługi czatu, użytkownik musi przefiltruj podobne polecenia, aby znaleźć Twoje.
  • Aby opisać polecenie po ukośniku:

    • Opis powinien być krótki i jasny, aby użytkownicy wiedzieli, czego mogą się spodziewać podczas wywoływania polecenia.
    • Powiadom użytkowników, jeśli istnieją wymagania dotyczące formatowania polecenia. Jeśli na przykład utworzysz polecenie /remindMe, które wymaga argumentu tekst, ustaw opis na około Remind me to do [something] at [time].
    • Poinformuj użytkowników, czy aplikacja Google Chat odpowiada na wszystkim w pokoju lub prywatnie użytkownikowi, który wywołał polecenie. Na przykład polecenie po ukośniku /about możesz opisać tak Learn about this app (Only visible to you) Aby odpowiedzieć prywatnie na Więcej informacji znajdziesz w sekcji Odpowiedz wiadomością prywatną.

Skonfiguruj polecenie po ukośniku w interfejsie Google Chat API

Aby utworzyć polecenie po ukośniku, podaj informacje o nim w konfigurację aplikacji Google Chat pod kątem interfejsu Google Chat API.

Aby skonfigurować polecenie po ukośniku w interfejsie Google Chat API, wykonaj te czynności kroki:

  1. W konsoli Google Cloud kliknij Menu. > Interfejsy API i Usługi > Włączone interfejsy API i Usługi > Google Chat API

    Otwórz stronę interfejsu Google Chat API

  2. Kliknij Konfiguracja.

  3. W sekcji Polecenia po ukośniku kliknij Dodaj polecenie po ukośniku.

  4. Wpisz nazwę, identyfikator polecenia i opis:

    1. Nazwa:wyświetlana nazwa polecenia i tekst wpisywany przez użytkownika. aby wywołać aplikację. Musi zaczynać się od ukośnika, zawierać tylko tekst i może może mieć maksymalnie 50 znaków.
    2. Opis: tekst opisujący sposób użycia i formatowania . Opis może mieć maksymalnie 50 znaków.
    3. Identyfikator polecenia: liczba od 1 do 1000, Aplikacja do obsługi czatu rozpoznaje polecenie po ukośniku i zwrócić odpowiedź.
  5. Opcjonalnie: jeśli chcesz, aby aplikacja Google Chat odpowiadała z oknem, wybierz Pole wyboru Otwórz okno.

  6. Kliknij Zapisz.

Polecenie po ukośniku zostało skonfigurowane w aplikacji Google Chat.

Odpowiadanie na polecenie po ukośniku

Gdy użytkownicy utworzą wiadomość w Google Chat zawierającą polecenie po ukośniku, Twoja aplikacja Google Chat odebrała zdarzenie interakcji MESSAGE. Zawiera on informacje o poleceniu po ukośniku, w tym slashCommand i slashCommandMetadata . Tych pól można użyć do identyfikacji identyfikatora polecenia i zwrócenia niestandardowego .

Poniższy przykład pokazuje ładunek JSON dla zdarzenia interakcji MESSAGE zawierające polecenie po ukośniku /vote:

    {
      ...
      "message": {
        ...
        "text": "/vote yes",
        "argumentText": " yes",
        "slashCommand": {
          "commandId": 2
        },
        "annotations": [
          {
            "length": 5,
            "startIndex": 0,
            "type": "SLASH_COMMAND",
            "slashCommand": {
              "commandName":"/vote",
              "commandId":1,
              "type": "INVOKE",
              "bot": {
                "avatarUrl": "https://www.example.com/images/vote-app-icon.png",
                "displayName": "Voter Chat App",
                "name": "users/1234567890987654321",
                "type": "BOT"
              }
            }
          }
        ]
      }
    }

Aby odpowiedzieć na polecenie po ukośniku, możesz sprawdzić, czy pole slashCommand jest obecny w ładunku zdarzenia, a jeśli tak, zwraca odpowiedź na polecenie. Poniższy przykładowy kod pokazuje, jak odpowiadać na zdarzenie interakcji MESSAGE zawierające polecenie po ukośniku:

Node.js

/**
* Responds to a MESSAGE event in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} function in response to a slash command.
*/

exports.onMessage = function onMessage(req, res) {

  // Stores the Google Chat event as a variable.
  var event = req.body;

  // Checks for the presence of event.message.slashCommand.
  if (event.message.slashCommand) {
    switch (event.message.slashCommand.commandId) {
      case ID: // The ID for your slash command
        res.json(runFunction); // The response to the slash command.
    }
  }

Google Apps Script

/**
* Responds to a MESSAGE event in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} function in response to a slash command.
*/

function onMessage(event) {

  // Checks for the presence of event.message.slashCommand
  if (event.message.slashCommand) {
    switch (event.message.slashCommand.commandId) {
      case ID: // The ID for your slash command
        return runFunction; // The response to the slash command.
    }
  }
}

Python

from typing import Any, Mapping

import flask
import functions_framework

@functions_framework.http
def main(req: flask.Request) -> Mapping[str, Any]:
  """Responds to a MESSAGE event in Google Chat that includes a slash command.

  Args:
      req (flask.Request): the event object from Chat API.

  Returns:
      Mapping[str, Any]: function in response to a slash command.
  """
  if req.method == 'GET':
    return 'Sorry, this function must be called from a Google Chat.'

  request = req.get_json(silent=True)

  if slash_command := request.get('message', dict()).get('slashCommand'):
    command_id = slash_command['commandId']
    if command_id == ID:
      return runFunction

Aby użyć tego kodu, zastąp fragment:

Opcjonalnie: odpowiedz na wiadomość prywatną

Wiadomości zawierające polecenia po ukośniku są widoczne tylko dla użytkownika, który wysłał oraz aplikację Google Chat, która otrzymuje polecenie. Jeśli skonfigurowano aplikację Google Chat tak, aby była dodawana do pokoi z wieloma osobami możesz odpowiedzieć na polecenie po ukośniku, aby zachować prywatność interakcji między użytkownikiem a aplikacja Google Chat.

Jeśli na przykład zespół korzysta z aplikacji Google Chat, która zarządza do działu obsługi klienta, użytkownicy mogą wywołać polecenie po ukośniku, takie jak /myCases, aby wyświetlić przypisane do niego zgłoszenia do zespołu pomocy. Jeśli zespół doda tagi z aplikacji do obsługi czatu do pokoju – użytkownik, który użyje tego polecenia po ukośniku użytkownicy w pokoju mogą chcieć, aby aplikacja Google Chat odpowiadała tylko . Aby uniknąć zamieszczenia zgłoszenia do pomocy technicznej dla wszystkich w pokoju, Aplikacja do obsługi czatu może odpowiadać prywatnie.

Aby prywatnie odpowiedzieć na polecenie po ukośniku, zobacz Wysyłanie wiadomości prywatnych do użytkowników Google Chat

Pełny przykład: konfigurowanie kontaktów za pomocą aplikacji Rolodex Chat

Poniższy przykład przedstawia aplikację Google Chat, która odpowiada na następujące polecenia po ukośniku:

  • Polecenie /help zwraca wiadomość tekstową z wyjaśnieniem, jak uzyskać z pomocą aplikacji Google Chat. Identyfikator polecenia jest ustawiony do: 1.
  • Polecenie /createContact otwiera okno, w którym użytkownicy mogą wpisać informacje o kontakcie. Identyfikator polecenia jest ustawiony na 2.

Zanim uruchomisz ten przykład, wykonaj te czynności, skonfigurować polecenia po ukośniku w interfejsie Google Chat API.

Node.js

/**
* Responds to messages that have links whose URLs
* match URL patterns configured for link previews.
*
* @param {Object} event The event object from Chat
* API.
*
* @return {Object} Response from the Chat app
* attached to the message with the previewed link.
*/
exports.onMessage = function onMessage(req, res) {

  // Store the Google Chat event as a variable.
  const event = req.body;

  if (req.method === "GET" || !event.message) {
    res.send("Hello! This function is meant to be used in a Google Chat " +
      "Space.");
  }

  // Checks for the presence of event.message.slashCommand.
  // If the slash command is "/help", responds with a text message.
  // If the slash command is "/createContact", opens a dialog.
  if (event.message.slashCommand) {
    switch (event.message.slashCommand.commandId) {
      case 1: // /help
        res.json({"text": "Contact bot helps you update your address book!"});
      case 2:  // /createContact
        res.json(openDialog(event));
    }
  }

  // If the Chat app doesn"t detect a slash command, it responds
  // with a card that prompts the user to add a contact
  else {
    res.json({
      "cardsV2": [{
        "cardId": "addContact",
        "card": {
          "header": {
            "title": "Rolodex",
            "subtitle": "Manage your contacts!",
            "imageUrl": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
            "imageType": "CIRCLE"
          },
          "sections": [
            {
              "widgets": [
                {
                  "buttonList": {
                    "buttons": [
                      {
                        "text": "Add Contact",
                        "onClick": {
                          "action": {
                            "function": "openDialog",
                            "interaction": "OPEN_DIALOG"
                          }
                        }
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }]
    });
  }

  // Respond to button clicks on attached cards
  if (event.type === "CARD_CLICKED") {
    if (event.common.invokedFunction === "openDialog") {
      res.json(openDialog(event));
    }

    if (event.common.invokedFunction === "openSequentialDialog") {
      res.json(openSequentialDialog(event));
    }

    if (event.common.invokedFunction === "confirmDialogSuccess") {
      res.json(confirmDialogSuccess(event));
    }
  }
};

/**
* Opens and starts a dialog that lets users add details about a contact.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openDialog(event) {
  return {
    "action_response": {
      "type": "DIALOG",
      "dialog_action": {
        "dialog": {
          "body": {
            "sections": [
              {
                "header": "Add new contact",
                "widgets": [
                  {
                    "textInput": {
                      "label": "Name",
                      "type": "SINGLE_LINE",
                      "name": "name"
                    }
                  },
                  {
                    "textInput": {
                      "label": "Address",
                      "type": "MULTIPLE_LINE",
                      "name": "address"
                    }
                  },
                  {
                    "decoratedText": {
                      "text": "Add to favorites",
                      "switchControl": {
                        "controlType": "SWITCH",
                        "name": "saveFavorite"
                      }
                    }
                  },
                  {
                    "decoratedText": {
                      "text": "Merge with existing contacts",
                      "switchControl": {
                        "controlType": "SWITCH",
                        "name": "mergeContact",
                        "selected": true
                      }
                    }
                  },
                  {
                    "buttonList": {
                      "buttons": [
                        {
                          "text": "Next",
                          "onClick": {
                            "action": {
                              "function": "openSequentialDialog"
                            }
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  };
};

/**
* Opens a second dialog that lets users add more contact details.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openSequentialDialog(event) {
  return {
    "action_response": {
      "type": "DIALOG",
      "dialog_action": {
        "dialog": {
          "body": {
            "sections": [
              {
                "header": "Add new contact",
                "widgets": [
                  {
                    "textInput": {
                      "label": "Notes",
                      "type": "MULTIPLE_LINE",
                      "name": "notes"
                    }
                  },
                  {
                    "selectionInput": {
                      "type": "RADIO_BUTTON",
                      "label": "Contact type",
                      "name": "contactType",
                      "items": [
                        {
                          "text": "Work",
                          "value": "Work",
                          "selected": false
                        },
                        {
                          "text": "Personal",
                          "value": "Personal",
                          "selected": false
                        }
                      ]
                    }
                  },
                  {
                    "buttonList": {
                      "buttons": [
                        {
                          "text": "Submit",
                          "onClick": {
                            "action": {
                              "function": "confirmDialogSuccess",
                              "parameters": [
                                {
                                  "key": "confirmDialogSuccess",
                                  "value": "confirmDialogSuccess"
                                }
                              ]
                            }
                          }
                        }
                      ]
                    },
                    "horizontalAlignment": "END"
                  }
                ]
              }
            ]
          }
        }
      }
    }
  };
}

/**
* Checks for a form input error, the absence of
* a "name" value, and returns an error if absent.
* Otherwise, confirms successful receipt of a dialog.
*
* Confirms successful receipt of a dialog.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in Google Chat.
*/
function receiveDialog(event) {

  // Checks to make sure the user entered a name
  // in a dialog. If no name value detected, returns
  // an error message.
  if (event.common.formInputs.contactName.stringInputs.value[0] === "") {
    return {
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": {
            "statusCode": "OK",
            "userFacingMessage": "Don't forget to name your new contact!"
          }
        }
      }
    };

    // Otherwise the app indicates that it received
    // form data from the dialog. Any value other than "OK"
    // gets returned as an error. "OK" is interpreted as
    // code 200, and the dialog closes.
  } else {
    return {
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": "OK"
        }
      }
    };
  }
}

Google Apps Script

Ten przykładowy kod karty wysyła wiadomość przez zwrócenie card JSON. Możesz też użyć usługi Usługa karty Apps Script.

apps-script/dialogs/rolodex.gs
/**
* Responds to a MESSAGE event in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {Object} open a Dialog in response to a slash command
* or a card"s button click.
*/
function onMessage(event) {

  // Checks for the presence of event.message.slashCommand.
  // If the slash command is "/help", responds with a text message.
  // If the slash command is "/createContact", opens a dialog.
  if (event.message.slashCommand) {
    switch (event.message.slashCommand.commandId) {
      case 1: // /help
        return {"text": "Contact bot helps you update your address book!"}
      case 2:  // /createContact
        return openDialog(event);
    }
  }

  // If the Chat app doesn"t detect a slash command, it responds
  // with a card that prompts the user to add a contact
  else {
    return {
      "cardsV2": [{
        "cardId": "addContact",
        "card": {
          "header": {
            "title": "Rolodex",
            "subtitle": "Manage your contacts!",
            "imageUrl": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
            "imageType": "CIRCLE"
          },
          "sections": [
            {
              "widgets": [
                {
                  "buttonList": {
                    "buttons": [
                      {
                        "text": "Add Contact",
                        "onClick": {
                          "action": {
                            "function": "openDialog",
                            "interaction": "OPEN_DIALOG"
                          }
                        }
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }]

    };
  }
}

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

  if (event.common.invokedFunction === "openDialog") {
    return openDialog(event);
  }

  if (event.common.invokedFunction === "openSequentialDialog") {
    const contactName = fetchFormValue(event, "contactName");
    const address = fetchFormValue(event, "address");
    return openSequentialDialog(contactName, address);
  }

  if (event.common.invokedFunction === "receiveDialog") {
    const parameters = event.common.parameters;
    parameters["contactType"] = fetchFormValue(event, "contactType");
    parameters["notes"] = fetchFormValue(event, "notes");
    return receiveDialog(parameters);
  }
}

/**
 * Extracts form input value for a given widget
 * 
 * @param {Object} event the event object from Google Chat
 * @param {String} widgetName the widget name
 * @returns the form input value for the widget
 */
function fetchFormValue(event, widgetName) {
  const widget = event.common.formInputs[widgetName];
  if (widget) {
    return widget[""]["stringInputs"]["value"][0];
  }
}

/**
* Opens and starts a dialog that lets users add details about a contact.
*
*
* @return {Object} open a dialog.
*/
function openDialog(event) {
  return {
    "action_response": {
      "type": "DIALOG",
      "dialog_action": {
        "dialog": {
          "body": {
            "sections": [
              {
                "header": "Add new contact",
                "widgets": [
                  {
                    "textInput": {
                      "label": "Name",
                      "type": "SINGLE_LINE",
                      "name": "contactName"
                    }
                  },
                  {
                    "textInput": {
                      "label": "Address",
                      "type": "MULTIPLE_LINE",
                      "name": "address"
                    }
                  },
                  {
                    "decoratedText": {
                      "text": "Add to favorites",
                      "switchControl": {
                        "controlType": "SWITCH",
                        "name": "saveFavorite"
                      }
                    }
                  },
                  {
                    "decoratedText": {
                      "text": "Merge with existing contacts",
                      "switchControl": {
                        "controlType": "SWITCH",
                        "name": "mergeContact",
                        "selected": true
                      }
                    }
                  },
                  {
                    "buttonList": {
                      "buttons": [
                        {
                          "text": "Next",
                          "onClick": {
                            "action": {
                              "function": "openSequentialDialog"
                            }
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  };
}

/**
* Opens a second dialog that lets users add more contact details.
*
* @param {String} contactName the contact name from the previous dialog.
* @param {String} address the address from the previous dialog.
*
* @return {Object} open a dialog.
*/
function openSequentialDialog(contactName, address) {
  return {
    "action_response": {
      "type": "DIALOG",
      "dialog_action": {
        "dialog": {
          "body": {
            "sections": [
              {
                "header": "Add new contact",
                "widgets": [
                  {
                    "textInput": {
                      "label": "Notes",
                      "type": "MULTIPLE_LINE",
                      "name": "notes"
                    }
                  },
                  {
                    "selectionInput": {
                      "type": "RADIO_BUTTON",
                      "label": "Contact type",
                      "name": "contactType",
                      "items": [
                        {
                          "text": "Work",
                          "value": "Work",
                          "selected": false
                        },
                        {
                          "text": "Personal",
                          "value": "Personal",
                          "selected": false
                        }
                      ]
                    }
                  },
                  {
                    "buttonList": {
                      "buttons": [
                        {
                          "text": "Submit",
                          "onClick": {
                            "action": {
                              "function": "receiveDialog",
                              "parameters": [
                                {
                                  "key": "contactName",
                                  "value": contactName
                                },
                                {
                                  "key": "address",
                                  "value": address
                                }
                              ]
                            }
                          }
                        }
                      ]
                    },
                    "horizontalAlignment": "END"
                  }
                ]
              }
            ]
          }
        }
      }
    }
  };
}

/**
* Checks for a form input error, the absence of
* a "name" value, and returns an error if absent.
* Otherwise, confirms successful receipt of a dialog.
*
* Confirms successful receipt of a dialog.
*
* @param {Object} parameters the form input values.
*
* @return {Object} open a Dialog in Google Chat.
*/
function receiveDialog(parameters) {

  // Checks to make sure the user entered a name
  // in a dialog. If no name value detected, returns
  // an error message.
  if (!parameters.contactName) {
    return {
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": {
            "statusCode": "INVALID_ARGUMENT",
            "userFacingMessage": "Don't forget to name your new contact!"
          }
        }
      }
    };

    // Otherwise the Chat app indicates that it received
    // form data from the dialog. Any value other than "OK"
    // gets returned as an error. "OK" is interpreted as
    // code 200, and the dialog closes.
  } else {
    return {
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": {
            "statusCode": "OK",
            "userFacingMessage": "Success " + JSON.stringify(parameters)
          }
        }
      }
    };
  }
}

Python

from typing import Any, Mapping

import flask
import functions_framework

@functions_framework.http
def main(req: flask.Request) -> Mapping[str, Any]:
  """Responds to a MESSAGE event in Google Chat that includes the /createContact
     slash command by opening a dialog.

  Args:
      req (flask.Request): the event object from Chat API.

  Returns:
      Mapping[str, Any]: open a Dialog in response to a card's button click.
  """

  if req.method == 'GET':
    return 'Sorry, this function must be called from a Google Chat.'

  request = req.get_json(silent=True)

  if request.get('type') == 'CARD_CLICKED':
    invoked_function = request.get('common', dict()).get('invokedFunction')
    if invoked_function == 'open_dialog':
      return open_dialog(request)

    elif invoked_function == 'open_sequential_dialog':
      return open_dialog(request)

    elif invoked_function == "receive_dialog":
      return receive_dialog(request)

  else:
    return {
      'cardsV2': [{
        'cardId': 'addContact',
        'card': {
          'header': {
            'title': 'Rolodex',
            'subtitle': 'Manage your contacts!',
            'imageUrl': 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',
            'imageType': 'CIRCLE'
          },
          'sections': [
            {
              'widgets': [
                {
                  'buttonList': {
                    'buttons': [
                      {
                        'text': 'Add Contact',
                        'onClick': {
                                'action': {
                                  'function': 'open_dialog',
                                  'interaction': 'OPEN_DIALOG'
                                }
                        }
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }]
    }

def open_dialog(request: Mapping[str, Any]) -> Mapping[str, Any]:
  """Opens a dialog in Google Chat.

  Args:
      request (Mapping[str, Any]): the event object from Chat API.

  Returns:
      Mapping[str, Any]: open a Dialog in response to a card's button click.
  """
  return {
    'action_response': {
      'type': 'DIALOG',
      'dialog_action': {
        'dialog': {
          'body': {
            'sections': [
              {
                'header': 'Add new contact',
                'widgets': [
                  {
                    'textInput': {
                      'label': 'Name',
                      'type': 'SINGLE_LINE',
                      'name': 'name'
                    }
                  },
                  {
                    'textInput': {
                      'label': 'Address',
                      'type': 'MULTIPLE_LINE',
                      'name': 'address'
                    }
                  },
                  {
                    'decoratedText': {
                      'text': 'Add to favorites',
                      'switchControl': {
                        'controlType': 'SWITCH',
                        'name': 'saveFavorite'
                      }
                    }
                  },
                  {
                    'decoratedText': {
                      'text': 'Merge with existing contacts',
                      'switchControl': {
                        'controlType': 'SWITCH',
                        'name': 'mergeContact',
                        'selected': True
                      }
                    }
                  },
                  {
                    'buttonList': {
                      'buttons': [
                        {
                          'text': 'Next',
                          'onClick': {
                            'action': {
                              'function': 'open_sequential_dialog'
                            }
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  }

def open_sequential_dialog(request: Mapping[str, Any]) -> Mapping[str, Any]:
  """Opens a second dialog that lets users add more contact details.

  Args:
      request (Mapping[str, Any]): the event object from Chat API.

  Returns:
      Mapping[str, Any]: open a Dialog in response to a card's button click.
  """
  return {
    'action_response': {
      'type': 'DIALOG',
      'dialog_action': {
        'dialog': {
              'body': {
                'sections': [
                  {
                    'header': 'Add new contact',
                    'widgets': [
                      {
                        'textInput': {
                          'label': 'Notes',
                          'type': 'MULTIPLE_LINE',
                          'name': 'notes'
                        }
                      },
                      {
                        'selectionInput': {
                          'type': 'RADIO_BUTTON',
                          'label': 'Contact type',
                          'name': 'contactType',
                          'items': [
                            {
                              'text': 'Work',
                              'value': 'Work',
                              'selected': False
                            },
                            {
                              'text': 'Personal',
                              'value': 'Personal',
                              'selected': False
                            }
                          ]
                        }
                      },
                      {
                        'buttonList': {
                          'buttons': [
                            {
                              'text': 'Submit',
                              'onClick': {
                                'action': {
                                  'function': 'receive_dialog',
                                  'parameters': [
                                    {
                                      'key': 'receiveDialog',
                                      'value': 'receiveDialog'
                                    }
                                  ]
                                }
                              }
                            }
                          ]
                        },
                        'horizontalAlignment': 'END'
                      }
                    ]
                  }
                ]
              }
        }
      }
    }
  }

def receive_dialog(event: Mapping[str, Any]) -> Mapping[str, Any]:
  """Checks for a form input error, the absence of a "name" value, and returns
     an error if absent. Otherwise, confirms successful receipt of a dialog.

  Args:
      event (Mapping[str, Any]): the event object from Chat API.

  Returns:
      Mapping[str, Any]: the response.
  """

  if event.get('common', dict()) \
      .get('formInputs', dict()).get('contactName', dict()) \
          .get('stringInputs').get('value', list()):
    return {
      'actionResponse': {
        'type': 'DIALOG',
        'dialogAction': {
          'actionStatus': 'OK'
        }
      }
    }
  else:
    return {
      'actionResponse': {
        'type': 'DIALOG',
        'dialogAction': {
          'actionStatus': "Don't forget to name your new contact!"
        }
      }
    }