הגדרת פקודות דרך שורת הפקודות כדי לתקשר עם משתמשי Google Chat

כאן מוסבר איך מגדירים פקודות דרך שורת הפקודות באפליקציית Google Chat.

פקודת לוכסן היא דרך נפוצה שבה משתמשים מפעילים אפליקציות Chat ומקיימים איתן אינטראקציה. פקודות לוכסן גם עוזרות למשתמשים לגלות תכונות חשובות של אפליקציית Chat ולהשתמש בהן.

כדי להשתמש בפקודת לוכסן, המשתמשים מקלידים לוכסן (/) ואז פקודת טקסט קצרה, כמו /about, כדי לקבל מידע על אפליקציית Chat. משתמשים יכולים לגלות לוכסן פקודות זמינות על ידי הקלדת לוכסן ב-Google Chat, שבו מוצג חלון עם הפקודות הזמינות באפליקציית Chat:

שורת הפקודה של חלון הפקודות
איור 1: החלון שמופיע כשמשתמשים מקלידים לוכסן ב-Google Chat.

בעקרונות העיצוב של Google Chat תוכלו להחליט אם כדאי להגדיר פקודות דרך שורת הפקודות ולהבין איך לעצב אינטראקציות של משתמשים.

דרישות מקדימות

Node.js

Apps Script

Python

הגדרת פקודה של שורת הפקודות

כדי להגדיר פקודה דרך שורת הפקודות, מבצעים את השלבים הבאים:

  1. נותנים שם לפקודה דרך שורת הפקודות.
  2. מגדירים את הפקודה של שורת הפקודות ב-Google Chat API.
  3. יוצרים תשובה לפקודה, כמו טקסט או הודעה עם כרטיס.

בקטע הזה מוסבר איך מגדירים פקודה של שורת הפקודות.

צריך לתת שם לפקודה שכתובה על שורת הפקודות

השם של פקודה דרך שורת הפקודות הוא מה שהמשתמשים מקלידים בהודעה ב-Chat כדי להפעיל את אפליקציית Chat. מתחת לשם של הפקודה מופיע גם תיאור קצר כדי להסביר למשתמשים איך להשתמש בפקודה:

השם והתיאור של הפקודה דרך שורת הפקודות
איור 2: השם והתיאור של פקודת לוכסן.

כשבוחרים שם ותיאור לפקודה עם שורת הפקודות, כדאי לשקול את ההמלצות הבאות:

  • כדי לתת שם לפקודה עם שורת הפקודות:

    • כדאי להשתמש במילים או בביטויים קצרים, תיאוריים ופרקטיים, כדי שהפקודות יהיו ברורות ופשוטות למשתמש. למשל, במקום לומר /createAReminder, אומרים /remindMe.
    • אם הפקודה מכילה יותר ממילה אחת, עוזרים למשתמשים לקרוא את הפקודה על ידי שימוש באות גדולה במילה הראשונה ואז באות רישית. לדוגמה, במקום /updatecontact, צריך להשתמש ב-/updateContact.
    • כדאי לשקול אם להשתמש בשם ייחודי או בשם נפוץ בפקודה. אם הפקודה מתארת אינטראקציה או תכונה אופיינית, ניתן להשתמש בשם נפוץ שהמשתמשים מזהים ומצפים, כמו /settingsאו /feedback. אחרת, נסו להשתמש בשמות פקודות ייחודיים כי אם שם הפקודה שלכם זהה לאפליקציות Chat אחרות, המשתמשים יצטרכו לסנן פקודות דומות כדי למצוא את השם שלכם ולהשתמש בו.
  • תיאור הפקודה של שורת הפקודות:

    • התיאור צריך להיות קצר וברור, כדי שהמשתמשים ידעו למה לצפות כשהם מפעילים את הפקודה.
    • צריך להודיע למשתמשים אם יש דרישות עיצוב כלשהן לפקודה. לדוגמה, אם יוצרים פקודת /remindMe שנדרשת לה טקסט ארגומנט, צריך להגדיר את התיאור בערך כמו Remind me to do [something] at [time].

הגדרה של פקודת לוכסן ב-Google Chat API

כדי ליצור פקודה של שורת הפקודות, צריך לציין את הפרטים של הפקודה בהגדרה של אפליקציית Chat ל-Google Chat API.

כך מגדירים פקודת לוכסן ב-Google Chat API:

  1. במסוף Google Cloud, לוחצים על סמל התפריט > APIs & Services > ממשקי API ושירותים מופעלים > Google Chat API

    כניסה לדף Google Chat API

  2. לוחצים על Configuration.

  3. בקטע פקודות Slash, לוחצים על Add a slash commands (הוספת פקודה של לוכסן).

  4. מזינים שם, מזהה פקודה ותיאור לפקודה:

    1. שם: השם המוצג של הפקודה וההקלדה של המשתמשים כדי להפעיל את האפליקציה. השם חייב להתחיל בקו נטוי, להכיל רק טקסט ויכול להכיל עד 50 תווים.
    2. Description: הטקסט שמתאר איך משתמשים בפקודה ואיך מעצבים אותה. תיאורים יכולים להכיל עד 50 תווים.
    3. Command ID: מספר מ-1 עד 1,000 שבו אפליקציית Chat משתמשת כדי לזהות את הפקודה לוכסן ולהחזיר תשובה.
  5. אם רוצים שאפליקציית Chat תגיב לפקודה באמצעות תיבת דו-שיח, מסמנים את התיבה Open a keywords (פתיחת תיבת דו-שיח).

  6. לוחצים על שמירה.

שורת הפקודה מוגדרת עכשיו לאפליקציית Chat.

תגובה לפקודה דרך שורת הפקודות

כשמשתמשים יוצרים הודעה ב-Chat שכוללת פקודה של קו נטוי, אפליקציית Chat מקבלת אירוע אינטראקציה מסוג MESSAGE. המטען הייעודי (payload) של האירוע מכיל מידע על הפקודה לוכסן, כולל השדות slashCommand ו-slashCommandMetadata. תוכלו להשתמש בשדות האלה כדי לזהות את מזהה הפקודה ולהחזיר תגובה מותאמת אישית.

בדוגמה הבאה מוצג המטען הייעודי (payload) של JSON עבור אירוע אינטראקציה מסוג MESSAGE שכולל את פקודת הלוכסן /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"
              }
            }
          }
        ]
      }
    }

כדי להגיב לפקודה שמבוססת על לוכסן, אפשר לזהות אם השדה slashCommand נמצא במטען הייעודי (payload) של האירוע, ואם כן להחזיר תגובה לפקודה. דוגמת הקוד הבאה מראה איך להגיב לאירוע אינטראקציה MESSAGE שמכיל פקודת לוכסן:

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.runFunction; // The response to the slash command.
    }
  }

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

כדי להשתמש בקוד, צריך להחליף את מה שכתוב בשדות הבאים:

דוגמה מלאה: הגדרת אנשי קשר באמצעות אפליקציית Rolodex Chat

בדוגמה הבאה מוצגת אפליקציית Chat שמגיבה לפקודות הבאות מתוך שורת הפקודות:

  • הפקודה /help מחזירה הודעת טקסט שמסבירה איך לקבל תמיכה באפליקציית Chat. מזהה הפקודה מוגדר כ-1.
  • הפקודה /createContact פותחת תיבת דו-שיח שבה משתמשים יכולים להזין פרטים לגבי איש קשר. מזהה הפקודה מוגדר כ-2.

לפני שמריצים את הדוגמה הזו, צריך לפעול לפי ההוראות כדי להגדיר את פקודות הלוכסן ב-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.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.openDialog(event);
    }

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

    if (event.common.invokedFunction === "confirmDialogSuccess") {
      res.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) {
  res.json({
    "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) {
  res.json({
    "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 {
    res.json({
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": "OK"
        }
      }
    });
  }
}

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!"
        }
      }
    }