מענה לפקודות דרך שורת הפקודות כאפליקציית Google Chat

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

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

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

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

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

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

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

Node.js

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

Apps Script

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

Python

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

הגדרה של פקודת קו נטוי

בקטע הזה מוסבר איך להגדיר קו נטוי הפקודה:

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

בחירת שם לפקודת הלוכסן

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

שם ותיאור של פקודה
איור 2: השם והתיאור של פקודה בקו נטוי.

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

  • כדי לתת שם לפקודה של הלוכסן:

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

    • התיאור צריך להיות קצר וברור כדי שהמשתמשים ידעו למה לצפות כשהוא מפעיל את הפקודה.
    • חשוב ליידע את המשתמשים אם יש דרישות פורמט כלשהן לפקודה. לדוגמה, אם יוצרים פקודת /remindMe שמחייבת ארגומנט text, צריך להגדיר את התיאור כך שיהיה משהו כמו Remind me to do [something] at [time].
    • אפשר ליידע את המשתמשים אם אפליקציית Chat משיבה את כל המשתמשים במרחב, או באופן פרטי למשתמש שהפעיל את הפקודה. לדוגמה, לפקודה /about, אפשר לתאר אותה כך: Learn about this app (Only visible to you). כדי להגיב באופן פרטי פקודה של שורת הפקודות, ראו את הקטע מענה באמצעות הודעה פרטית.

איך מגדירים פקודה של שורת הפקודות ב-Google Chat API

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

כדי להגדיר פקודה של שורת הפקודות ב-Google Chat API: שלבים:

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

    כניסה לדף Google Chat API

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

  3. בקטע SlashCommand, לוחצים על Add a לוכסן הפקודה.

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

    1. שם: השם המוצג של הפקודה והסוג של המשתמשים כדי להפעיל את האפליקציה. חייב להתחיל בקו נטוי, להכיל רק טקסט ויכול להיות באורך של עד 50 תווים.
    2. תיאור: הטקסט שמתאר את השימוש ואת הפורמט את הפקודה. האורך המרבי של תיאורים הוא 50 תווים.
    3. מזהה פקודה: מספר מ-1 עד 1,000 אפליקציית Chat משתמשת בה כדי לזהות את הפקודה "קו נטוי" ולהחזיר תשובה.
  5. אופציונלי: אם רוצים שאפליקציית Chat תגיב על הפקודה עם תיבת הדו-שיח, בוחרים את הפקודה פותחים תיבת דו-שיח.

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

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

תשובה לפקודה של קו נטוי

כשמשתמשים יוצרים הודעת Chat שמכילה פקודה של קו נטוי, אפליקציית Chat מקבלת אירוע אינטראקציה אחד (MESSAGE). המטען הייעודי של האירוע מכיל מידע על הפקודה של הלוכסן, כולל 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.json(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

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

אם רוצים, אפשר לענות באמצעות הודעה פרטית

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

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

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

דוגמה מלאה: הגדרת אנשי קשר באמצעות האפליקציה 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.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"
        }
      }
    };
  }
}

Apps Script

בדוגמה הזו, נשלחת הודעת כרטיס על ידי חזרה כרטיס JSON. אפשר גם להשתמש שירות הכרטיסים של 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!"
        }
      }
    }