Trả lời các lệnh dấu gạch chéo trong ứng dụng Google Chat

Trang này giải thích cách thiết lập và phản hồi lệnh dấu gạch chéo cho ứng dụng Google Chat.

Lệnh dấu gạch chéo là một cách phổ biến mà người dùng gọi và tương tác với ứng dụng trong Chat. Lệnh dấu gạch chéo cũng giúp người dùng khám phá và sử dụng các tính năng chính của ứng dụng trong Chat.

Để sử dụng lệnh dấu gạch chéo, người dùng nhập dấu gạch chéo (/) rồi nhập lệnh văn bản ngắn, chẳng hạn như /about để biết thông tin về ứng dụng Chat. Người dùng có thể khám phá các lệnh dấu gạch chéo có sẵn bằng cách nhập dấu gạch chéo vào Google Chat. Thao tác này sẽ hiển thị cửa sổ liệt kê các lệnh có sẵn cho ứng dụng Chat:

Dấu gạch chéo cửa sổ lệnh
Hình 1: Cửa sổ xuất hiện khi người dùng nhập dấu gạch chéo vào Google Chat.

Khi người dùng gửi một tin nhắn chứa lệnh dấu gạch chéo, chỉ người dùng và ứng dụng Chat mới thấy tin nhắn đó.

Để quyết định xem bạn có nên thiết lập lệnh dấu gạch chéo hay không và hiểu cách thiết kế hoạt động tương tác của người dùng, hãy xem phần Xác định tất cả hành trình của người dùng.

Điều kiện tiên quyết

Node.js

  • Một tài khoản Google Workspace có quyền truy cập vào Google Chat.
  • Một ứng dụng trong Chat. Để tạo một ứng dụng nhắn tin, hãy làm theo hướng dẫn quickstart này.

Apps Script

  • Một tài khoản Google Workspace có quyền truy cập vào Google Chat.
  • Một ứng dụng trong Chat. Để tạo một ứng dụng nhắn tin, hãy làm theo hướng dẫn quickstart này.

Python

  • Một tài khoản Google Workspace có quyền truy cập vào Google Chat.
  • Một ứng dụng trong Chat. Để tạo một ứng dụng nhắn tin, hãy làm theo hướng dẫn quickstart này.

Thiết lập lệnh dấu gạch chéo

Phần này giải thích cách hoàn thành các bước sau để thiết lập lệnh dấu gạch chéo:

  1. Tạo tên cho lệnh dấu gạch chéo.
  2. Định cấu hình lệnh dấu gạch chéo trong API Google Chat.

Đặt tên cho lệnh dấu gạch chéo

Tên của lệnh dấu gạch chéo là tên mà người dùng nhập vào tin nhắn trong Chat để gọi ứng dụng Chat. Một đoạn mô tả ngắn cũng xuất hiện bên dưới tên lệnh để nhắc người dùng biết thêm về cách sử dụng lệnh đó:

Dấu gạch chéo tên và nội dung mô tả lệnh
Hình 2: Tên và nội dung mô tả của lệnh dấu gạch chéo.

Khi chọn tên và nội dung mô tả cho lệnh dấu gạch chéo, hãy cân nhắc các đề xuất sau:

  • Cách đặt tên cho lệnh dấu gạch chéo:

    • Hãy sử dụng các từ hoặc cụm từ ngắn gọn, có tính mô tả và có thể thao tác để làm cho các lệnh trở nên rõ ràng và đơn giản đối với người dùng. Ví dụ: thay vì nói /createAReminder, hãy sử dụng /remindMe.
    • Nếu lệnh của bạn có nhiều từ, hãy giúp người dùng đọc lệnh đó bằng cách sử dụng tất cả chữ thường cho từ đầu tiên rồi viết hoa chữ cái đầu tiên của các từ bổ sung. Ví dụ: thay vì /updatecontact, hãy sử dụng /updateContact.
    • Hãy cân nhắc xem nên sử dụng tên riêng biệt hay tên thông dụng cho lệnh của bạn. Nếu lệnh của bạn mô tả một tương tác hoặc tính năng thông thường, thì bạn có thể sử dụng tên phổ biến mà người dùng nhận ra và kỳ vọng, chẳng hạn như /settings hoặc /feedback. Ngược lại, hãy thử sử dụng tên lệnh duy nhất vì nếu tên lệnh của bạn giống với các ứng dụng khác trong Chat, thì người dùng phải lọc qua các lệnh tương tự để tìm và sử dụng tên lệnh của bạn.
  • Cách mô tả lệnh dấu gạch chéo:

    • Soạn nội dung mô tả ngắn gọn và rõ ràng để người dùng biết điều gì sẽ xảy ra khi họ gọi lệnh.
    • Hãy cho người dùng biết nếu có bất kỳ yêu cầu định dạng nào đối với lệnh này. Ví dụ: nếu bạn tạo một lệnh /remindMe yêu cầu văn bản đối số, hãy đặt nội dung mô tả thành Remind me to do [something] at [time].
    • Cho người dùng biết liệu ứng dụng Chat trả lời mọi người trong không gian hay trả lời riêng tư cho người dùng gọi lệnh. Ví dụ: đối với lệnh dấu gạch chéo /about, bạn có thể mô tả lệnh này là Learn about this app (Only visible to you). Để phản hồi một lệnh dấu gạch chéo ở chế độ riêng tư, hãy xem phần Trả lời bằng tin nhắn riêng tư.

Định cấu hình lệnh dấu gạch chéo trong API Google Chat

Để tạo lệnh dấu gạch chéo, bạn cần chỉ định thông tin về lệnh đó trong cấu hình của ứng dụng Chat dành cho API Google Chat.

Để định cấu hình lệnh dấu gạch chéo trong API Google Chat, hãy hoàn tất các bước sau:

  1. Trong bảng điều khiển Google Cloud, hãy nhấp vào Trình đơn > API và dịch vụ > API và dịch vụ đã bật > API Google Chat

    Truy cập trang API Google Chat

  2. Nhấp vào Cấu hình.

  3. Trong phần Lệnh chặt, hãy nhấp vào Thêm lệnh dấu gạch chéo.

  4. Nhập tên, mã lệnh và nội dung mô tả cho lệnh:

    1. Tên: tên hiển thị cho lệnh và tên người dùng nhập để gọi ứng dụng. Phải bắt đầu bằng dấu gạch chéo, chỉ chứa văn bản và có thể dài tối đa 50 ký tự.
    2. Mô tả: văn bản mô tả cách sử dụng và định dạng lệnh. Nội dung mô tả có thể dài tối đa 50 ký tự.
    3. Command ID: một số từ 1 đến 1000 mà ứng dụng Chat dùng để nhận ra lệnh dấu gạch chéo và trả về câu trả lời.
  5. Không bắt buộc: Nếu bạn muốn ứng dụng Chat phản hồi lệnh bằng hộp thoại, hãy chọn hộp đánh dấu Mở hộp thoại.

  6. Nhấp vào Lưu.

Lệnh dấu gạch chéo hiện đã được định cấu hình cho ứng dụng Chat.

Phản hồi lệnh dấu gạch chéo

Khi người dùng tạo tin nhắn trong Chat chứa lệnh dấu gạch chéo, ứng dụng Chat sẽ nhận được một sự kiện tương tác MESSAGE. Tải trọng sự kiện chứa thông tin về lệnh dấu gạch chéo, bao gồm cả các trường slashCommandslashCommandMetadata. Bạn sử dụng các trường này để xác định mã lệnh và trả về một phản hồi tuỳ chỉnh.

Ví dụ sau đây cho thấy tải trọng JSON cho một sự kiện tương tác MESSAGE bao gồm lệnh dấu gạch chéo /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"
              }
            }
          }
        ]
      }
    }

Để phản hồi lệnh dấu gạch chéo, bạn có thể phát hiện xem trường slashCommand có trong tải trọng sự kiện hay không. Nếu có, hãy trả về phản hồi cho lệnh đó. Mã mẫu sau đây cho biết cách phản hồi một sự kiện tương tác MESSAGE có chứa lệnh dấu gạch chéo:

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

Để sử dụng mã này, hãy thay thế đoạn mã sau:

Không bắt buộc: Trả lời bằng tin nhắn riêng tư

Chỉ người dùng đã gửi tin nhắn và ứng dụng Chat nhận được lệnh này mới thấy tin nhắn chứa lệnh dấu gạch chéo. Nếu đã định cấu hình để thêm ứng dụng Chat vào không gian có nhiều người, bạn có thể cân nhắc phản hồi lệnh dấu gạch chéo một cách riêng tư, để đảm bảo sự riêng tư cho hoạt động tương tác giữa người dùng và ứng dụng Chat.

Ví dụ: nếu một nhóm đang dùng một ứng dụng trong Chat quản lý dịch vụ hỗ trợ khách hàng, thì người dùng có thể gọi một lệnh dấu gạch chéo, chẳng hạn như /myCases để xem các yêu cầu hỗ trợ được chỉ định cho họ. Nếu nhóm thêm ứng dụng Chat vào một không gian, thì người dùng sử dụng lệnh dấu gạch chéo này trong không gian có thể muốn ứng dụng Chat chỉ phản hồi họ. Để tránh đăng yêu cầu hỗ trợ của người dùng cho mọi người trong không gian, ứng dụng Chat có thể trả lời riêng tư.

Để phản hồi lệnh dấu gạch chéo ở chế độ riêng tư, hãy xem phần Gửi tin nhắn riêng tư cho người dùng Google Chat.

Ví dụ hoàn chỉnh: Thiết lập danh bạ bằng ứng dụng Rolodex Chat

Ví dụ sau đây cho thấy một ứng dụng Chat phản hồi các lệnh dấu gạch chéo sau đây:

  • Lệnh /help trả về một tin nhắn văn bản giải thích cách yêu cầu hỗ trợ bằng ứng dụng Chat. Mã lệnh được đặt thành 1.
  • Lệnh /createContact sẽ mở ra một hộp thoại để người dùng có thể nhập thông tin chi tiết về một người liên hệ. Mã lệnh được đặt thành 2.

Trước khi chạy mẫu này, hãy làm theo các bước để định cấu hình lệnh dấu gạch chéo trong API Google Chat.

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

Ví dụ này gửi tin nhắn thẻ bằng cách trả về tệp JSON của thẻ. Bạn cũng có thể sử dụng dịch vụ thẻ 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!"
        }
      }
    }