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

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

在 Google Chat 中,插件会以 Google Chat 应用的形式向用户显示。如需了解详情,请参阅扩展 Google Chat 概览

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

Chat 应用会向用户请求信息,以便在 Chat 中或之外执行操作,包括通过以下方式:

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

前提条件

Node.js

在 Google Chat 中运行的 Google Workspace 插件。如需构建一个,请完成 HTTP 快速入门

Apps 脚本

可在 Google Chat 中使用的 Google Workspace 插件。如需构建一个,请完成 Apps Script 快速入门

使用卡片构建表单

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

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

Chat 应用可以使用以下微件构建卡片:

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

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

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

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

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

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

添加多选菜单

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

您可以从以下数据源为多选菜单填充项:

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

从 Google Workspace 数据源填充项

如需使用 Google Workspace 数据源,请在 SelectionInput 微件中指定 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"
    }
  }
}

以下代码展示了聊天室的多选菜单。如需填充空格,选择输入会指定 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 网址或 Google Apps 脚本函数名称。如需查看展示如何返回建议项的完整示例,请参阅建议多选项部分。

接收来自互动 widget 的数据

每当用户点击某个按钮时,系统都会触发其 Chat 应用操作,并附带有关互动的信息。在事件载荷的 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 来查看一个菜单,该菜单会填充美国的城市,那么您的 Chat 应用可以在用户输入完毕之前自动建议 Atlanta。Chat 应用最多可建议 100 项内容。

如需在多选菜单中建议和动态填充项,卡片上的 SelectionInput Widget 必须指定用于查询外部数据源的函数。如需返回建议内容,该函数必须执行以下操作:

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

以下代码示例展示了 Chat 应用如何在卡片上的多选菜单中动态建议项。当用户在菜单中输入内容时,微件 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 interactive event.
* @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
  };
}

将数据转移到其他卡

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

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

如需从初始卡片传输数据输入,您可以使用包含微件的 name 和用户输入的值的 actionParameters 构建 button 微件,如以下示例所示:

{
  "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 应用会通过确认收到或返回错误来响应。

在以下示例中,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 应用或卡片会产生意外结果;例如,卡片消息可能不会显示。

虽然 Chat 界面中可能不会显示错误消息,但当 Chat 应用的错误日志记录功能处于开启状态时,描述性错误消息和日志数据可帮助您修正错误。如需有关查看、调试和修复错误的帮助,请参阅排查和修复 Google Chat 错误