ตอบกลับคําสั่งเครื่องหมายทับในรูปแบบแอป Google Chat

หน้านี้อธิบายวิธีตั้งค่าและตอบกลับคำสั่งเครื่องหมายทับสำหรับแอป Google Chat

คำสั่งเครื่องหมายทับเป็นวิธีทั่วไปที่ผู้ใช้เรียกใช้และโต้ตอบกับแอป Chat คำสั่งเครื่องหมายทับยังช่วยให้ผู้ใช้ค้นพบและใช้ฟีเจอร์หลักของแอป Chat ได้ด้วย

หากต้องการใช้คำสั่งเครื่องหมายทับ ผู้ใช้พิมพ์เครื่องหมายทับ (/) แล้วตามด้วยคำสั่งข้อความสั้นๆ เช่น /about เพื่อดูข้อมูลเกี่ยวกับแอป Chat ผู้ใช้จะดูคำสั่งเครื่องหมายทับที่มีอยู่ได้โดยพิมพ์เครื่องหมายทับลงใน Google Chat ซึ่งจะแสดงหน้าต่างที่แสดงคำสั่งที่ใช้ได้กับแอป Chat ดังนี้

หน้าต่างคำสั่งเครื่องหมายทับ
ภาพที่ 1: หน้าต่างที่ปรากฏขึ้นเมื่อผู้ใช้พิมพ์เครื่องหมายทับลงใน Google Chat

เมื่อผู้ใช้ส่งข้อความที่มีคำสั่งเครื่องหมายทับ จะมีเพียงผู้ใช้และแอป Chat เท่านั้นที่เห็นข้อความดังกล่าว

หากต้องการตัดสินใจว่าคุณควรตั้งค่าคําสั่งเครื่องหมายทับหรือไม่ และทําความเข้าใจวิธีออกแบบการโต้ตอบของผู้ใช้ โปรดดูกําหนดเส้นทางทั้งหมดของผู้ใช้

สิ่งที่ต้องดำเนินการก่อน

Node.js

  • บัญชี Google Workspace ที่มีสิทธิ์เข้าถึง Google Chat
  • แอป Chat หากต้องการสร้างแอป Chat ให้ทำตามquickstartนี้

Apps Script

  • บัญชี Google Workspace ที่มีสิทธิ์เข้าถึง Google Chat
  • แอป Chat หากต้องการสร้างแอป Chat ให้ทำตามquickstartนี้

Python

  • บัญชี Google Workspace ที่มีสิทธิ์เข้าถึง Google Chat
  • แอป Chat หากต้องการสร้างแอป Chat ให้ทำตามquickstartนี้

ตั้งค่าคำสั่งเครื่องหมายทับ

ส่วนนี้จะอธิบายวิธีทำตามขั้นตอนต่อไปนี้เพื่อตั้งค่าคำสั่งเครื่องหมายทับ

  1. สร้างชื่อสำหรับคำสั่งเครื่องหมายทับ
  2. กำหนดค่าคำสั่งเครื่องหมายทับใน Google Chat API

ตั้งชื่อคำสั่งเครื่องหมายทับ

ชื่อของคำสั่งเครื่องหมายทับคือสิ่งที่ผู้ใช้พิมพ์ในข้อความ Chat เพื่อเรียกใช้แอป Chat นอกจากนี้ คำอธิบายสั้นๆ จะปรากฏอยู่ใต้ชื่อเพื่อแจ้งให้ผู้ใช้เพิ่มเติมเกี่ยวกับวิธีใช้คำสั่ง ดังนี้

ชื่อและคำอธิบายคําสั่งเครื่องหมายทับ
ภาพที่ 2: ชื่อและคำอธิบายสำหรับคำสั่งเครื่องหมายทับ

โปรดพิจารณาคำแนะนำต่อไปนี้เมื่อเลือกชื่อและคำอธิบายสำหรับคำสั่งเครื่องหมายทับ

  • วิธีตั้งชื่อคำสั่งเครื่องหมายทับ

    • ใช้คำหรือวลีที่กระชับ สื่อความหมาย และนำไปใช้ได้จริงเพื่อทำให้คำสั่งชัดเจนและเข้าใจง่ายสำหรับผู้ใช้ ตัวอย่างเช่น แทนที่จะพูดว่า /createAReminder ให้ใช้ /remindMe
    • หากคำสั่งมีมากกว่า 1 คำ โปรดช่วยให้ผู้ใช้อ่านคำสั่งโดยใช้อักษรตัวพิมพ์เล็กทั้งหมดสำหรับคำแรก จากนั้นจึงใช้อักษรตัวพิมพ์ใหญ่ในอักษรตัวแรกของคำเพิ่มเติม ตัวอย่างเช่น แทนที่จะใช้ /updatecontact ให้ใช้ /updateContact
    • พิจารณาว่าจะใช้ชื่อที่ไม่ซ้ำกันหรือชื่อทั่วไปสำหรับคำสั่ง หากคำสั่งของคุณอธิบายถึงการโต้ตอบหรือฟีเจอร์ทั่วไป คุณจะใช้ชื่อทั่วไปที่ผู้ใช้จดจำและคาดหวังได้ เช่น /settingsหรือ/feedback หรือลองใช้ชื่อคำสั่งที่ไม่ซ้ำกัน เพราะหากชื่อคำสั่งของคุณเหมือนกันสำหรับแอป Chat อื่นๆ ผู้ใช้ต้องกรองโดยใช้คำสั่งที่คล้ายกันเพื่อค้นหาและใช้คำสั่งของคุณ
  • วิธีอธิบายคำสั่งเครื่องหมายทับ

    • เขียนคำอธิบายที่สั้นและชัดเจนเพื่อให้ผู้ใช้รู้ว่าจะเกิดอะไรขึ้นเมื่อเรียกใช้คำสั่ง
    • แจ้งให้ผู้ใช้ทราบหากมีข้อกำหนดการจัดรูปแบบสำหรับคำสั่งดังกล่าว ตัวอย่างเช่น หากคุณสร้างคำสั่ง /remindMe ที่ต้องใช้ข้อความอาร์กิวเมนต์ ให้ตั้งค่าคำอธิบายเป็น 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. คลิกการกำหนดค่า

  3. ในส่วนคำสั่งเครื่องหมายทับ ให้คลิกเพิ่มคำสั่งเครื่องหมายทับ

  4. ป้อนชื่อ รหัสคำสั่ง และคำอธิบายคำสั่ง ดังนี้

    1. ชื่อ: ชื่อที่แสดงของคำสั่ง และสิ่งที่ผู้ใช้พิมพ์ เพื่อเรียกใช้แอปของคุณ ต้องขึ้นต้นด้วยเครื่องหมายทับ มีเฉพาะข้อความ และยาวไม่เกิน 50 อักขระ
    2. คำอธิบาย: ข้อความที่อธิบายวิธีใช้และจัดรูปแบบคำสั่ง โดยคำอธิบายต้องมีความยาวไม่เกิน 50 อักขระ
    3. รหัสคำสั่ง: ตัวเลขตั้งแต่ 1 ถึง 1000 ที่แอป Chat ใช้ในการจดจำคำสั่งเครื่องหมายทับและแสดงผลการตอบกลับ
  5. ไม่บังคับ: หากต้องการให้แอป Chat ตอบกลับ คำสั่งด้วยกล่องโต้ตอบ ให้เลือกช่องทำเครื่องหมายเปิดกล่องโต้ตอบ

  6. คลิกบันทึก

ตอนนี้มีการกำหนดค่าคำสั่งเครื่องหมายทับสำหรับแอป Chat แล้ว

ตอบกลับคำสั่งเครื่องหมายทับ

เมื่อผู้ใช้สร้างข้อความใน Chat ที่มีคำสั่งเครื่องหมายทับ แอป Chat จะได้รับเหตุการณ์การโต้ตอบ MESSAGE เพย์โหลดของเหตุการณ์มีข้อมูลเกี่ยวกับคำสั่งเครื่องหมายทับ ซึ่งรวมถึงช่อง slashCommand และ slashCommandMetadata คุณใช้ช่องเหล่านี้เพื่อระบุรหัสคำสั่งและแสดงผลการตอบกลับที่กำหนดเองได้

ตัวอย่างต่อไปนี้แสดงเพย์โหลด 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 อยู่ในเพย์โหลดเหตุการณ์หรือไม่ หากมี ก็ให้ตอบกลับคำสั่งนั้น ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตอบกลับเหตุการณ์การโต้ตอบ 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

หากต้องการใช้โค้ด ให้แทนที่รายการต่อไปนี้:

ไม่บังคับ: ตอบด้วยข้อความส่วนตัว

เฉพาะผู้ใช้ที่ส่งข้อความและแอป Chat ที่ได้รับคำสั่งเท่านั้นที่จะเห็นข้อความที่มีคำสั่งเครื่องหมายทับเท่านั้น หากคุณกำหนดค่าแอป Chat ให้เพิ่มไปยังพื้นที่ทำงานที่มีผู้ใช้หลายคน คุณอาจพิจารณาตอบกลับคำสั่งเครื่องหมายทับแบบส่วนตัวเพื่อรักษาความเป็นส่วนตัวระหว่างผู้ใช้และแอปใน Chat

ตัวอย่างเช่น หากทีมกำลังใช้แอป Chat ที่จัดการบริการสนับสนุนลูกค้า ผู้ใช้จะเรียกใช้คำสั่งเครื่องหมายทับ เช่น /myCases เพื่อดูเคสขอรับความช่วยเหลือที่มอบหมายได้ หากทีมเพิ่มแอป Chat ไปยังพื้นที่ทำงาน ผู้ใช้ที่ใช้คำสั่งเครื่องหมายทับนี้ในพื้นที่ทำงานอาจต้องการให้แอป Chat ตอบกลับเท่านั้น แอป Chat จะตอบกลับแบบส่วนตัวได้ เพื่อหลีกเลี่ยงการโพสต์เคสขอรับความช่วยเหลือของผู้ใช้ถึงทุกคนในพื้นที่ทำงาน

หากต้องการตอบกลับคำสั่งเครื่องหมายทับแบบส่วนตัว โปรดดูหัวข้อส่งข้อความส่วนตัวไปยังผู้ใช้ Google Chat

ตัวอย่างทั้งหมด: ตั้งค่ารายชื่อติดต่อโดยใช้แอป Rolodex Chat

ตัวอย่างต่อไปนี้แสดงแอป Chat ที่ตอบสนองต่อคำสั่งเครื่องหมายทับต่อไปนี้

  • คำสั่ง /help จะแสดงผล SMS อธิบายวิธีรับการสนับสนุนด้วยแอป 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!"
        }
      }
    }