Как собирать и обрабатывать информацию пользователей Google Chat

В этом руководстве рассказывается, как приложения Google Chat могут собирать и обрабатывать информацию от пользователей, создавая поля ввода в интерфейсах на основе карточек.

Диалоговое окно с различными виджетами.
Рисунок 1. Пример приложения Chat, которое открывает диалоговое окно для сбора контактной информации.

Приложения Chat запрашивают у пользователей информацию для выполнения действий в Chat или за его пределами, в том числе следующими способами:

  • Настройте параметры. Например, чтобы пользователи могли настраивать уведомления или добавлять приложение Chat в одну или несколько чат-групп.
  • Создавать или изменять информацию в других приложениях Google Workspace. Например, разрешите пользователям создавать мероприятия в Google Календаре.
  • Предоставлять пользователям доступ к ресурсам в других приложениях или веб-сервисах и возможность их изменять. Например, приложение Chat может помочь пользователям обновить статус запроса в службу поддержки прямо из чат-группы.

Требования

HTTP

Приложение Google Chat, которое получает и обрабатывает действия пользователей. Чтобы создать его, выполните краткое руководство по HTTP.

Apps Script

Приложение Google Chat, которое получает и обрабатывает действия пользователей. Чтобы создать такой скрипт, выполните инструкции по началу работы с Apps Script.

Как создавать формы с помощью карточек

Чтобы собирать информацию, приложения Chat создают формы и их поля ввода, а затем встраивают их в карточки. Чтобы показывать пользователям карточки, приложения Chat могут использовать следующие интерфейсы Chat:

  • Сообщения, содержащие одну или несколько карточек.
  • Главные страницы – карточка, которая появляется на вкладке Главная в прямых переписках с приложением Chat.
  • Диалоговые окна – карточки, которые открываются в новом окне из сообщений и на главных страницах.

Приложения для Chat могут создавать карточки с помощью следующих виджетов:

В примере ниже карточка собирает контактную информацию с помощью текстового поля, выбора даты и времени и выбора варианта:

Другие примеры интерактивных виджетов, которые можно использовать для сбора информации, приведены в статье Как создать интерактивную карточку или диалоговое окно.

Как добавить раскрывающееся меню

Чтобы настроить элементы выбора или разрешить пользователям выбирать один элемент из динамического источника данных, приложения Chat могут использовать раскрывающиеся меню, которые являются одним из типов виджетов SelectionInput. Например, на карточке ниже показано раскрывающееся меню, в котором пользователи могут выбирать контакты из списка:

Вы можете заполнить раскрывающееся меню данными из следующих источников:

Как заполнять объекты из источника данных Google Workspace

Чтобы заполнить объекты из источников данных Google Workspace, например пользователей Google Workspace, укажите поле platformDataSource в объекте DataSourceConfig. В отличие от других типов входных данных для выбора, объекты SelectionItem не указываются, поскольку эти элементы выбора динамически извлекаются из Google Workspace.

В приведенном ниже коде показано раскрывающееся меню пользователей Google Workspace:

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "widgets": [
        {
          "selectionInput": {
            "name": "contacts",
            "type": "DROPDOWN",
            "label": "Select contact from organization",
            "data_source_configs": [
              {
                "platformDataSource": {
                  "commonDataSource": "USER"
                },
                "min_characters_trigger": 1
              }
            ]
          }
        }
      ]
    }
  ]
}

Как заполнять сведения о товарах из внешнего источника данных

В раскрывающихся меню также могут быть элементы из стороннего или внешнего источника данных. Чтобы использовать внешний источник данных, укажите поле remoteDataSource в объекте DataSourceConfig, который содержит функцию, запрашивающую и возвращающую элементы из источника данных.

Чтобы уменьшить количество запросов к внешнему источнику данных, вы можете добавить в раскрывающееся меню предлагаемые элементы, которые будут показываться до того, как пользователь начнет вводить текст. Чтобы заполнить список рекомендуемых товаров из внешнего источника данных, укажите статические объекты SelectionItem.

В приведенном ниже примере кода показано раскрывающееся меню, которое запрашивает и заполняет элементы из внешнего источника данных:

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "widgets": [
        {
          "selectionInput": {
            "name": "crm_leads",
            "type": "DROPDOWN",
            "label": "Select CRM Lead",
            "data_source_configs": [
              {
                "remoteDataSource": {
                  "function": "getCrmLeads"
                },
                "min_characters_trigger": 2
              }
            ],
            "items": [
              {
                "text": "Suggested Lead 1",
                "value": "lead-1"
              }
            ]
          }
        }
      ]
    }
  ]
}

Полный пример того, как возвращать предложенные товары, приведен в разделе Предлагайте товары для выбора.

Как добавить меню с возможностью выбора нескольких вариантов

Чтобы настроить элементы выбора или разрешить пользователям выбирать элементы из динамического источника данных, приложения Chat могут использовать меню с множественным выбором, которые являются одним из типов виджетов SelectionInput. Например, на карточке ниже показано меню с возможностью выбора нескольких вариантов, в котором пользователи могут динамически выбирать контакты из списка:

В меню с возможностью выбора нескольких вариантов можно добавить элементы из следующих источников данных:

  • Данные Google Workspace, в том числе пользователи или чат-группы, в которых состоит пользователь. В меню будут показываться только объекты из той же организации Google Workspace.
  • Внешние источники данных, например реляционная база данных. Например, меню с множественным выбором можно использовать, чтобы помочь пользователю выбрать потенциальных клиентов из списка в системе управления взаимоотношениями с клиентами (CRM).

Как заполнять объекты из источника данных Google Workspace

Чтобы использовать источники данных Google Workspace, укажите поле platformDataSource в виджете SelectionInput. В отличие от других типов входных данных для выбора, объекты 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
          }
        }
      }
    }
  }
}

Как заполнять сведения о товарах из внешнего источника данных

Кроме того, меню с множественным выбором могут заполняться элементами из стороннего или внешнего источника данных. Чтобы использовать внешний источник данных, укажите поле externalDataSource в виджете SelectionInput, содержащем функцию, которая запрашивает и возвращает элементы из источника данных.

Чтобы уменьшить количество запросов к внешнему источнику данных, вы можете добавить в меню с множественным выбором предлагаемые варианты, которые будут показываться до того, как пользователь начнет вводить текст. Например, вы можете заполнить список недавно найденных контактов для пользователя. Чтобы заполнить список рекомендуемых товаров из внешнего источника данных, укажите статические объекты SelectionItem.

В приведенном ниже фрагменте кода показано меню с возможностью выбора нескольких вариантов, которое запрашивает и заполняет элементы из внешнего источника данных:

Node.js

node/chat/selection-input/index.js
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")]
}

Замените FUNCTION_URL конечной точкой HTTP, которая запрашивает внешний источник данных.

Python

python/chat/selection-input/main.py
'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': [get_suggested_contact("3")]
}

Замените FUNCTION_URL конечной точкой HTTP, которая запрашивает внешний источник данных.

Java

java/chat/selection-input/src/main/java/com/google/chat/selectionInput/App.java
.setSelectionInput(new GoogleAppsCardV1SelectionInput()
  .setName("contacts")
  .setType("MULTI_SELECT")
  .setLabel("Selected contacts")
  .setMultiSelectMaxSelectedItems(3)
  .setMultiSelectMinQueryLength(1)
  .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL))
  // Suggested items loaded by default.
  // The list is static here but it could be dynamic.
  .setItems(List.of(getSuggestedContact("3")))))))))));

Замените FUNCTION_URL конечной точкой HTTP, которая запрашивает внешний источник данных.

Apps Script

В этом примере отправляется сообщение с карточкой, для чего возвращается JSON-код карточки. Вы также можете использовать сервис карточек Apps Script.

apps-script/chat/selection-input/selection-input.gs
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")]
}

Полный пример того, как возвращать предложенные товары, приведен в разделе Предлагайте товары для выбора.

Как получать данные от интерактивных виджетов

Когда пользователь нажимает кнопку, запускается действие приложения Chat с информацией о взаимодействии. В объекте commonEventObject полезной нагрузки события объект formInputs содержит все значения, введенные пользователем.

Вы можете получить значения из объекта event.commonEventObject.formInputs.WIDGET_NAME, где WIDGET_NAME – это поле name, которое вы указали для виджета. Значения возвращаются в виде определенного типа данных для виджета.

Ниже показана часть объекта события, в которой пользователь ввел значения для каждого виджета:

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

Чтобы получить данные, приложение Chat обрабатывает объект события и извлекает значения, которые пользователи вводят в виджеты. В таблице ниже показано, как получить значение для определенного виджета ввода формы. В таблице для каждого виджета указан тип данных, который он принимает, где хранится значение в объекте события и пример значения.

Виджет ввода формы Тип входных данных Входное значение из объекта события Пример значения
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 на карточке должен указывать функцию, которая запрашивает внешний источник данных. Для меню с множественным выбором нужно указать поле externalDataSource. Для раскрывающихся меню нужно указать поле remoteDataSource в объекте DataSourceConfig.

Вы также можете указать, сколько символов должен ввести пользователь, прежде чем меню предложит варианты. Для меню с множественным выбором задайте поле multiSelectMinQueryLength. Для раскрывающихся меню задайте поле min_characters_trigger в элементе DataSourceConfig.

Чтобы возвращать предложенные объекты, функция должна:

  1. Обрабатывать объект события, который приложение Chat получает, когда пользователи вводят текст в меню.
  2. Из объекта события получите значение, которое ввел пользователь. Оно представлено в поле event.commonEventObject.parameters["autocomplete_widget_query"].
  3. Отправьте запрос к источнику данных, используя введенное пользователем значение, чтобы получить одно или несколько значений SelectionItems и предложить их пользователю.
  4. Чтобы вернуть предложенные элементы, верните объект RenderActions с объектом modifyCard.

В следующем примере кода показано, как приложение Chat динамически предлагает элементы в меню с множественным выбором на карточке. Когда пользователь вводит текст в меню, функция или конечная точка, указанная в поле externalDataSource виджета, отправляет запрос к внешнему источнику данных и предлагает варианты, которые пользователь может выбрать.

Node.js

node/chat/selection-input/index.js
/**
 * Web app 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
 */
app.post('/', async (req, res) => {
  // Stores the Google Chat event
  const chatEvent = req.body.chat;

  // Handle user interaction with multiselect.
  if(chatEvent.widgetUpdatedPayload) {
    return res.json(queryContacts(req.body));
  }

  // Replies with a card that contains the multiselect menu.
  return res.json({ 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, которая запрашивает внешний источник данных.

Python

python/chat/selection-input/main.py
@app.route('/', methods=['POST'])
def post() -> Mapping[str, Any]:
  """Handle requests from Google Chat

  Returns:
      Mapping[str, Any]: The response
  """
  # Stores the Google Chat event
  chatEvent = request.get_json().get('chat')

  # Handle user interaction with multiselect.
  if chatEvent.get('widgetUpdatedPayload') is not None:
    return json.jsonify(query_contacts(request.get_json()))

  # Replies with a card that contains the multiselect menu.
  return json.jsonify({ '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': [get_suggested_contact("3")]
        }
      }]}]}
    }]}
  }}}})


def query_contacts(event: dict) -> dict:
  """Get contact suggestions based on text typed by users.

  Args:
      event (Mapping[str, Any]): The event object that contains the user's query

  Returns:
      Mapping[str, Any]: The response with contact suggestions.
  """
  query = event.get("commonEventObject").get("parameters").get("autocomplete_widget_query")
  return { 'action': { 'modifyOperations': [{ 'updateWidget': { 'selectionInputWidgetSuggestions': { 'suggestions': list(
    filter(lambda e: query is None or query in e["text"], [
      # The list is static here but it could be dynamic.
      get_suggested_contact("1"), get_suggested_contact("2"), get_suggested_contact("3"), get_suggested_contact("4"), get_suggested_contact("5")
    # Only return items based on the query from the user
    ])
  )}}}]}}


def get_suggested_contact(id: str) -> dict:
  """Generate a suggested contact given an ID.

  Args:
      id (str): The ID of the contact to return.

  Returns:
      Mapping[str, Any]: The contact formatted as a selection item in the menu.
  """
  return {
    'value': id,
    'startIconUri': "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    'text': "Contact " + id
  }

Замените FUNCTION_URL конечной точкой HTTP, которая запрашивает внешний источник данных.

Java

java/chat/selection-input/src/main/java/com/google/chat/selectionInput/App.java
@SpringBootApplication
@RestController
// Web app that responds to events sent from a Google Chat space.
public class App {
  private static final String FUNCTION_URL = "your-function-url";

  public static void main(String[] args) {
    SpringApplication.run(App.class, args);
  }

  /**
   * Handle requests from Google Chat
   * 
   * @param event the event object sent by Google Chat
   * @return The response to be sent back to Google Chat
   */
  @PostMapping("/")
  @ResponseBody
  public GenericJson onEvent(@RequestBody JsonNode event) throws Exception {
    // Stores the Google Chat event
    JsonNode chatEvent = event.at("/chat");

    // Handle user interaction with multiselect.
    if (!chatEvent.at("/widgetUpdatedPayload").isEmpty()) {
      return queryContacts(event);
    }

    // Replies with a card that contains the multiselect menu.
    Message message = new Message().setCardsV2(List.of(new CardWithId()
      .setCardId("contactSelector")
      .setCard(new GoogleAppsCardV1Card()
        .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(new GoogleAppsCardV1Widget()
          .setSelectionInput(new GoogleAppsCardV1SelectionInput()
            .setName("contacts")
            .setType("MULTI_SELECT")
            .setLabel("Selected contacts")
            .setMultiSelectMaxSelectedItems(3)
            .setMultiSelectMinQueryLength(1)
            .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL))
            // Suggested items loaded by default.
            // The list is static here but it could be dynamic.
            .setItems(List.of(getSuggestedContact("3")))))))))));

    return new GenericJson() {{
      put("hostAppDataAction", new GenericJson() {{
        put("chatDataAction", new GenericJson() {{
          put("createMessageAction", new GenericJson() {{
            put("message", message);
          }});
        }});
      }});
    }};
  }

  /**
   * Get contact suggestions based on text typed by users.
   *
   * @param event the event object that contains the user's query.
   * @return The response with contact suggestions.
   */
  GenericJson queryContacts(JsonNode event) throws Exception {
    String query = event.at("/commonEventObject/parameters/autocomplete_widget_query").asText();
    List<GoogleAppsCardV1SelectionItem> suggestions = List.of(
      // 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
    ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList();

    return new GenericJson() {{
      put("action", new GenericJson() {{
        put("modifyOperations", List.of(new GenericJson() {{
          put("updateWidget", new GenericJson() {{
            put("selectionInputWidgetSuggestions", new GenericJson() {{
              put("suggestions", suggestions);
            }});
          }});
        }}));
      }});
    }};
  }

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

Замените FUNCTION_URL конечной точкой HTTP, которая запрашивает внешний источник данных.

Apps Script

В этом примере отправляется сообщение с карточкой, для чего возвращается JSON-код карточки. Вы также можете использовать сервис карточек Apps Script.

apps-script/chat/selection-input/selection-input.gs
/**
* 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
  };
}

Как перенести данные на другую карту

После того как пользователь отправит информацию с карточки, вам может понадобиться вернуть дополнительные карточки, чтобы:

  • Разделите длинную форму на несколько разделов, чтобы пользователям было удобнее ее заполнять.
  • Предоставьте пользователям возможность просматривать и подтверждать информацию с первой карточки, чтобы они могли проверить свои ответы перед отправкой.
  • Динамически заполните оставшиеся части формы. Например, чтобы предложить пользователям создать встречу, приложение Chat может сначала показать карточку с запросом причины встречи, а затем заполнить другую карточку с доступным временем на основе типа встречи.

Чтобы перенести введенные данные с первой карточки, создайте виджет button с помощью actionParameters, содержащего виджет name и значение, введенное пользователем, как показано в следующем примере:

Node.js

node/chat/contact-form-app/index.js
{ buttonList: { buttons: [{
  text: "SUBMIT",
  onClick: { action: {
    function: FUNCTION_URL,
    parameters: [
      { key: "actionName", value: "submitDialog" },
      // Pass input values as parameters for last dialog step (submission)
      { key: "contactName", value: name },
      { key: "contactBirthdate", value: birthdate },
      { key: "contactType", value: type }
    ]
  }}
}]}}

Замените FUNCTION_URL конечной точкой HTTP, которая обрабатывает нажатия кнопок.

Python

python/chat/contact-form-app/main.py
{ 'buttonList': { 'buttons': [{
  'text': "SUBMIT",
  'onClick': { 'action': {
    'function': FUNCTION_URL,
    'parameters': [
      { 'key': "actionName", 'value': "submitDialog" },
      # Pass input values as parameters for last dialog step (submission)
      { 'key': "contactName", 'value': name },
      { 'key': "contactBirthdate", 'value': birthdate },
      { 'key': "contactType", 'value': type }
    ]
  }}
}]}}

Замените FUNCTION_URL конечной точкой HTTP, которая обрабатывает нажатия кнопок.

Java

java/chat/contact-form-app/src/main/java/com/google/chat/contact/App.java
new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(
  new GoogleAppsCardV1Button()
    .setText("SUBMIT")
    .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
      .setFunction(FUNCTION_URL)
      .setParameters(List.of(
        new GoogleAppsCardV1ActionParameter().setKey("actionName").setValue("submitDialog"),
        // Pass input values as parameters for last dialog step (submission)
        new GoogleAppsCardV1ActionParameter().setKey("contactName").setValue(name),
        new GoogleAppsCardV1ActionParameter().setKey("contactBirthdate").setValue(birthdate),
        new GoogleAppsCardV1ActionParameter().setKey("contactType").setValue(type))))))))))));

Замените FUNCTION_URL конечной точкой HTTP, которая обрабатывает нажатия кнопок.

Apps Script

В этом примере отправляется сообщение с карточкой, для чего возвращается JSON-код карточки. Вы также можете использовать сервис карточек Apps Script.

apps-script/chat/contact-form-app/Code.gs
{ buttonList: { buttons: [{
  text: "SUBMIT",
  onClick: { action: {
    function: "submitDialog",
    // Pass input values as parameters for last dialog step (submission)
    parameters: [
      { key: "contactName", value: name },
      { key: "contactBirthdate", value: birthdate },
      { key: "contactType", value: type }
    ]
  }}
}]}}

Когда пользователь нажимает на кнопку, ваше приложение Chat получает объект события, из которого можно получить данные.

Как ответить на отправленную форму

После получения данных из сообщения с карточкой или диалогового окна приложение Chat отвечает, подтверждая получение или возвращая ошибку.

В следующем примере приложение Chat отправляет текстовое сообщение, чтобы подтвердить, что оно успешно получило форму, отправленную из сообщения с карточкой.

Node.js

node/chat/contact-form-app/index.js
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
  text: "✅ " + event.commonEventObject.parameters["contactName"] + " has been added to your contacts."
}}}}};

Python

python/chat/contact-form-app/main.py
return { 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': { 'message': {
  'text': "✅ " + event.get('commonEventObject').get('parameters')["contactName"] + " has been added to your contacts."
}}}}}

Java

java/chat/contact-form-app/src/main/java/com/google/chat/contact/App.java
return new GenericJson() {{
  put("hostAppDataAction", new GenericJson() {{
    put("chatDataAction", new GenericJson() {{
      put("createMessageAction", new GenericJson() {{
        put("message", new Message()
          .setText( "✅ " + event.at("/commonEventObject/parameters/contactName").asText() +
                    " has been added to your contacts."));
      }});
    }});
  }});
}};

Apps Script

В этом примере отправляется сообщение с карточкой, для чего возвращается JSON-код карточки. Вы также можете использовать сервис карточек Apps Script.

apps-script/chat/contact-form-app/Code.gs
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
  text: "✅ " + event.commonEventObject.parameters["contactName"] + " has been added to your contacts."
}}}}};

Чтобы обработать и закрыть диалоговое окно, вы возвращаете объект RenderActions, который указывает, хотите ли вы отправить подтверждающее сообщение, обновить исходное сообщение или карточку или просто закрыть диалоговое окно. Подробнее о том, как закрыть диалоговое окно…

Устранение неполадок

В этом разделе приведены инструкции по устранению неполадок, связанных с определенными кодами ошибок и поведением диалоговых окон в Chat во время выполнения.

При взаимодействии с диалоговым окном возвращается ошибка "При вызове дополнения возникла неизвестная ошибка"

Если при взаимодействии с диалоговым окном вы видите журналы ошибок с сообщением "Неизвестная ошибка при вызове дополнения" и кодом 13, это обычно указывает на внутреннюю ошибку или на то, что конечная точка HTTP приложения Chat не смогла обработать запрос или вернуть действительный ответ.

Чтобы устранить эту ошибку:

  • Проверьте журналы конечной точки HTTP на наличие необработанных исключений или сбоев.
  • Убедитесь, что конечная точка отвечает на запросы в течение 30 секунд. Если выполнение конечной точки занимает больше 30 секунд, Chat не может обработать ответ и взаимодействие завершается неудачно. Подробнее об ограничении частоты запросов и рекомендациях…
  • Убедитесь, что конечная точка возвращает действительный ответ. Для отправки диалогового окна конечная точка должна возвращать объект RenderActions в правильном формате JSON. Если ответ неправильно сформирован или не содержит обязательных полей, взаимодействие с диалоговым окном может завершиться неудачно.

Если приложение Google Chat или карточка возвращает ошибку, в интерфейсе Chat появляется сообщение "Что-то пошло не так". или "Не удалось обработать запрос". Иногда в интерфейсе Chat не показывается сообщение об ошибке, но приложение Chat или карточка выдает неожиданный результат, например не появляется сообщение на карточке.

Хотя в интерфейсе Chat может не показываться сообщение об ошибке, при включенном ведении журнала ошибок для приложений Chat вам будут доступны подробные сообщения об ошибках и данные журнала, которые помогут устранить неполадки. Чтобы узнать, как просматривать, отлаживать и исправлять ошибки, ознакомьтесь с разделом Устранение неполадок в Google Chat.

Приложения Chat, не являющиеся дополнениями: чтение данных, введенных пользователями в формы на карточках

Ниже приведена документация по приложениям Chat, которые не являются дополнениями Google Workspace. Чтобы перенести приложение Chat, которое не является дополнением, ознакомьтесь со статьей Как преобразовать приложение Google Chat в дополнение Google Workspace.

Как создавать формы с помощью карточек

Пример приложения Chat, которое не является дополнением и использует форму обратной связи с текстовым полем, инструментом выбора даты и времени и полем выбора, приведен в следующем коде:

Node.js

node/contact-form-app/index.js
/**
 * The section of the contact card that contains the form input widgets. Used in a dialog and card message.
 * To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder
 */
const CONTACT_FORM_WIDGETS = [
  {
    "textInput": {
      "name": "contactName",
      "label": "First and last name",
      "type": "SINGLE_LINE"
    }
  },
  {
    "dateTimePicker": {
      "name": "contactBirthdate",
      "label": "Birthdate",
      "type": "DATE_ONLY"
    }
  },
  {
    "selectionInput": {
      "name": "contactType",
      "label": "Contact type",
      "type": "RADIO_BUTTON",
      "items": [
        {
          "text": "Work",
          "value": "Work",
          "selected": false
        },
        {
          "text": "Personal",
          "value": "Personal",
          "selected": false
        }
      ]
    }
  }
];

Python

python/contact-form-app/main.py
# The section of the contact card that contains the form input widgets. Used in a dialog and card message.
# To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder
CONTACT_FORM_WIDGETS = [
  {
    "textInput": {
      "name": "contactName",
      "label": "First and last name",
      "type": "SINGLE_LINE"
    }
  },
  {
    "dateTimePicker": {
      "name": "contactBirthdate",
      "label": "Birthdate",
      "type": "DATE_ONLY"
    }
  },
  {
    "selectionInput": {
      "name": "contactType",
      "label": "Contact type",
      "type": "RADIO_BUTTON",
      "items": [
        {
          "text": "Work",
          "value": "Work",
          "selected": False
        },
        {
          "text": "Personal",
          "value": "Personal",
          "selected": False
        }
      ]
    }
  }
]

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
// The section of the contact card that contains the form input widgets. Used in a dialog and card message.
// To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder
final static private List<GoogleAppsCardV1Widget> CONTACT_FORM_WIDGETS = List.of(
  new GoogleAppsCardV1Widget().setTextInput(new GoogleAppsCardV1TextInput()
    .setName("contactName")
    .setLabel("First and last name")
    .setType("SINGLE_LINE")),
  new GoogleAppsCardV1Widget().setDateTimePicker(new GoogleAppsCardV1DateTimePicker()
    .setName("contactBirthdate")
    .setLabel("Birthdate")
    .setType("DATE_ONLY")),
  new GoogleAppsCardV1Widget().setSelectionInput(new GoogleAppsCardV1SelectionInput()
    .setName("contactType")
    .setLabel("Contact type")
    .setType("RADIO_BUTTON")
    .setItems(List.of(
      new GoogleAppsCardV1SelectionItem()
        .setText("Work")
        .setValue("Work")
        .setSelected(false),
      new GoogleAppsCardV1SelectionItem()
        .setText("Personal")
        .setValue("Personal")
        .setSelected(false)))));

Apps Script

apps-script/contact-form-app/contactForm.gs
/**
 * The section of the contact card that contains the form input widgets. Used in a dialog and card message.
 * To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder
 */
const CONTACT_FORM_WIDGETS = [
  {
    "textInput": {
      "name": "contactName",
      "label": "First and last name",
      "type": "SINGLE_LINE"
    }
  },
  {
    "dateTimePicker": {
      "name": "contactBirthdate",
      "label": "Birthdate",
      "type": "DATE_ONLY"
    }
  },
  {
    "selectionInput": {
      "name": "contactType",
      "label": "Contact type",
      "type": "RADIO_BUTTON",
      "items": [
        {
          "text": "Work",
          "value": "Work",
          "selected": false
        },
        {
          "text": "Personal",
          "value": "Personal",
          "selected": false
        }
      ]
    }
  }
];

Как получать данные от интерактивных виджетов

Когда пользователь нажимает кнопку, приложения Chat, которые не являются дополнениями, получают событие взаимодействия в зависимости от местоположения кнопки:

  • Если кнопка находится в сообщении или диалоговом окне, приложения Chat, которые не являются дополнениями, получают событие взаимодействия CARD_CLICKED, содержащее информацию о взаимодействии. Полезная нагрузка событий взаимодействия CARD_CLICKED содержит объект common.formInputs (event.common.formInputs) со всеми значениями, которые вводит пользователь.

    Вы можете получить значения из объекта common.formInputs.WIDGET_NAME, где WIDGET_NAME – это поле name, указанное для виджета. Значения возвращаются в виде определенного типа данных для виджета (представленного как объект Inputs).

    Ниже показана часть события взаимодействия CARD_CLICKED, в которой пользователь ввел значения для каждого виджета:

    HTTP

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

    Apps Script

    {
      "type": "CARD_CLICKED",
      "common": { "formInputs": {
        "contactName": { "": { "stringInputs": {
          "value": ["Kai 0"]
        }}},
        "contactBirthdate": { "": { "dateInput": {
          "msSinceEpoch": 1000425600000
        }}},
          "contactType": { "": { "stringInputs": {
          "value": ["Personal"]
        }}}
      }}
    }
    
  • Если кнопка находится на главной странице, приложения Chat, которые не являются дополнениями, получают событие взаимодействия SUBMIT_FORM. Полезная нагрузка события взаимодействия содержит объект commonEventObject.formInputs (event.commonEventObject.formInputs) со всеми значениями, которые ввел пользователь.

    Вы можете получить значения из объекта commonEventObject.formInputs.WIDGET_NAME, где WIDGET_NAME – это поле name, указанное для виджета. Значения возвращаются в виде определенного типа данных для виджета (представленного как объект Inputs).

    Ниже показана часть события взаимодействия SUBMIT_FORM, в которой пользователь ввел значения для каждого виджета:

    HTTP

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

    Apps Script

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

Чтобы получить данные, приложение Chat, не являющееся дополнением, обрабатывает событие взаимодействия и получает значения, которые пользователи вводят в виджеты. В таблице ниже показано, как получить значение для определенного виджета ввода формы. Для каждого виджета в таблице указаны тип данных, который принимает виджет, место хранения значения в событии взаимодействия и пример значения.

Виджет ввода формы Тип входных данных Значение, полученное из события взаимодействия Пример значения
textInput stringInputs event.common.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs Чтобы получить первое или единственное значение, event.common.formInputs.contactType.stringInputs.value[0] Personal
dateTimePicker, в котором можно указать только даты. dateInput event.common.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Как перенести данные на другую карту

Чтобы перенести введенные данные с исходной карточки в приложение Chat, которое не является дополнением, создайте виджет button с actionParameters, содержащими name виджета и значение, введенное пользователем, как показано в следующем примере:

Node.js

node/contact-form-app/index.js
buttonList: { buttons: [{
  text: "Submit",
  onClick: { action: {
    function: "submitForm",
    parameters: [{
      key: "contactName", value: name }, {
      key: "contactBirthdate", value: birthdate }, {
      key: "contactType", value: type
    }]
  }}
}]}

Python

python/contact-form-app/main.py
'buttonList': { 'buttons': [{
  'text': "Submit",
  'onClick': { 'action': {
    'function': "submitForm",
    'parameters': [{
      'key': "contactName", 'value': name }, {
      'key': "contactBirthdate", 'value': birthdate }, {
      'key': "contactType", 'value': type
    }]
  }}
}]}

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()
  .setText("Submit")
  .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
    .setFunction("submitForm")
    .setParameters(List.of(
      new GoogleAppsCardV1ActionParameter().setKey("contactName").setValue(name),
      new GoogleAppsCardV1ActionParameter().setKey("contactBirthdate").setValue(birthdate),
      new GoogleAppsCardV1ActionParameter().setKey("contactType").setValue(type))))))))));

Apps Script

apps-script/contact-form-app/main.gs
buttonList: { buttons: [{
  text: "Submit",
  onClick: { action: {
    function: "submitForm",
    parameters: [{
      key: "contactName", value: name }, {
      key: "contactBirthdate", value: birthdate }, {
      key: "contactType", value: type
    }]
  }}
}]}

Когда пользователь нажимает на кнопку, приложение Chat, которое не является дополнением, получает событие взаимодействия CARD_CLICKED, из которого вы можете получить данные.

Как ответить на отправленную форму

В следующем примере приложение Chat, которое не является дополнением, отправляет текстовое сообщение, чтобы подтвердить, что оно успешно получило форму, отправленную из диалогового окна или карточки:

Node.js

node/contact-form-app/index.js
const contactName = event.common.parameters["contactName"];
// Checks to make sure the user entered a contact name.
// If no name value detected, returns an error message.
const errorMessage = "Don't forget to name your new contact!";
if (!contactName && event.dialogEventType === "SUBMIT_DIALOG") {
  return { actionResponse: {
    type: "DIALOG",
    dialogAction: { actionStatus: {
      statusCode: "INVALID_ARGUMENT",
      userFacingMessage: errorMessage
    }}
  }};
}

Python

python/contact-form-app/main.py
contact_name = event.get('common').get('parameters')["contactName"]
# Checks to make sure the user entered a contact name.
# If no name value detected, returns an error message.
error_message = "Don't forget to name your new contact!"
if contact_name == "" and "SUBMIT_DIALOG" == event.get('dialogEventType'):
  return { 'actionResponse': {
    'type': "DIALOG",
    'dialogAction': { 'actionStatus': {
      'statusCode': "INVALID_ARGUMENT",
      'userFacingMessage': error_message
    }}
  }}

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
String contactName = event.at("/common/parameters/contactName").asText();
// Checks to make sure the user entered a contact name.
// If no name value detected, returns an error message.
String errorMessage = "Don't forget to name your new contact!";
if (contactName.isEmpty() && event.at("/dialogEventType") != null && "SUBMIT_DIALOG".equals(event.at("/dialogEventType").asText())) {
  return new Message().setActionResponse(new ActionResponse()
    .setType("DIALOG")
    .setDialogAction(new DialogAction().setActionStatus(new ActionStatus()
      .setStatusCode("INVALID_ARGUMENT")
      .setUserFacingMessage(errorMessage))));
}

Apps Script

apps-script/contact-form-app/main.gs
const contactName = event.common.parameters["contactName"];
// Checks to make sure the user entered a contact name.
// If no name value detected, returns an error message.
const errorMessage = "Don't forget to name your new contact!";
if (!contactName && event.dialogEventType === "SUBMIT_DIALOG") {
  return { actionResponse: {
    type: "DIALOG",
    dialogAction: { actionStatus: {
      statusCode: "INVALID_ARGUMENT",
      userFacingMessage: errorMessage
    }}
  }};
}

Чтобы обработать и закрыть диалоговое окно в приложении Chat, которое не является дополнением, верните объект ActionResponse, в котором указано, хотите ли вы отправить подтверждающее сообщение, обновить исходное сообщение или карточку или просто закрыть диалоговое окно. Подробнее о том, как закрыть диалоговое окно…