Zbieranie i przetwarzanie informacji od użytkowników Google Chat

Z tego przewodnika dowiesz się, jak aplikacje Google Chat mogą zbierać i przetwarzać informacje od użytkowników, tworząc dane wejściowe formularza w interfejsach opartych na kartach.

Okno z różnymi widżetami.
Ilustracja 1. Przykładowa aplikacja Google Chat, która otwiera okno dialogowe do zbierania informacji kontaktowych.

Aplikacje do czatu proszą użytkowników o informacje, aby wykonywać działania w Google Chat lub poza nim, w tym w następujący sposób:

  • Skonfiguruj ustawienia. Może to być na przykład umożliwienie użytkownikom dostosowywania ustawień powiadomień lub konfigurowania i dodawania aplikacji Chat do co najmniej jednego pokoju.
  • tworzyć lub aktualizować informacje w innych aplikacjach Google Workspace; Na przykład umożliwia użytkownikom tworzenie wydarzeń w Kalendarzu Google.
  • umożliwiać użytkownikom dostęp do zasobów w innych aplikacjach lub usługach internetowych i ich aktualizowanie; Na przykład aplikacja do obsługi czatu może pomagać użytkownikom w aktualizowaniu stanu zgłoszenia bezpośrednio z pokoju w Google Chat.

Wymagania wstępne

HTTP

Aplikacja Google Chat, która odbiera i odpowiada na interakcje użytkowników. Aby go utworzyć, wykonaj czynności opisane w krótkim wprowadzeniu do HTTP.

Google Apps Script

Aplikacja Google Chat, która odbiera i odpowiada na interakcje użytkowników. Aby utworzyć taki skrypt, wykonaj czynności opisane w krótkim wprowadzeniu do Apps Script.

Tworzenie formularzy za pomocą kart

Aby zbierać informacje, aplikacje do Google Chat projektują formularze i ich pola wejściowe oraz tworzą z nich karty. Aby wyświetlać karty użytkownikom, aplikacje do obsługi czatu mogą korzystać z tych interfejsów Google Chat:

  • Wiadomości zawierające co najmniej 1 kartę.
  • Strony główne, czyli karty wyświetlane na karcie Strona główna w czatach z aplikacją Google Chat.
  • Okna, czyli karty, które otwierają się w nowym oknie z wiadomości i stron głównych.

Aplikacje do obsługi czatu mogą tworzyć karty za pomocą tych widżetów:

  • Widżety wprowadzania danych w formularzu, które proszą użytkowników o informacje. Opcjonalnie możesz dodać weryfikację do widżetów wprowadzania danych w formularzu, aby mieć pewność, że użytkownicy wprowadzają i formatują informacje prawidłowo. Aplikacje do obsługi czatu mogą używać tych widżetów wprowadzania danych:

    • Dane wejściowe (textInput) w postaci tekstu dowolnego lub sugerowanego.
    • Pola wyboru (selectionInput) to elementy interfejsu, które można wybrać, np. pola wyboru, przyciski opcji i menu. Widżety wprowadzania wyboru mogą też wypełniać i sugerować elementy z danych Google Workspace (np. pokój w Google Chat) lub dynamicznego źródła danych. Szczegółowe informacje znajdziesz w sekcjach Dodawanie menu i Dodawanie menu wielokrotnego wyboru.
    • selektory daty i godziny (dateTimePicker) do wprowadzania daty i godziny;
  • Widżet przycisku, aby użytkownicy mogli przesyłać wartości wpisane na karcie. Gdy użytkownik kliknie przycisk, aplikacja Google Chat może przetworzyć otrzymane informacje.

W tym przykładzie karta zbiera informacje kontaktowe za pomocą pola tekstowego, selektora daty i godziny oraz pola wyboru:

Więcej przykładów interaktywnych widżetów, których możesz używać do zbierania informacji, znajdziesz w artykule Projektowanie interaktywnej karty lub okna.

Dodawanie menu

Aby dostosować elementy wyboru lub umożliwić użytkownikom wybór pojedynczego elementu z dynamicznego źródła danych, aplikacje na czacie mogą używać menu, które są rodzajem widżetu SelectionInput. Na przykład ta karta wyświetla menu, w którym użytkownicy mogą dynamicznie wybierać pozycje z listy kontaktów:

Elementy menu możesz wypełniać danymi z tych źródeł:

Wypełnianie elementów ze źródła danych Google Workspace

Aby wypełnić elementy ze źródeł danych Google Workspace, takich jak użytkownicy Google Workspace, określ pole platformDataSource w obiekcie DataSourceConfig. W przeciwieństwie do innych typów danych wejściowych wyboru pomijasz obiekty SelectionItem, ponieważ te elementy wyboru są dynamicznie pobierane z Google Workspace.

Poniższy kod pokazuje menu z użytkownikami 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
              }
            ]
          }
        }
      ]
    }
  ]
}

Wypełnianie elementów danymi z zewnętrznego źródła danych

Menu rozwijane mogą też zawierać elementy pochodzące ze źródła danych zewnętrznego lub należącego do innej firmy. Aby użyć zewnętrznego źródła danych, w obiekcie DataSourceConfig określ pole remoteDataSource zawierające funkcję, która wysyła zapytania do źródła danych i zwraca z niego elementy.

Aby zmniejszyć liczbę żądań wysyłanych do zewnętrznego źródła danych, możesz uwzględnić sugerowane elementy, które pojawiają się w menu przed wpisaniem przez użytkowników tekstu w menu. Aby wypełnić sugerowane produkty z zewnętrznego źródła danych, określ obiekty statyczne SelectionItem.

Poniższy kod pokazuje menu, które wysyła zapytanie do zewnętrznego źródła danych i wypełnia je elementami:

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

Pełny przykład pokazujący, jak zwracać sugerowane elementy, znajdziesz w sekcji Sugerowanie elementów do wyboru.

Dodawanie menu wielokrotnego wyboru

Aby dostosować elementy wyboru lub umożliwić użytkownikom wybieranie elementów z dynamicznego źródła danych, aplikacje na czacie mogą używać menu wielokrotnego wyboru, które są rodzajem widżetu SelectionInput. Na przykład poniższa karta wyświetla menu wielokrotnego wyboru, w którym użytkownicy mogą dynamicznie wybierać elementy z listy kontaktów:

Elementy menu wielokrotnego wyboru możesz wypełniać danymi z tych źródeł:

  • dane Google Workspace, które obejmują użytkowników lub pokoje w Google Chat, w których użytkownik jest członkiem. Menu zawiera tylko elementy z tej samej organizacji Google Workspace.
  • zewnętrzne źródła danych, np. relacyjna baza danych; Możesz na przykład użyć menu wielokrotnego wyboru, aby pomóc użytkownikowi wybrać z listy potencjalnych klientów z systemu zarządzania relacjami z klientami (CRM).

Wypełnianie elementów ze źródła danych Google Workspace

Aby używać źródeł danych Google Workspace, w widżecie SelectionInput określ pole platformDataSource. W przeciwieństwie do innych typów danych wejściowych wyboru pomijasz obiekty SelectionItem, ponieważ te elementy wyboru są dynamicznie pobierane z Google Workspace.

Poniższy kod przedstawia menu wielokrotnego wyboru użytkowników Google Workspace. Aby wypełnić listę użytkowników, w polu wyboru ustaw wartość commonDataSource na USER:

JSON

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

Poniższy kod pokazuje menu wielokrotnego wyboru przestrzeni na czacie. Aby wypełnić miejsca, w polu wyboru należy podać pole hostAppDataSource. W menu wielokrotnego wyboru ustawia się też wartość defaultToCurrentSpace na true, co sprawia, że bieżący pokój jest domyślnie wybierany w menu:

JSON

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

Wypełnianie elementów danymi z zewnętrznego źródła danych

Menu wielokrotnego wyboru mogą też zawierać elementy pochodzące ze źródła danych zewnętrznego lub firmy zewnętrznej. Aby użyć zewnętrznego źródła danych, w widżecie SelectionInput podaj pole externalDataSource zawierające funkcję, która wysyła zapytania do źródła danych i zwraca z niego elementy.

Aby zmniejszyć liczbę żądań wysyłanych do zewnętrznego źródła danych, możesz uwzględnić sugerowane elementy, które pojawiają się w menu wielokrotnego wyboru, zanim użytkownicy wpiszą tekst w menu. Możesz na przykład wypełnić listę ostatnio wyszukiwanych kontaktów użytkownika. Aby wypełnić sugerowane produkty z zewnętrznego źródła danych, określ obiekty statyczne SelectionItem.

Poniższy przykładowy kod pokazuje menu wielokrotnego wyboru, które wysyła zapytania i wypełnia elementy z zewnętrznego źródła danych:

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")]
}

Zastąp FUNCTION_URL punktem końcowym HTTP, który wysyła zapytania do zewnętrznego źródła danych.

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")]
}

Zastąp FUNCTION_URL punktem końcowym HTTP, który wysyła zapytania do zewnętrznego źródła danych.

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")))))))))));

Zastąp FUNCTION_URL punktem końcowym HTTP, który wysyła zapytania do zewnętrznego źródła danych.

Google Apps Script

W tym przykładzie wysyłana jest wiadomość z kartą przez zwrócenie kodu JSON karty. Możesz też użyć usługi kart 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")]
}

Pełny przykład pokazujący, jak zwracać sugerowane elementy, znajdziesz w sekcji Sugerowanie elementów do wyboru.

Otrzymywanie danych z interaktywnych widżetów

Gdy użytkownicy klikną przycisk, wywoływane jest działanie aplikacji Google Chat, które zawiera informacje o interakcji. W parametrze commonEventObject ładunku zdarzenia obiekt formInputs zawiera wszystkie wartości wpisane przez użytkownika.

Wartości możesz pobrać z obiektu event.commonEventObject.formInputs.WIDGET_NAME, gdzie WIDGET_NAME to pole name określone dla widżetu. Wartości są zwracane jako określony typ danych widżetu.

Poniżej przedstawiono fragment obiektu zdarzenia, w którym użytkownik wprowadził wartości w poszczególnych widżetach:

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

Aby otrzymać dane, aplikacja w Google Chat obsługuje obiekt zdarzenia, aby uzyskać wartości wpisywane przez użytkowników w widżetach. W tabeli poniżej znajdziesz informacje o tym, jak uzyskać wartość danego widżetu wejściowego formularza. W przypadku każdego widżetu tabela zawiera typ danych, który akceptuje widżet, miejsce przechowywania wartości w obiekcie zdarzenia oraz przykładową wartość.

Widżet wprowadzania danych w formularzu Typ danych wejściowych Wartość wejściowa z obiektu zdarzenia Przykładowa wartość
textInput stringInputs event.commonEventObject.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs Aby uzyskać pierwszą lub jedyną wartość, event.commonEventObject.formInputs.contactType.stringInputs.value[0] Personal
dateTimePicker, które akceptuje tylko daty. dateInput event.commonEventObject.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Po otrzymaniu danych aplikacja do obsługi czatu może wykonać dowolną z tych czynności:

  • W przypadku kart zawierających menu wielokrotnego wyboru wypełniaj lub sugeruj elementy na podstawie tego, co użytkownik wpisuje w menu.
  • Przenieś dane na inną kartę, aby użytkownik mógł sprawdzić swoje informacje lub przejść do następnej sekcji formularza.
  • Odpowiedz użytkownikowi, aby potwierdzić, że użytkownik prawidłowo wypełnił formularz.

Sugerowanie elementów wyboru

Jeśli karta zawiera menu wielokrotnego wyboru lub menu, które wypełnia elementy z zewnętrznego źródła danych, aplikacja Chat może zwracać sugerowane elementy na podstawie tego, co użytkownicy wpisują w menu. Jeśli na przykład użytkownik zacznie wpisywać Atl w menu, które zawiera miasta w Stanach Zjednoczonych, aplikacja do Google Chat może automatycznie zasugerować Atlanta, zanim użytkownik skończy wpisywać. Aplikacja Chat może sugerować maksymalnie 100 elementów.

Aby sugerować i dynamicznie wypełniać elementy w polu wyboru, widżet SelectionInput na karcie musi określać funkcję, która wysyła zapytania do zewnętrznego źródła danych. W przypadku menu wielokrotnego wyboru określasz pole externalDataSource. W przypadku menu określasz pole remoteDataSource w obiekcie DataSourceConfig.

Możesz też skonfigurować liczbę znaków, które użytkownik musi wpisać, zanim menu wyświetli sugestie. W przypadku menu wielokrotnego wyboru ustaw pole multiSelectMinQueryLength. W przypadku menu wybieranych ustaw pole min_characters_trigger w ramach DataSourceConfig.

Aby zwracać sugerowane produkty, funkcja musi wykonać te czynności:

  1. Obsłuż obiekt zdarzenia, który aplikacja do obsługi czatu otrzymuje, gdy użytkownicy wpisują tekst w menu.
  2. Z obiektu zdarzenia pobierz wartość wpisaną przez użytkownika, która jest reprezentowana w polu event.commonEventObject.parameters["autocomplete_widget_query"].
  3. Wysyłaj do źródła danych zapytania z danymi wejściowymi użytkownika, aby uzyskać co najmniej 1 SelectionItems do zaproponowania użytkownikowi.
  4. Zwróć sugerowane produkty, zwracając działanieRenderActions z obiektem modifyCard.

Poniższy przykładowy kod pokazuje, jak aplikacja w Google Chat dynamicznie sugeruje elementy w menu wielokrotnego wyboru na karcie. Gdy użytkownik wpisuje tekst w menu, funkcja lub punkt końcowy podany w polu externalDataSource widżetu wysyła zapytanie do zewnętrznego źródła danych i sugeruje elementy, które użytkownik może wybrać.

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
  };
}

Zastąp FUNCTION_URL punktem końcowym HTTP, który wysyła zapytania do zewnętrznego źródła danych.

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
  }

Zastąp FUNCTION_URL punktem końcowym HTTP, który wysyła zapytania do zewnętrznego źródła danych.

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);
  }
}

Zastąp FUNCTION_URL punktem końcowym HTTP, który wysyła zapytania do zewnętrznego źródła danych.

Google Apps Script

W tym przykładzie wysyłana jest wiadomość z kartą przez zwrócenie kodu JSON karty. Możesz też użyć usługi kart 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
  };
}

Przenoszenie danych na inną kartę

Gdy użytkownik prześle informacje z karty, może być konieczne zwrócenie dodatkowych kart w celu wykonania jednej z tych czynności:

  • Ułatw użytkownikom wypełnianie dłuższych formularzy, tworząc w nich osobne sekcje.
  • Pozwól użytkownikom wyświetlać podgląd informacji z karty początkowej i potwierdzać je, aby mogli sprawdzić swoje odpowiedzi przed przesłaniem.
  • dynamicznie wypełniać pozostałe części formularza. Na przykład, aby zachęcić użytkowników do utworzenia spotkania, aplikacja do czatu może wyświetlić początkową kartę z prośbą o podanie powodu spotkania, a następnie wypełnić inną kartę, która zawiera dostępne terminy na podstawie typu spotkania.

Aby przenieść dane wejściowe z pierwszej karty, możesz utworzyć widżet button z elementami actionParameters zawierającymi name widżetu i wartość wprowadzoną przez użytkownika, jak pokazano w tym przykładzie:

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 }
    ]
  }}
}]}}

Zastąp FUNCTION_URL punktem końcowym HTTP, który obsługuje kliknięcia przycisku.

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 }
    ]
  }}
}]}}

Zastąp FUNCTION_URL punktem końcowym HTTP, który obsługuje kliknięcia przycisku.

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))))))))))));

Zastąp FUNCTION_URL punktem końcowym HTTP, który obsługuje kliknięcia przycisku.

Google Apps Script

W tym przykładzie wysyłana jest wiadomość z kartą przez zwrócenie kodu JSON karty. Możesz też użyć usługi kart 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 }
    ]
  }}
}]}}

Gdy użytkownik kliknie przycisk, aplikacja do obsługi czatu otrzyma obiekt zdarzenia, z którego możesz pobrać dane.

Odpowiadanie na przesłane formularze

Po otrzymaniu danych z wiadomości na karcie lub okna aplikacji do obsługi czatu odpowiada ona, potwierdzając odbiór lub zwracając błąd.

W przykładzie poniżej aplikacja do obsługi czatu wysyła wiadomość tekstową z potwierdzeniem, że otrzymała formularz przesłany z wiadomości w formie karty.

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."));
      }});
    }});
  }});
}};

Google Apps Script

W tym przykładzie wysyłana jest wiadomość z kartą przez zwrócenie kodu JSON karty. Możesz też użyć usługi kart 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."
}}}}};

Aby przetworzyć i zamknąć okno, zwracasz obiekt RenderActions określający, czy chcesz wysłać wiadomość z potwierdzeniem, zaktualizować oryginalną wiadomość lub kartę, czy tylko zamknąć okno. Instrukcje znajdziesz w artykule Zamykanie okna.

Rozwiązywanie problemów

W tej sekcji znajdziesz instrukcje rozwiązywania problemów z określonymi kodami błędów i zachowaniami środowiska wykonawczego podczas interakcji z oknami w Google Chat.

Interakcje w oknie zwracają błąd „Podczas wywoływania dodatku wystąpił nieokreślony błąd”

Jeśli podczas interakcji z oknem dialogowym zobaczysz dzienniki błędów z komunikatem „Nieokreślony błąd podczas wywoływania dodatku” i kodem 13, zwykle oznacza to błąd wewnętrzny lub że punkt końcowy HTTP aplikacji Chat nie przetworzył żądania lub nie zwrócił prawidłowej odpowiedzi.

Aby rozwiązać ten problem:

  • Sprawdź w logach punktu końcowego HTTP, czy nie występują nieobsłużone wyjątki lub awarie.
  • Sprawdź, czy punkt końcowy odpowiada na żądania w ciągu 30 sekund. Jeśli działanie punktu końcowego trwa dłużej niż 30 sekund, Chat nie może przetworzyć odpowiedzi i interakcja się nie powiedzie. Więcej informacji znajdziesz w artykule Limity szybkości i sprawdzone metody.
  • Sprawdź, czy punkt końcowy zwraca prawidłową odpowiedź. W przypadku przesłanych okien dialogowych punkt końcowy musi zwracać obiekt RenderActions w prawidłowym formacie JSON. Jeśli odpowiedź jest nieprawidłowo sformatowana lub nie zawiera wymaganych pól, interakcja z oknem może się nie udać.

Gdy aplikacja Google Chat lub karta zwróci błąd, w interfejsie Google Chat pojawi się komunikat „Coś poszło nie tak”. lub „Nie udało się przetworzyć żądania”. Czasami interfejs Google Chat nie wyświetla żadnego komunikatu o błędzie, ale aplikacja lub karta Google Chat zwraca nieoczekiwany wynik, np. może nie pojawić się wiadomość na karcie.

Chociaż w interfejsie czatu może nie wyświetlać się komunikat o błędzie, opisowe komunikaty o błędach i dane logowania są dostępne, aby pomóc w naprawieniu błędów, gdy rejestrowanie błędów w aplikacjach na czat jest włączone. Pomoc dotyczącą wyświetlania, debugowania i naprawiania błędów znajdziesz w artykule Rozwiązywanie problemów z Google Chat.

Aplikacje do obsługi czatu, które nie są dodatkami: odczytywanie danych z formularzy wprowadzonych przez użytkowników na kartach

Poniższa dokumentacja dotyczy aplikacji do Google Chat, które nie są dodatkami do Google Workspace. Aby przeprowadzić migrację aplikacji do czatu, która nie jest dodatkiem, przeczytaj artykuł Przekształcanie aplikacji do Google Chat w dodatek do Google Workspace.

Tworzenie formularzy za pomocą kart

Przykład aplikacji Google Chat, która nie jest dodatkiem i korzysta z formularza kontaktowego z polem tekstowym, selektorem daty i godziny oraz polem wyboru, znajdziesz w tym kodzie:

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)))));

Google 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
        }
      ]
    }
  }
];

Otrzymywanie danych z interaktywnych widżetów

Gdy użytkownicy klikną przycisk, aplikacje do obsługi czatu, które nie są dodatkami, otrzymają zdarzenie interakcji zależne od lokalizacji przycisku:

  • Jeśli przycisk znajduje się w wiadomości lub oknie, aplikacje Google Chat, które nie są dodatkami, otrzymują CARD_CLICKEDzdarzenie interakcji zawierające informacje o interakcji. Ładunek zdarzeń interakcjiCARD_CLICKED zawiera obiektcommon.formInputs (event.common.formInputs) z dowolnymi wartościami wprowadzonymi przez użytkownika.

    Wartości możesz pobrać z obiektu common.formInputs.WIDGET_NAME, gdzie WIDGET_NAME to pole name określone dla widżetu. Wartości są zwracane jako określony typ danych widżetu (reprezentowany jako obiekt Inputs).

    Poniżej przedstawiamy fragment CARD_CLICKEDzdarzenia interakcji, w którym użytkownik wprowadził wartości w przypadku każdego widżetu:

    HTTP

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

    Google Apps Script

    {
      "type": "CARD_CLICKED",
      "common": { "formInputs": {
        "contactName": { "": { "stringInputs": {
          "value": ["Kai 0"]
        }}},
        "contactBirthdate": { "": { "dateInput": {
          "msSinceEpoch": 1000425600000
        }}},
          "contactType": { "": { "stringInputs": {
          "value": ["Personal"]
        }}}
      }}
    }
    
  • Jeśli przycisk znajduje się na stronie głównej, aplikacje Google Chat, które nie są dodatkami, otrzymują SUBMIT_FORM zdarzenie interakcji. Ładunek zdarzenia interakcji zawiera obiekt commonEventObject.formInputs (event.commonEventObject.formInputs) z dowolnymi wartościami wprowadzonymi przez użytkownika.

    Wartości możesz pobrać z obiektu commonEventObject.formInputs.WIDGET_NAME, gdzie WIDGET_NAME to pole name określone dla widżetu. Wartości są zwracane jako określony typ danych widżetu (reprezentowany jako obiekt Inputs).

    Poniżej przedstawiamy fragment SUBMIT_FORMzdarzenia interakcji, w którym użytkownik wprowadził wartości w przypadku każdego widżetu:

    HTTP

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

    Google Apps Script

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

Aby otrzymać dane, aplikacja Google Chat, która nie jest dodatkiem, obsługuje zdarzenie interakcji, aby uzyskać wartości wprowadzane przez użytkowników w widżetach. W tabeli poniżej pokazujemy, jak uzyskać wartość danego widżetu wejściowego formularza. W przypadku każdego widżetu tabela zawiera typ danych, który akceptuje widżet, miejsce przechowywania wartości w zdarzeniu interakcji oraz przykładową wartość.

Widżet wprowadzania danych w formularzu Typ danych wejściowych Wartość wejściowa ze zdarzenia interakcji Przykładowa wartość
textInput stringInputs event.common.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs Aby uzyskać pierwszą lub jedyną wartość, event.common.formInputs.contactType.stringInputs.value[0] Personal
dateTimePicker, które akceptuje tylko daty. dateInput event.common.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Przenoszenie danych na inną kartę

Aby przenieść dane wejściowe z pierwszej karty w aplikacji do Google Chat, która nie jest dodatkiem, utwórz widżet button z elementem actionParameters, który zawiera name widżetu i wartość wprowadzoną przez użytkownika, jak pokazano w tym przykładzie:

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))))))))));

Google 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
    }]
  }}
}]}

Gdy użytkownik kliknie przycisk, aplikacja do obsługi czatu, która nie jest dodatkiem, otrzyma CARD_CLICKEDzdarzenie interakcji, z którego możesz otrzymywać dane.

Odpowiadanie na przesłane formularze

W tym przykładzie aplikacja do obsługi czatu, która nie jest dodatkiem, wysyła wiadomość tekstową z potwierdzeniem, że otrzymała formularz przesłany z okna lub karty:

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))));
}

Google 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
    }}
  }};
}

Aby przetworzyć i zamknąć okno dialogowe w aplikacji do obsługi czatu, która nie jest dodatkiem, zwróć obiekt ActionResponse, który określa, czy chcesz wysłać wiadomość z potwierdzeniem, zaktualizować oryginalną wiadomość lub kartę, czy tylko zamknąć okno dialogowe. Instrukcje znajdziesz w artykule Zamykanie okna.