الردّ على الأوامر التي تبدأ بشرطة مائلة كتطبيق Google Chat

تشرح هذه الصفحة كيفية إعداد أوامر الشرطة المائلة والاستجابة لها في تطبيق Google Chat

تُعد أمر الشرطة المائلة طريقة شائعة يقوم بها المستخدمون باستدعاء وتفاعل مع تطبيق Chat تساعد الأوامر التي تبدأ بشرطة مائلة المستخدمين على اكتشاف استخدام الميزات الرئيسية لتطبيق Chat.

لاستخدام أمر شرطة مائلة، يكتب المستخدمون شرطة مائلة (/)، ثم أمرًا نصيًا قصيرًا، مثل "/about" للحصول على معلومات عن تطبيق Chat. يمكن للمستخدمين اكتشاف الأوامر التي تبدأ بشرطة مائلة عن طريق كتابة شرطة مائلة في Google Chat، الذي يعرض نافذة تسرد الأوامر المتاحة تطبيق Chat:

نافذة أوامر تبدأ بشرطة مائلة
الشكل 1: النافذة التي تظهر عندما يكتب المستخدمون شرطة مائلة في Google Chat

عندما يرسل المستخدم رسالة تحتوي على أمر شرطة مائلة، فإن الرسالة مرئية للمستخدم وتطبيق Chat.

لتحديد ما إذا كان ينبغي لك إعداد الأوامر التي تبدأ بشرطة مائلة، وفهم كيفية تصميم تفاعلات المستخدم، انظر تحديد جميع رحلات المستخدم:

المتطلبات الأساسية

Node.js

تطبيق Google Chat مفعَّل للميزات التفاعلية لإنشاء تطبيق Chat تفاعليًا باستخدام خدمة HTTP، أكمِل عملية البدء السريع هذه.

برمجة تطبيقات

تطبيق Google Chat مفعَّل للميزات التفاعلية لإنشاء تطبيق Chat التفاعلي في "برمجة تطبيقات Google"، أكمِل البدء السريع هذا.

Python

تطبيق Google Chat مفعَّل للميزات التفاعلية لإنشاء تطبيق Chat تفاعليًا باستخدام خدمة HTTP، أكمِل عملية البدء السريع هذه.

إعداد أمر شرطة مائلة

يوضّح هذا القسم كيفية إكمال الخطوات التالية لإعداد الشرطة المائلة :

  1. أنشئ اسمًا للأمر الذي يبدأ بشرطة مائلة.
  2. ضبط الأمر الشرطة المائلة في Google Chat API

إدخال اسم للأمر الذي يبدأ بشرطة مائلة

يكون اسم الأمر slash هو ما يكتبه المستخدمون في رسالة Chat. لاستدعاء تطبيق Chat. يحتوي الوصف الموجز أيضًا أسفل الاسم، لمطالبة المستخدمين بالمزيد حول كيفية استخدام الأمر:

اسم الأمر الذي يبدأ بشرطة مائلة ووصفها
الشكل 2: اسم أمر الشرطة المائلة ووصفه

عند اختيار اسم ووصف للأمر الذي يبدأ بشرطة مائلة، ضع في اعتبارك الاقتراحات التالية:

  • لتسمية الأمر الذي تبدأ بشرطة مائلة:

    • استخدم كلمات أو عبارات قصيرة ووصفية وقابلة للتنفيذ لإنشاء واضحة وبسيطة للمستخدم. على سبيل المثال، بدلاً من أن تقول /createAReminder، استخدِم /remindMe.
    • إذا كان الأمر يحتوي على أكثر من كلمة، ساعِد المستخدمين على قراءة الأمر. باستخدام الأحرف الصغيرة بالكامل في الكلمة الأولى ثم كتابة الكلمة الأولى بالحروف الكبيرة حرف من الكلمات الإضافية. على سبيل المثال، بدلاً من /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، انقر على "القائمة" > واجهات برمجة التطبيقات الخدمات > واجهات برمجة التطبيقات المفعّلة الخدمات > 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.json(runFunction); // The response to the slash command.
    }
  }

برمجة تطبيقات

/**
* 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"
        }
      }
    };
  }
}

برمجة تطبيقات

يرسل هذا المثال رسالة بطاقة من خلال عرض بطاقة JSON. يمكنك أيضًا استخدام صفحة خدمة بطاقة "برمجة تطبيقات Google"

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