Thu thập và xử lý thông tin của người dùng Google Chat

Hướng dẫn này mô tả cách các ứng dụng Google Chat có thể thu thập và xử lý thông tin của người dùng bằng cách tạo biểu mẫu nhập dữ liệu trong giao diện dựa trên thẻ.

Một hộp thoại có nhiều tiện ích.
Hình 1: Một ứng dụng Chat mẫu mở hộp thoại để thu thập thông tin liên hệ.

Ứng dụng Chat yêu cầu người dùng cung cấp thông tin để thực hiện hành động trong hoặc ngoài Chat, bao gồm cả những cách sau:

  • Định cấu hình chế độ cài đặt. Ví dụ: để cho phép người dùng tuỳ chỉnh chế độ cài đặt thông báo hoặc định cấu hình và thêm ứng dụng Chat vào một hoặc nhiều không gian.
  • Tạo hoặc cập nhật thông tin trong các ứng dụng khác của Google Workspace. Ví dụ: cho phép người dùng tạo sự kiện trên Lịch Google.
  • Cho phép người dùng truy cập và cập nhật tài nguyên trong các ứng dụng hoặc dịch vụ web khác. Ví dụ: một ứng dụng Chat có thể giúp người dùng cập nhật trạng thái của phiếu yêu cầu hỗ trợ ngay trong không gian Chat.

Điều kiện tiên quyết

HTTP

Một ứng dụng Google Chat nhận và phản hồi các hoạt động tương tác của người dùng. Để tạo một ứng dụng, hãy hoàn tất hướng dẫn bắt đầu nhanh về HTTP.

Apps Script

Một ứng dụng Google Chat nhận và phản hồi các hoạt động tương tác của người dùng. Để tạo một tiện ích bổ sung, hãy hoàn tất hướng dẫn bắt đầu nhanh về Apps Script.

Tạo biểu mẫu bằng thẻ

Để thu thập thông tin, các ứng dụng Chat thiết kế biểu mẫu và dữ liệu đầu vào, rồi đưa vào thẻ. Để hiển thị thẻ cho người dùng, ứng dụng Chat có thể sử dụng các giao diện Chat sau:

  • Tin nhắn chứa một hoặc nhiều thẻ.
  • Trang chủ, là một thẻ xuất hiện trong thẻ Trang chủ trong tin nhắn trực tiếp với ứng dụng Chat.
  • Hộp thoại, là các thẻ mở ra trong một cửa sổ mới từ tin nhắn và trang chủ.

Ứng dụng trò chuyện có thể tạo thẻ bằng các tiện ích sau:

  • Tiện ích nhập biểu mẫu yêu cầu thông tin từ người dùng. Bạn có thể thêm quy trình xác thực vào các tiện ích nhập dữ liệu của biểu mẫu để đảm bảo người dùng nhập và định dạng thông tin chính xác. Ứng dụng trò chuyện có thể sử dụng các tiện ích nhập biểu mẫu sau:

  • Một tiện ích nút để người dùng có thể gửi các giá trị mà họ đã nhập vào thẻ. Sau khi người dùng nhấp vào nút, ứng dụng Chat có thể xử lý thông tin mà ứng dụng nhận được.

Trong ví dụ sau, một thẻ thu thập thông tin liên hệ bằng cách sử dụng phương thức nhập văn bản, bộ chọn ngày giờ và phương thức nhập lựa chọn:

Để xem thêm ví dụ về các tiện ích tương tác mà bạn có thể dùng để thu thập thông tin, hãy xem bài viết Thiết kế thẻ hoặc hộp thoại tương tác.

Thêm trình đơn thả xuống

Để tuỳ chỉnh các mục lựa chọn hoặc cho phép người dùng chọn một mục trong nguồn dữ liệu động, ứng dụng Chat có thể sử dụng trình đơn thả xuống, đây là một loại tiện ích SelectionInput. Ví dụ: thẻ sau đây hiển thị một trình đơn thả xuống, trong đó người dùng có thể chọn linh hoạt trong danh sách người liên hệ:

Bạn có thể điền các mục cho trình đơn thả xuống từ các nguồn dữ liệu sau:

Điền các mục từ một nguồn dữ liệu của Google Workspace

Để điền các mục từ nguồn dữ liệu của Google Workspace, chẳng hạn như người dùng Google Workspace, hãy chỉ định trường platformDataSource trong đối tượng DataSourceConfig. Không giống như các loại dữ liệu đầu vào lựa chọn khác, bạn bỏ qua các đối tượng SelectionItem vì các mục lựa chọn này được lấy động từ Google Workspace.

Đoạn mã sau đây cho thấy trình đơn thả xuống gồm những người dùng Google Workspace:

JSON

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

Điền các mục từ một nguồn dữ liệu bên ngoài

Trình đơn thả xuống cũng có thể điền các mục từ một nguồn dữ liệu bên ngoài hoặc bên thứ ba. Để sử dụng nguồn dữ liệu bên ngoài, bạn cần chỉ định trường remoteDataSource trong đối tượng DataSourceConfig chứa hàm truy vấn và trả về các mục từ nguồn dữ liệu.

Để giảm số lượng yêu cầu đến nguồn dữ liệu bên ngoài, bạn có thể thêm các mục đề xuất xuất hiện trong trình đơn thả xuống trước khi người dùng nhập vào trình đơn. Để điền các mục được đề xuất từ một nguồn dữ liệu bên ngoài, hãy chỉ định các đối tượng SelectionItem tĩnh.

Mã sau đây cho thấy một trình đơn thả xuống truy vấn và điền sẵn các mục từ một nguồn dữ liệu bên ngoài:

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

Để xem ví dụ hoàn chỉnh về cách trả về các mục được đề xuất, hãy xem phần Đề xuất các mục lựa chọn.

Thêm trình đơn chọn nhiều mục

Để tuỳ chỉnh các mục lựa chọn hoặc cho phép người dùng chọn các mục từ một nguồn dữ liệu động, ứng dụng Chat có thể sử dụng trình đơn nhiều lựa chọn, đây là một loại tiện ích SelectionInput. Ví dụ: thẻ sau đây hiển thị một trình đơn chọn nhiều mục, trong đó người dùng có thể chọn linh hoạt trong danh sách người liên hệ:

Bạn có thể điền các mục cho trình đơn nhiều lựa chọn từ các nguồn dữ liệu sau:

  • Dữ liệu trên Google Workspace, bao gồm người dùng hoặc không gian trong Chat mà người dùng là thành viên. Trình đơn này chỉ điền sẵn các mục trong cùng một tổ chức Google Workspace.
  • Nguồn dữ liệu bên ngoài, chẳng hạn như cơ sở dữ liệu quan hệ. Ví dụ: bạn có thể sử dụng trình đơn chọn nhiều mục để giúp người dùng chọn trong danh sách khách hàng tiềm năng từ hệ thống quản lý quan hệ khách hàng (CRM).

Điền các mục từ một nguồn dữ liệu của Google Workspace

Để sử dụng các nguồn dữ liệu của Google Workspace, hãy chỉ định trường platformDataSource trong tiện ích SelectionInput. Không giống như các loại dữ liệu đầu vào lựa chọn khác, bạn bỏ qua các đối tượng SelectionItem vì các mục lựa chọn này được lấy động từ Google Workspace.

Mã sau đây cho thấy một trình đơn nhiều lựa chọn gồm những người dùng Google Workspace. Để điền người dùng, dữ liệu đầu vào lựa chọn sẽ đặt commonDataSource thành USER:

JSON

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

Mã sau đây cho thấy một trình đơn chọn nhiều mục của không gian trò chuyện. Để điền vào không gian, dữ liệu đầu vào của lựa chọn sẽ chỉ định trường hostAppDataSource. Trình đơn nhiều lựa chọn cũng đặt defaultToCurrentSpace thành true, giúp không gian hiện tại trở thành lựa chọn mặc định trong trình đơn:

JSON

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

Điền các mục từ một nguồn dữ liệu bên ngoài

Trình đơn nhiều lựa chọn cũng có thể điền sẵn các mục từ một nguồn dữ liệu bên ngoài hoặc bên thứ ba. Để sử dụng nguồn dữ liệu bên ngoài, bạn chỉ định trường externalDataSource trong tiện ích SelectionInput chứa hàm truy vấn và trả về các mục từ nguồn dữ liệu.

Để giảm số lượng yêu cầu đến nguồn dữ liệu bên ngoài, bạn có thể thêm các mục đề xuất xuất hiện trong trình đơn chọn nhiều mục trước khi người dùng nhập vào trình đơn. Ví dụ: bạn có thể điền sẵn những người liên hệ mà người dùng tìm kiếm gần đây. Để điền các mục được đề xuất từ một nguồn dữ liệu bên ngoài, hãy chỉ định các đối tượng SelectionItem tĩnh.

Mã mẫu sau đây cho thấy một trình đơn nhiều lựa chọn truy vấn và điền sẵn các mục từ một nguồn dữ liệu bên ngoài:

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP truy vấn nguồn dữ liệu bên ngoài.

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP truy vấn nguồn dữ liệu bên ngoài.

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP truy vấn nguồn dữ liệu bên ngoài.

Apps Script

Ví dụ này gửi một tin nhắn dạng thẻ bằng cách trả về JSON của thẻ. Bạn cũng có thể sử dụng Dịch vụ thẻ 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")]
}

Để xem ví dụ hoàn chỉnh về cách trả về các mục được đề xuất, hãy xem phần Đề xuất các mục lựa chọn.

Nhận dữ liệu từ các tiện ích tương tác

Bất cứ khi nào người dùng nhấp vào một nút, hành động của ứng dụng Chat sẽ được kích hoạt cùng với thông tin về hoạt động tương tác. Trong commonEventObject của tải trọng sự kiện, đối tượng formInputs chứa mọi giá trị mà người dùng nhập.

Bạn có thể truy xuất các giá trị từ đối tượng event.commonEventObject.formInputs.WIDGET_NAME, trong đó WIDGET_NAME là trường name mà bạn đã chỉ định cho tiện ích. Các giá trị được trả về dưới dạng một loại dữ liệu cụ thể cho tiện ích.

Phần sau đây cho thấy một phần của đối tượng sự kiện mà người dùng đã nhập giá trị cho từng tiện ích:

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

Để nhận dữ liệu, ứng dụng Chat của bạn sẽ xử lý đối tượng sự kiện để lấy các giá trị mà người dùng nhập vào tiện ích. Bảng sau đây cho biết cách lấy giá trị cho một tiện ích nhập dữ liệu biểu mẫu nhất định. Đối với mỗi tiện ích, bảng này cho biết kiểu dữ liệu mà tiện ích chấp nhận, vị trí lưu trữ giá trị trong đối tượng sự kiện và giá trị mẫu.

Tiện ích nhập dữ liệu vào biểu mẫu Loại dữ liệu đầu vào Giá trị đầu vào từ đối tượng sự kiện Giá trị mẫu
textInput stringInputs event.commonEventObject.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs Để lấy giá trị đầu tiên hoặc giá trị duy nhất, hãy dùng event.commonEventObject.formInputs.contactType.stringInputs.value[0] Personal
dateTimePicker chỉ chấp nhận ngày. dateInput event.commonEventObject.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Sau khi nhận được dữ liệu, ứng dụng Chat có thể làm bất kỳ việc nào sau đây:

  • Đối với các thẻ có trình đơn chọn nhiều mục, hãy điền sẵn hoặc đề xuất các mục dựa trên nội dung người dùng nhập vào trình đơn.
  • Chuyển dữ liệu sang một thẻ khác để người dùng có thể xem lại thông tin của họ hoặc tiếp tục chuyển sang phần tiếp theo của biểu mẫu.
  • Phản hồi người dùng để xác nhận rằng người dùng đã hoàn tất biểu mẫu thành công.

Đề xuất các mục lựa chọn

Nếu một thẻ chứa trình đơn nhiều lựa chọn hoặc trình đơn thả xuống điền sẵn các mục từ một nguồn dữ liệu bên ngoài, thì ứng dụng Chat có thể trả về các mục được đề xuất dựa trên nội dung người dùng nhập vào trình đơn. Ví dụ: nếu người dùng bắt đầu nhập Atl cho một trình đơn điền sẵn các thành phố ở Hoa Kỳ, thì ứng dụng Chat có thể tự động đề xuất Atlanta trước khi người dùng nhập xong. Ứng dụng Chat có thể đề xuất tối đa 100 mục.

Để đề xuất và tự động điền các mục trong một dữ liệu đầu vào lựa chọn, tiện ích SelectionInput trên thẻ phải chỉ định một hàm truy vấn nguồn dữ liệu bên ngoài. Đối với trình đơn nhiều lựa chọn, bạn chỉ định trường externalDataSource. Đối với trình đơn thả xuống, bạn chỉ định trường remoteDataSource trong đối tượng DataSourceConfig.

Bạn cũng có thể định cấu hình số ký tự mà người dùng nhập trước khi trình đơn trả về nội dung đề xuất. Đối với trình đơn nhiều lựa chọn, hãy đặt trường multiSelectMinQueryLength. Đối với trình đơn thả xuống, hãy đặt trường min_characters_trigger trong DataSourceConfig.

Để trả về các mục được đề xuất, hàm phải làm như sau:

  1. Xử lý một đối tượng sự kiện mà ứng dụng Chat nhận được khi người dùng nhập vào trình đơn.
  2. Từ đối tượng sự kiện, hãy lấy giá trị mà người dùng nhập, được biểu thị trong trường event.commonEventObject.parameters["autocomplete_widget_query"].
  3. Truy vấn nguồn dữ liệu bằng giá trị do người dùng nhập để nhận một hoặc nhiều SelectionItems đề xuất cho người dùng.
  4. Trả về các mục được đề xuất bằng cách trả về hành động RenderActions bằng đối tượng modifyCard.

Mã mẫu sau đây cho thấy cách một ứng dụng Chat đề xuất các mục một cách linh động trong trình đơn nhiều lựa chọn trên một thẻ. Khi người dùng nhập vào trình đơn, hàm hoặc điểm cuối được cung cấp trong trường externalDataSource của tiện ích sẽ truy vấn một nguồn dữ liệu bên ngoài và đề xuất các mục mà người dùng có thể chọn.

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP truy vấn nguồn dữ liệu bên ngoài.

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
  }

Thay thế FUNCTION_URL bằng điểm cuối HTTP truy vấn nguồn dữ liệu bên ngoài.

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP truy vấn nguồn dữ liệu bên ngoài.

Apps Script

Ví dụ này gửi một tin nhắn dạng thẻ bằng cách trả về JSON của thẻ. Bạn cũng có thể sử dụng Dịch vụ thẻ 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
  };
}

Chuyển dữ liệu sang thẻ khác

Sau khi người dùng gửi thông tin từ một thẻ, bạn có thể cần trả về các thẻ bổ sung để thực hiện bất kỳ thao tác nào sau đây:

  • Giúp người dùng hoàn thành các biểu mẫu dài hơn bằng cách tạo các phần riêng biệt.
  • Cho phép người dùng xem trước và xác nhận thông tin từ thẻ ban đầu để họ có thể xem lại câu trả lời trước khi gửi.
  • Điền động vào các phần còn lại của biểu mẫu. Ví dụ: để nhắc người dùng tạo cuộc hẹn, ứng dụng Chat có thể hiển thị một thẻ ban đầu yêu cầu lý do của cuộc hẹn, sau đó điền vào một thẻ khác cung cấp thời gian rảnh dựa trên loại cuộc hẹn.

Để chuyển dữ liệu đầu vào từ thẻ ban đầu, bạn có thể tạo tiện ích button bằng actionParameters chứa name của tiện ích và giá trị mà người dùng nhập, như trong ví dụ sau:

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP xử lý các lượt nhấp vào nút.

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP xử lý các lượt nhấp vào nút.

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

Thay thế FUNCTION_URL bằng điểm cuối HTTP xử lý các lượt nhấp vào nút.

Apps Script

Ví dụ này gửi một tin nhắn dạng thẻ bằng cách trả về JSON của thẻ. Bạn cũng có thể sử dụng Dịch vụ thẻ 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 }
    ]
  }}
}]}}

Khi người dùng nhấp vào nút, ứng dụng Chat của bạn sẽ nhận được một đối tượng sự kiện mà từ đó bạn có thể nhận dữ liệu.

Phản hồi một biểu mẫu đã gửi

Sau khi nhận được dữ liệu từ một tin nhắn hoặc hộp thoại dạng thẻ, Ứng dụng Chat sẽ phản hồi bằng cách xác nhận đã nhận hoặc trả về lỗi.

Trong ví dụ sau, một ứng dụng Chat gửi tin nhắn văn bản để xác nhận rằng ứng dụng đã nhận thành công biểu mẫu được gửi từ tin nhắn dạng thẻ.

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

Ví dụ này gửi một tin nhắn dạng thẻ bằng cách trả về JSON của thẻ. Bạn cũng có thể sử dụng Dịch vụ thẻ 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."
}}}}};

Để xử lý và đóng hộp thoại, bạn trả về một đối tượng RenderActions chỉ định xem bạn muốn gửi thông báo xác nhận, cập nhật tin nhắn hoặc thẻ ban đầu hay chỉ đóng hộp thoại. Để biết các bước thực hiện, hãy xem phần Đóng hộp thoại.

Khắc phục sự cố

Phần này cung cấp các bước khắc phục sự cố cho các mã lỗi và hành vi cụ thể trong thời gian chạy khi tương tác với hộp thoại trong Chat.

Các lượt tương tác trong hộp thoại trả về lỗi "Lỗi không xác định khi gọi tiện ích bổ sung"

Khi tương tác với một hộp thoại, nếu bạn thấy nhật ký lỗi có thông báo "Lỗi không xác định khi gọi tiện ích bổ sung" và mã 13, thì điều này thường cho biết lỗi nội bộ hoặc điểm cuối HTTP của ứng dụng Chat không xử lý được yêu cầu hoặc không trả về phản hồi hợp lệ.

Cách khắc phục lỗi này:

  • Kiểm tra nhật ký của điểm cuối HTTP để xem có ngoại lệ hoặc sự cố nào chưa được xử lý hay không.
  • Xác minh rằng điểm cuối của bạn phản hồi các yêu cầu trong vòng 30 giây. Nếu điểm cuối mất hơn 30 giây để chạy, thì Chat không thể xử lý phản hồi và hoạt động tương tác sẽ không thành công. Để biết thông tin chi tiết, hãy xem phần Giới hạn tốc độ và các phương pháp hay nhất.
  • Đảm bảo điểm cuối của bạn trả về phản hồi hợp lệ. Đối với nội dung gửi hộp thoại, điểm cuối phải trả về một đối tượng RenderActions ở định dạng JSON chính xác. Nếu phản hồi không đúng định dạng hoặc không chứa các trường bắt buộc, thì hoạt động tương tác với hộp thoại có thể không thành công.

Khi một ứng dụng Google Chat hoặc thẻ trả về lỗi, giao diện Chat sẽ hiển thị thông báo "Đã xảy ra lỗi". hoặc "Không thể xử lý yêu cầu của bạn". Đôi khi, giao diện người dùng của Chat không hiển thị thông báo lỗi, nhưng ứng dụng Chat hoặc thẻ lại tạo ra kết quả không mong muốn; ví dụ: thông báo dạng thẻ có thể không xuất hiện.

Mặc dù thông báo lỗi có thể không xuất hiện trong giao diện người dùng của Chat, nhưng thông báo lỗi mô tả và dữ liệu nhật ký sẽ giúp bạn khắc phục lỗi khi bật tính năng ghi nhật ký lỗi cho ứng dụng Chat. Để được trợ giúp xem, gỡ lỗi và khắc phục lỗi, hãy xem bài viết Khắc phục sự cố và sửa lỗi trong Google Chat.

Ứng dụng Chat không phải là tiện ích bổ sung: Đọc dữ liệu biểu mẫu do người dùng nhập trên thẻ

Tài liệu sau đây áp dụng cho các ứng dụng Chat không phải là tiện ích bổ sung của Google Workspace. Để di chuyển một ứng dụng Chat không phải là tiện ích bổ sung, hãy xem bài viết Chuyển đổi ứng dụng Google Chat thành tiện ích bổ sung của Google Workspace.

Tạo biểu mẫu bằng thẻ

Để xem ví dụ về một ứng dụng Chat không phải là tiện ích bổ sung sử dụng biểu mẫu liên hệ có dữ liệu đầu vào dạng văn bản, bộ chọn ngày giờ và dữ liệu đầu vào dạng lựa chọn, hãy xem mã sau:

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

Nhận dữ liệu từ các tiện ích tương tác

Bất cứ khi nào người dùng nhấp vào một nút, các ứng dụng Chat không phải là tiện ích bổ sung sẽ nhận được một sự kiện tương tác tuỳ thuộc vào vị trí của nút:

  • Nếu nút nằm trong một tin nhắn hoặc hộp thoại, thì các ứng dụng Chat không phải là tiện ích bổ sung sẽ nhận được một CARD_CLICKED sự kiện tương tác chứa thông tin về lượt tương tác. Trọng tải của các sự kiện tương tác CARD_CLICKED chứa một đối tượng common.formInputs (event.common.formInputs) có mọi giá trị mà người dùng nhập.

    Bạn có thể truy xuất các giá trị từ đối tượng common.formInputs.WIDGET_NAME, trong đó WIDGET_NAME là trường name mà bạn đã chỉ định cho tiện ích. Các giá trị được trả về dưới dạng một loại dữ liệu cụ thể cho tiện ích (được biểu thị dưới dạng đối tượng Inputs).

    Phần sau đây cho thấy một phần của sự kiện tương tác CARD_CLICKED, trong đó người dùng nhập giá trị cho từng tiện ích:

    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"]
        }}}
      }}
    }
    
  • Nếu nút này nằm trên trang chủ, thì các ứng dụng Chat không phải là tiện ích bổ sung sẽ nhận được một sự kiện tương tác SUBMIT_FORM. Trọng tải của sự kiện tương tác chứa một đối tượng commonEventObject.formInputs (event.commonEventObject.formInputs) có mọi giá trị mà người dùng nhập.

    Bạn có thể truy xuất các giá trị từ đối tượng commonEventObject.formInputs.WIDGET_NAME, trong đó WIDGET_NAME là trường name mà bạn đã chỉ định cho tiện ích. Các giá trị được trả về dưới dạng một loại dữ liệu cụ thể cho tiện ích (được biểu thị dưới dạng đối tượng Inputs).

    Phần sau đây cho thấy một phần của sự kiện tương tác SUBMIT_FORM, trong đó người dùng nhập giá trị cho từng tiện ích:

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

Để nhận dữ liệu, một ứng dụng Chat không phải là tiện ích bổ sung sẽ xử lý sự kiện tương tác để lấy các giá trị mà người dùng nhập vào tiện ích. Bảng sau đây cho biết cách lấy giá trị cho một tiện ích nhập dữ liệu biểu mẫu nhất định. Đối với mỗi tiện ích, bảng này cho thấy kiểu dữ liệu mà tiện ích chấp nhận, nơi lưu trữ giá trị trong sự kiện tương tác và một giá trị mẫu.

Tiện ích nhập dữ liệu vào biểu mẫu Loại dữ liệu đầu vào Giá trị đầu vào từ sự kiện tương tác Giá trị mẫu
textInput stringInputs event.common.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs Để lấy giá trị đầu tiên hoặc giá trị duy nhất, hãy dùng event.common.formInputs.contactType.stringInputs.value[0] Personal
dateTimePicker chỉ chấp nhận ngày. dateInput event.common.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Chuyển dữ liệu sang thẻ khác

Để chuyển dữ liệu đầu vào từ thẻ ban đầu trong một ứng dụng Chat không phải là tiện ích bổ sung, hãy tạo tiện ích button bằng actionParameters chứa name của tiện ích và giá trị mà người dùng nhập, như trong ví dụ sau:

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

Khi người dùng nhấp vào nút, ứng dụng Chat không phải là tiện ích bổ sung sẽ nhận được một sự kiện tương tác CARD_CLICKED mà bạn có thể nhận dữ liệu.

Phản hồi một biểu mẫu đã gửi

Trong ví dụ sau, một ứng dụng Chat không phải là tiện ích bổ sung sẽ gửi tin nhắn văn bản để xác nhận rằng ứng dụng đã nhận thành công biểu mẫu được gửi từ hộp thoại hoặc tin nhắn dạng thẻ:

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

Để xử lý và đóng hộp thoại trong một ứng dụng Chat không phải là tiện ích bổ sung, hãy trả về một đối tượng ActionResponse chỉ định xem bạn muốn gửi thông báo xác nhận, cập nhật tin nhắn hoặc thẻ ban đầu hay chỉ đóng hộp thoại. Để biết các bước thực hiện, hãy xem phần Đóng hộp thoại.