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

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

إنّ الأمر بالشرطة المائلة هو طريقة شائعة يستدعي بها المستخدمون تطبيق Chat ويتفاعلون معه. تساعد هذه الأوامر أيضًا المستخدمين في اكتشاف الميزات الرئيسية لتطبيق Chat واستخدامها.

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

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

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

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

Node.js

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

Python

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

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

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

أدخِل اسمًا للأمر الذي يبدأ بشرطة مائلة.

إنّ اسم الأمر الذي يبدأ بشرطة مائلة هو ما يكتبه المستخدمون في رسالة 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.

لضبط أمر شرطة مائلة في Google Chat API، أكمِل الخطوات التالية:

  1. في Google Cloud Console، انقر على رمز القائمة > واجهات برمجة التطبيقات والخدمات > واجهات برمجة التطبيقات والخدمات التي تم تفعيلها > Google Chat API

    الانتقال إلى صفحة Google Chat API

  2. انقر على الإعداد.

  3. ضمن أوامر Slash، انقر على إضافة أمر شرطة مائلة.

  4. أدخِل اسمًا ومعرّفًا للأمر ووصفًا له:

    1. الاسم: الاسم المعروض للأمر وما يكتبه المستخدمون لاستدعاء تطبيقك. ويجب أن يبدأ بشرطة مائلة وأن يحتوي على نص فقط وألّا يزيد طوله عن 50 حرفًا.
    2. Description: النص الذي يصف كيفية استخدام الأمر وتنسيقه. ويمكن أن يصل طول الأوصاف إلى 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.
    }
  }

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

/**
* 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 يدير خدمة دعم عملاء، يمكن للمستخدمين استدعاء أمر شرطة مائلة مثل /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.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/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!"
        }
      }
    }