收集和处理 Google Chat 用户的信息

本指南介绍了 Google Chat 应用如何通过在基于卡片的界面中构建表单输入来收集和处理用户信息。

一个包含各种不同 widget 的对话框。
图 1:一个打开对话框以收集联系信息的 Chat 应用。

聊天应用会通过以下方式请求用户提供信息,以便在 Chat 内或 Chat 外执行操作:

  • 配置设置。例如,允许用户自定义通知设置,或配置 Chat 应用并将其添加到一个或多个聊天室。
  • 在其他 Google Workspace 应用中创建或更新信息。例如,允许用户创建 Google 日历活动。
  • 允许用户访问和更新其他应用或 Web 服务中的资源。例如,Chat 应用可以帮助用户直接在聊天室中更新支持请求的状态。

前提条件

HTTP

可扩展 Google Chat 的 Google Workspace 加载项。如需构建一个,请完成 HTTP 快速入门

Apps 脚本

可扩展 Google Chat 的 Google Workspace 加载项。如需构建一个,请完成 Apps 脚本快速入门

使用卡片构建表单

为了收集信息,Chat 应用会设计表单及其输入,并将其构建到卡片中。如需向用户显示卡片,Chat 应用可以使用以下 Chat 接口:

  • 包含一个或多个卡片的聊天消息。
  • 对话框:从消息和主页在新窗口中打开的卡片。

聊天应用可以使用以下 widget 构建卡片:

  • 用于向用户请求信息的表单输入 widget。您可以选择向表单输入 widget 添加验证,以确保用户正确输入和格式化信息。聊天应用可以使用以下表单输入 widget:

    • 用于自由格式或建议文本的文本输入 (textInput)。
    • 选择输入源 (selectionInput) 是可选择的界面元素,例如复选框、单选按钮和下拉菜单。选择输入 widget 还可以填充和建议来自 Google Workspace 数据(例如 Chat 群组)或动态数据源的项目。如需了解详情,请参阅以下部分:添加多选菜单

    • 用于日期和时间条目的日期时间选择器 (dateTimePicker)。

  • 一个按钮 widget,以便用户可以提交他们在卡片中输入的值。用户点击该按钮后,Chat 应用即可处理其收到的信息

在以下示例中,卡片使用文本输入、日期时间选择器和选择输入来收集联系信息:

如需查看可用于收集信息的互动式 widget 的更多示例,请参阅 Google Chat API 文档中的设计互动式卡片或对话框

添加多选菜单

如需自定义选择项或让用户从动态数据源中选择项,Chat 应用可以使用多选菜单,这是一种 SelectionInput widget。例如,以下卡片显示了一个多选菜单,用户可以从联系人列表中动态选择:

您可以通过以下数据源填充多选菜单的项:

  • Google Workspace 数据,包括用户或用户所属的 Chat 聊天室。该菜单仅填充来自同一 Google Workspace 组织的项。
  • 外部数据源,例如关系型数据库。 例如,您可以使用多选菜单帮助用户从客户关系管理 (CRM) 系统中的销售线索列表中进行选择。

从 Google Workspace 数据源填充内容

如需使用 Google Workspace 数据源,请在 SelectionInput widget 中指定 platformDataSource 字段。与其他选择输入类型不同,您需要省略 SelectionItem 对象,因为这些选择项是从 Google Workspace 动态获取的。

以下代码显示了 Google Workspace 用户的多选菜单。 为了填充用户,选择输入将 commonDataSource 设置为 USER

JSON

{
  "selectionInput": {
    "name": "contacts",
    "type": "MULTI_SELECT",
    "label": "Selected contacts",
    "multiSelectMaxSelectedItems": 5,
    "multiSelectMinQueryLength": 1,
    "platformDataSource": {
      "commonDataSource": "USER"
    }
  }
}

以下代码展示了一个 Chat 空间的单选菜单。为了填充空间,选择输入指定了 hostAppDataSource 字段。多选菜单还会将 defaultToCurrentSpace 设置为 true,这会使当前空间成为菜单中的默认选择:

JSON

{
  "selectionInput": {
    "name": "spaces",
    "type": "MULTI_SELECT",
    "label": "Selected contacts",
    "multiSelectMaxSelectedItems": 3,
    "multiSelectMinQueryLength": 1,
    "platformDataSource": {
      "hostAppDataSource": {
        "chatDataSource": {
          "spaceDataSource": {
            "defaultToCurrentSpace": true
          }
        }
      }
    }
  }
}

从外部数据源填充商品

多选菜单还可以从第三方或外部数据源填充项。如需使用外部数据源,您需要在 SelectionInput widget 中指定 externalDataSource 字段,该字段包含用于查询数据源并返回相应项的函数。

为了减少对外部数据源的请求,您可以在用户在多选菜单中输入内容之前,先在其中显示建议的项。例如,您可以为用户填充最近搜索过的联系人。如需从外部数据源填充建议的商品,请指定静态 SelectionItem 对象。

以下代码展示了一个多选菜单,该菜单可查询外部数据源并从中填充项:

JSON

{
  "selectionInput": {
    "name": "contacts",
    "type": "MULTI_SELECT",
    "label": "Selected contacts",
    "multiSelectMaxSelectedItems": 3,
    "multiSelectMinQueryLength": 1,
    "externalDataSource": { "function": "FUNCTION" },
    // Suggested items loaded by default.
    // The list is static here but it could be dynamic.
    "items": [FUNCTION]
  }
}

FUNCTION 替换为查询外部数据库的 HTTP 网址或 Apps 脚本函数名称。如需查看有关如何返回建议项的完整示例,请参阅建议多选项部分。

从互动 widget 接收数据

每当用户点击按钮时,系统都会触发聊天应用的操作,并提供有关互动的相关信息。在事件载荷的 commonEventObject 中,formInputs 对象包含用户输入的任何值。

您可以从对象 commonEventObject.formInputs.WIDGET_NAME 中检索值,其中 WIDGET_NAME 是您为 widget 指定的 name 字段。这些值会以特定数据类型返回给 widget。

以下示例展示了事件对象的一部分,其中用户为每个 widget 输入了值:

{
  "commonEventObject": { "formInputs": {
    "contactName": { "stringInputs": {
      "value": ["Kai 0"]
    }},
    "contactBirthdate": { "dateInput": {
      "msSinceEpoch": 1000425600000
    }},
    "contactType": { "stringInputs": {
      "value": ["Personal"]
    }}
  }}
}

为了接收数据,Chat 应用会处理事件对象,以获取用户在 widget 中输入的值。下表显示了如何获取给定表单输入 widget 的值。对于每个 widget,该表格都会显示 widget 接受的数据类型、值在事件对象中的存储位置以及示例值。

表单输入微件 输入数据类型 来自事件对象的输入值 示例值
textInput stringInputs event.commonEventObject.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs 如需获取第一个值或唯一值,请使用 event.commonEventObject.formInputs.contactType.stringInputs.value[0] Personal
dateTimePicker,仅接受日期。 dateInput event.commonEventObject.formInputs.contactBirthdate.dateInput.msSinceEpoch 1000425600000

Chat 应用收到数据后,可以执行以下任一操作:

  • 对于包含多选菜单的卡片,请根据用户在菜单中输入的内容填充或建议项目
  • 将数据转移到另一张卡片,以便用户查看自己的信息或继续填写表单的下一部分。
  • 回复用户,确认用户已成功填写表单。

建议多选项目

如果卡片包含一个从外部数据源填充项的多选菜单,Chat 应用可以根据用户在菜单中输入的内容返回建议项。例如,如果用户开始输入 Atl 以打开填充美国城市的菜单,您的聊天应用可以在用户完成输入之前自动建议 Atlanta。Chat 应用最多可建议 100 项。

如需在多选菜单中建议并动态填充项,卡片上的 SelectionInput widget 必须指定一个查询外部数据源的函数。为了返回建议的项目,该函数必须执行以下操作:

  1. 处理 event 对象,这是 Chat 应用在用户在菜单中输入内容时收到的对象。
  2. 从事件对象中获取用户输入的值,该值在 event.commonEventObject.parameters["autocomplete_widget_query"] 字段中表示。
  3. 使用用户输入的值查询数据源,以获取一个或多个 SelectionItems 来向用户提供建议。
  4. 通过返回包含 modifyCard 对象的 action RenderActions 来返回建议的项目。

以下代码示例展示了 Chat 应用如何在卡片上的多选菜单中动态建议项。当用户在菜单中输入内容时,widget 的 externalDataSource 字段中提供的函数或端点会查询外部数据源,并建议用户可以选择的项。

Node.js

/**
 * Google Cloud Function that responds to events sent from a
 * Google Chat space.
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
exports.selectionInput = function selectionInput(req, res) {
  if (req.method === 'GET' || !req.body.chat) {
    return res.send('Hello! This function is meant to be used ' +
        'in a Google Chat Space.');
  }
  // Stores the Google Chat event
  const chatEvent = req.body.chat;

  // Handle user interaction with multiselect.
  if(chatEvent.widgetUpdatedPayload) {
    return res.send(queryContacts(req.body));
  }
  // Replies with a card that contains the multiselect menu.
  return res.send({ hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    cardsV2: [{
      cardId: "contactSelector",
      card: { sections:[{ widgets: [{
        selectionInput: {
          name: "contacts",
          type: "MULTI_SELECT",
          label: "Selected contacts",
          multiSelectMaxSelectedItems: 3,
          multiSelectMinQueryLength: 1,
          externalDataSource: { function: "FUNCTION_URL" },
          // Suggested items loaded by default.
          // The list is static here but it could be dynamic.
          items: [getSuggestedContact("3")]
        }
      }]}]}
    }]
  }}}}});
};

/**
* Get contact suggestions based on text typed by users.
*
* @param {Object} event the event object that contains the user's query
* @return {Object} suggestions
*/
function queryContacts(event) {
  const query = event.commonEventObject.parameters["autocomplete_widget_query"];
  return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [
    // The list is static here but it could be dynamic.
    getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("5")
  // Only return items based on the query from the user.
  ].filter(e => !query || e.text.includes(query)) }}}]}};
}

/**
 * Generate a suggested contact given an ID.
 *
 * @param {String} id The ID of the contact to return.
 * @return {Object} The contact formatted as a selection item in the menu.
 */
function getSuggestedContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

FUNCTION_URL 替换为用于查询外部数据源的 HTTP 端点。

Apps 脚本

/**
* Responds to a Message trigger in Google Chat.
*
* @param {Object} event the event object from Google Chat
* @return {Object} Response from the Chat app.
*/
function onMessage(event) {
  // Replies with a card that contains the multiselect menu.
  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    cardsV2: [{
      cardId: "contactSelector",
      card: { sections:[{ widgets: [{
        selectionInput: {
          name: "contacts",
          type: "MULTI_SELECT",
          label: "Selected contacts",
          multiSelectMaxSelectedItems: 3,
          multiSelectMinQueryLength: 1,
          externalDataSource: { function: "queryContacts" },
          // Suggested items loaded by default.
          // The list is static here but it could be dynamic.
          items: [getSuggestedContact("3")]
        }
      }]}]}
    }]
  }}}}};
}

/**
* Get contact suggestions based on text typed by users.
*
* @param {Object} event the event object that contains the user's query
* @return {Object} suggestions
*/
function queryContacts(event) {
  const query = event.commonEventObject.parameters["autocomplete_widget_query"];
  return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [
    // The list is static here but it could be dynamic.
    getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("5")
  // Only return items based on the query from the user.
  ].filter(e => !query || e.text.includes(query)) }}}]}};
}

/**
* Generate a suggested contact given an ID.
*
* @param {String} id The ID of the contact to return.
* @return {Object} The contact formatted as a selection item in the menu.
*/
function getSuggestedContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

将数据转移到另一张卡

用户提交卡片中的信息后,您可能需要返回其他卡片以执行以下任一操作:

  • 通过创建不同的部分,帮助用户完成较长的表单。
  • 让用户预览并确认初始卡片中的信息,以便他们在提交之前检查自己的回答。
  • 动态填充表单的其余部分。例如,为了提示用户创建预约,聊天应用可以显示一个初始卡片,请求用户提供预约原因,然后填充另一个卡片,其中根据预约类型提供可预约的时间。

如需转移初始卡片中的输入数据,您可以构建包含 widget 的 name 和用户输入的值的 actionParameters,如下例所示:button

Node.js

{
  "buttonList": { "buttons": [{
    "text": "Submit",
    "onClick": { "action": {
      "function": "FUNCTION_URL", // Must be an `https` endpoint.
      "parameters": [
        {
          "key": "WIDGET_NAME",
          "value": "USER_INPUT_VALUE"
        },
        // Can specify multiple parameters
      ]
    }}
  }]}
}

Apps 脚本

{
  "buttonList": { "buttons": [{
    "text": "Submit",
    "onClick": { "action": {
      "function": "submitForm",
      "parameters": [
        {
          "key": "WIDGET_NAME",
          "value": "USER_INPUT_VALUE"
        },
        // Can specify multiple parameters
      ]
    }}
  }]}
}

其中,WIDGET_NAME 是 widget 的 nameUSER_INPUT_VALUE 是用户输入的内容。例如,对于收集用户姓名的文本输入,微件名称为 contactName,示例值为 Kai O

当用户点击该按钮时,您的 Chat 应用会收到一个事件对象,您可以从中接收数据

回复表单提交

在收到来自卡片消息或对话框的数据后,Chat 应用会通过确认收到数据或返回错误来做出响应。

在以下示例中,聊天应用发送了一条文本消息,以确认其已成功收到从卡片消息提交的表单。

Node.js

/**
 * Google Cloud Function that handles all Google Workspace Add On events for
 * the contact manager app.
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
exports.contactManager = function contactManager(req, res) {
  const chatEvent = req.body.chat;
  const chatMessage = chatEvent.messagePayload.message;

  // Handle message payloads in the event object
  if(chatEvent.messagePayload) {
    return res.send(handleMessage(chatMessage, chatEvent.user));
  // Handle button clicks on the card
  } else if(chatEvent.buttonClickedPayload) {
    switch(req.body.commonEventObject.parameters.actionName) {
        case "openDialog":
            return res.send(openDialog());
        case "openNextCard":
            return res.send(openNextCard(req.body));
        case "submitForm":
            return res.send(submitForm(req.body));
    }
  }
};

/**
 * Submits information from a dialog or card message.
 *
 * @param {Object} event the interactive event with form inputs.
 * @return {Object} a message response that posts a private message.
 */
function submitForm(event) {
  const chatUser = event.chat.user;
  const contactName = event.commonEventObject.parameters["contactName"];

  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    privateMessageViewer: chatUser,
    text: "✅ " + contactName + " has been added to your contacts."
  }}}}};
}

Apps 脚本

/**
 * Sends private text message that confirms submission.
 *
 * @param {Object} event the interactive event with form inputs.
 * @return {Object} a message response that posts a private message.
 */
function submitForm(event) {
  const chatUser = event.chat.user;
  const contactName = event.commonEventObject.parameters["contactName"];

  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    privateMessageViewer: chatUser,
    text: "✅ " + contactName + " has been added to your contacts."
  }}}}};
}

若要处理并关闭对话框,您需要返回一个 RenderActions 对象,该对象用于指定您是否要发送确认消息、更新原始消息或卡片,或者只是关闭对话框。如需了解相关步骤,请参阅关闭对话框

问题排查

当 Google Chat 应用或卡片返回错误时,Chat 界面会显示一条消息,提示“出了点问题”。 或“无法处理您的请求”。有时,Chat 界面不会显示任何错误消息,但 Chat 应用或卡片会产生意外结果;例如,卡片消息可能不会显示。

虽然聊天界面中可能不会显示错误消息,但当为聊天应用启用错误日志记录功能后,系统会提供描述性错误消息和日志数据,帮助您修复错误。如需有关查看、调试和修复错误的帮助,请参阅排查和修复 Google Chat 错误