Google Chat ユーザーから情報を収集して処理する

このガイドでは、Google Chat アプリがカードベースのインターフェースでフォーム入力を構築して、ユーザーから情報を収集して処理する方法について説明します。

さまざまなウィジェットを備えたダイアログ。
図 1: 連絡先情報を収集するためのダイアログを開くサンプル Chat アプリ。

Chat 用アプリは、Chat 内外でアクションを実行するために、ユーザーに情報をリクエストします。これには、次の方法があります。

  • 設定を構成します。たとえば、ユーザーが通知設定をカスタマイズしたり、Chat アプリを構成して 1 つ以上のスペースに追加したりできるようにします。
  • 他の Google Workspace アプリケーションで情報を作成または更新する。たとえば、ユーザーが Google カレンダーの予定を作成できるようにします。
  • ユーザーが他のアプリやウェブサービスのリソースにアクセスして更新できるようにします。たとえば、Chat アプリを使用すると、ユーザーは Chat スペースから直接サポート チケットのステータスを更新できます。

前提条件

HTTP

ユーザー インタラクションを受信して応答する Google Chat アプリ。作成するには、HTTP クイックスタートを完了します。

Apps Script

ユーザー インタラクションを受信して応答する Google Chat アプリ。作成するには、Apps Script のクイックスタートを完了してください。

カードを使用してフォームを作成する

情報を収集するために、Chat 用アプリはフォームとその入力を設計し、カードに組み込みます。ユーザーにカードを表示するために、Chat 用アプリは次の Chat インターフェースを使用できます。

  • 1 つ以上のカードを含むメッセージ。
  • ホームページ。Chat アプリとのダイレクト メッセージの [ホーム] タブに表示されるカードです。
  • ダイアログ。メッセージやホームページから新しいウィンドウで開くカードです。

Chat 用アプリは、次のウィジェットを使用してカードを作成できます。

  • ユーザーに情報の入力を求めるフォーム入力ウィジェット。必要に応じて、フォーム入力ウィジェットに検証を追加して、ユーザーが情報を正しく入力し、フォーマットできるようにすることもできます。Chat 用アプリでは、次のフォーム入力ウィジェットを使用できます。

  • ボタン ウィジェット。ユーザーがカードに入力した値を送信できるようにします。ユーザーがボタンをクリックすると、Chat 用アプリは受信した情報を処理できます。

次の例では、カードがテキスト入力、日時選択ツール、選択入力を使用して連絡先情報を収集します。

情報の収集に使用できるインタラクティブなウィジェットのその他の例については、インタラクティブなカードやダイアログを設計するをご覧ください。

プルダウン メニューを追加する

選択項目をカスタマイズしたり、ユーザーが動的データソースから 1 つの項目を選択できるようにしたりするには、Chat 用アプリでプルダウン メニューを使用します。これは SelectionInput ウィジェットの一種です。たとえば、次のカードには、ユーザーが連絡先のリストから動的に選択できるプルダウン メニューが表示されます。

プルダウン メニューの項目は、次のデータソースから入力できます。

Google Workspace データソースからアイテムを入力する

Google Workspace ユーザーなどの Google Workspace データソースからアイテムを入力するには、DataSourceConfig オブジェクト内の platformDataSource フィールドを指定します。他の選択入力タイプとは異なり、SelectionItem オブジェクトは省略します。これらの選択項目は Google Workspace から動的に取得されるためです。

次のコードは、Google Workspace ユーザーのプルダウン メニューを示しています。

JSON

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

外部データソースからアイテムを入力する

プルダウン メニューでは、サードパーティまたは外部のデータソースからアイテムを入力することもできます。外部データソースを使用するには、DataSourceConfig オブジェクト内の remoteDataSource フィールドを指定します。このオブジェクトには、データソースからアイテムをクエリして返す関数が含まれています。

外部データソースへのリクエストを減らすには、ユーザーがメニューに入力する前にプルダウン メニューに表示される候補アイテムを含めます。外部データソースから候補アイテムを入力するには、静的な SelectionItem オブジェクトを指定します。

次のコードは、外部データソースからアイテムをクエリして入力するプルダウン メニューを示しています。

JSON

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

提案されたアイテムを返す方法を示す実証済みの例については、選択アイテムを提案するセクションをご覧ください。

複数選択メニューを追加する

選択項目をカスタマイズしたり、ユーザーが動的データソースから項目を選択できるようにしたりするには、Chat 用アプリで複数選択メニューを使用します。これは SelectionInput ウィジェットの一種です。たとえば、次のカードには、ユーザーが連絡先のリストから動的に選択できる複数選択メニューが表示されます。

複数選択メニューの項目は、次のデータソースから入力できます。

  • Google Workspace データ。これには、ユーザーまたはユーザーがメンバーである Chat スペースが含まれます。メニューには、同じ Google Workspace 組織のアイテムのみが表示されます。
  • リレーショナル データベースなどの外部データソース。たとえば、複数選択メニューを使用すると、ユーザーが顧客管理(CRM)システムの見込み顧客のリストから選択できます。

Google Workspace データソースからアイテムを入力する

Google Workspace データソースを使用するには、SelectionInput ウィジェットで platformDataSource フィールドを指定します。他の選択入力タイプとは異なり、SelectionItem オブジェクトは省略します。これらの選択項目は Google Workspace から動的に取得されるためです。

次のコードは、Google Workspace ユーザーの複数選択メニューを示しています。ユーザーを入力するために、選択インプット セットで commonDataSource を USER に設定します。

JSON

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

次のコードは、Chat スペースの複数選択メニューを示しています。スペースにデータを入力するために、選択入力で hostAppDataSource フィールドを指定します。複数選択メニューでは、defaultToCurrentSpace が true に設定され、現在のスペースがメニューのデフォルトの選択になります。

JSON

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

外部データソースからアイテムを入力する

複数選択メニューでは、サードパーティまたは外部のデータソースからアイテムを入力することもできます。外部データソースを使用するには、SelectionInput ウィジェットの externalDataSource フィールドに、データソースからアイテムをクエリして返す関数を指定します。

外部データソースへのリクエストを減らすには、ユーザーがメニューに入力する前に、複数選択メニューに表示される候補アイテムを含めることができます。たとえば、ユーザーが最近検索した連絡先を入力できます。外部データソースから候補アイテムを入力するには、静的な 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 フィールドを指定します。プルダウン メニューの場合は、DataSourceConfig オブジェクト内の remoteDataSource フィールドを指定します。

また、メニューに候補が表示されるまでに入力する文字数を構成することもできます。複数選択メニューの場合は、multiSelectMinQueryLength フィールドを設定します。プルダウン メニューの場合は、DataSourceConfig 内の min_characters_trigger フィールドを設定します。

提案されたアイテムを返すには、関数で次の操作を行う必要があります。

  1. ユーザーがメニューに入力したときに Chat アプリが受信するイベント オブジェクトを処理します。
  2. イベント オブジェクトから、ユーザーが入力した値(event.commonEventObject.parameters["autocomplete_widget_query"] フィールドに表示される値)を取得します。
  3. ユーザー入力値を使用してデータソースにクエリを実行し、ユーザーに提案する 1 つ以上の SelectionItems を取得します。
  4. modifyCard オブジェクトを含むアクション RenderActions を返すことで、おすすめのアイテムを返します。

次のコードサンプルは、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 用アプリは、予約の理由をリクエストする最初のカードを表示し、予約の種類に基づいて利用可能な時間を表示する別のカードを表示できます。

最初のカードから入力されたデータを転送するには、次の例に示すように、ウィジェットの name とユーザーが入力した値を含む actionParameters を使用して button ウィジェットを構築します。

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 でダイアログを操作する際のエラーコードとランタイム動作のトラブルシューティング手順について説明します。

ダイアログの操作で「アドオンの呼び出し中に不明なエラーが発生しました」と返される

ダイアログを操作しているときに、メッセージ「Unspecified error invoking the add-on.」とコード 13 のエラーログが表示された場合は、通常、内部エラーが発生しているか、Chat アプリの HTTP エンドポイントがリクエストの処理に失敗したか、有効なレスポンスを返さなかったことを示しています。

このエラーを解決する方法は以下のとおりです。

  • HTTP エンドポイントのログで、未処理の例外やクラッシュがないか確認します。
  • エンドポイントが 30 秒以内にリクエストに応答することを確認します。エンドポイントの実行に 30 秒以上かかると、Chat はレスポンスを処理できず、インタラクションは失敗します。詳細については、レート制限とベスト プラクティスをご覧ください。
  • エンドポイントが有効なレスポンスを返すことを確認します。ダイアログの送信の場合、エンドポイントは正しい JSON 形式の RenderActions オブジェクトを返す必要があります。レスポンスの形式が正しくないか、必須フィールドが含まれていない場合、ダイアログの操作が失敗することがあります。

Google Chat アプリまたはカードからエラーが返されると、Chat インターフェースに「エラーが発生しました」というメッセージが表示されます。または「リクエストを処理できません。」Chat UI にエラー メッセージが表示されない場合でも、Chat アプリやカードで予期しない結果が生じることがあります。たとえば、カード メッセージが表示されないことがあります。

Chat UI にエラー メッセージが表示されない場合でも、Chat 用アプリのエラー ロギングが有効になっていると、エラーの修正に役立つ詳細なエラー メッセージとログデータを利用できます。エラーの表示、デバッグ、修正については、Google Chat のエラーのトラブルシューティングと修正をご覧ください。

アドオンではない Chat 用アプリ: ユーザーがカードに入力したフォームデータを読み取る

以下のドキュメントは、Google Workspace アドオンではない Chat アプリに適用されます。アドオンではない 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 アプリの最初のカードからデータ入力を転送するには、次の例に示すように、ウィジェットの name とユーザーが入力した値を含む actionParameters を使用して button ウィジェットをビルドします。

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 オブジェクトを返します。手順については、ダイアログを閉じるをご覧ください。