תמיכה בתיבות דו-שיח אינטראקטיביות

במאמר הזה נסביר איך אפליקציית Google Chat יכולה לפתוח תיבות דו-שיח כדי להציג ממשקי משתמש ולהגיב למשתמשים.

תיבות דו-שיח הן ממשקי חלונות מבוססי-כרטיסים שנפתחים ממרחב או מהודעה ב-Chat. רק המשתמש שפתח את תיבת הדו-שיח יכול לראות אותה ואת התוכן שלה.

אפליקציות צ'אט יכולות להשתמש בתיבות דו-שיח כדי לבקש ולאסוף מידע ממשתמשי Chat, כולל טפסים מרובי שלבים. פרטים נוספים על יצירת שדות קלט בטופס זמינים במאמר איסוף ועיבוד מידע מהמשתמשים.

דרישות מוקדמות

HTTP

אפליקציה ל-Google Chat שמקבלת אינטראקציות עם משתמשים ומגיבה להן. כדי ליצור אחד, צריך להשלים את המדריך למתחילים בנושא HTTP.

Apps Script

אפליקציה ל-Google Chat שמקבלת אינטראקציות עם משתמשים ומגיבה להן. כדי ליצור כזה, צריך להשלים את המדריך למתחילים של Apps Script.

פתיחת תיבת דו-שיח

תיבת דו-שיח עם מגוון ווידג'טים שונים.
איור 1: דוגמה לאפליקציית Chat שפותחת תיבת דו-שיח לאיסוף פרטים ליצירת קשר.

בקטע הזה מוסבר איך להגיב ולהגדיר דו-שיח באמצעות הפעולות הבאות:

  1. הפעלת בקשת תיבת הדו-שיח מאינטראקציה של משתמש.
  2. מטפלים בבקשה על ידי החזרה ופתיחה של תיבת דו-שיח.
  3. אחרי שהמשתמשים שולחים מידע, מעבדים את השליחה על ידי סגירת תיבת הדו-שיח או הצגת תיבת דו-שיח אחרת.

הפעלת בקשה לתיבת דו-שיח

אפליקציה ל-Chat יכולה לפתוח תיבות דו-שיח רק כדי להגיב לאינטראקציה של משתמש, כמו פקודה או לחיצה על לחצן בהודעה בכרטיס.

כדי להגיב למשתמשים באמצעות תיבת דו-שיח, אפליקציית Chat צריכה ליצור אינטראקציה שמפעילה את בקשת תיבת הדו-שיח, כמו:

  • להגיב לפקודה. כדי להפעיל את הבקשה מפקודה, צריך לסמן את התיבה Opens a dialog כשמגדירים את הפקודה.
  • להגיב ללחיצה על לחצן בהודעה, כחלק מכרטיס או בתחתית ההודעה. כדי להפעיל את הבקשה באמצעות לחצן בהודעה, מגדירים את הפעולה של הלחצן onClick על ידי הגדרת הערך של interaction ל-OPEN_DIALOG.
  • איך מגיבים ללחיצה על לחצן בדף הבית של אפליקציה ב-Chat מידע על פתיחת תיבות דו-שיח מדפי הבית זמין במאמר יצירת דף בית לאפליקציית Google Chat.
כפתור שמפעיל תיבת דו-שיח
איור 2: אפליקציה ל-Chat שולחת הודעה שמבקשת מהמשתמשים להשתמש /addContact בפקודה דרך שורת הפקודות.
ההודעה כוללת גם לחצן שהמשתמשים יכולים ללחוץ עליו כדי להפעיל את הפקודה.

בדוגמת הקוד הבאה אפשר לראות איך להפעיל בקשה לדיאלוג מלחצן בהודעת כרטיס. כדי לפתוח את תיבת הדו-שיח, מגדירים את השדה onClick.action.interaction של הכפתור לערך OPEN_DIALOG:

Node.js

node/chat/contact-form-app/index.js
{ buttonList: { buttons: [{
  text: "ADD CONTACT",
  onClick: { action: {
    function: FUNCTION_URL,
    interaction: "OPEN_DIALOG",
    parameters: [
      { key: "actionName", value: "openInitialDialog" }
    ]
  }}
}]}}

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Python

python/chat/contact-form-app/main.py
{ 'buttonList': { 'buttons': [{
  'text': "ADD CONTACT",
  'onClick': { 'action': {
    'function': FUNCTION_URL,
    'interaction': "OPEN_DIALOG",
    'parameters': [
      { 'key': "actionName", 'value': "openInitialDialog" }
    ]
  }}
}]}}

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Java

java/chat/contact-form-app/src/main/java/com/google/chat/contact/App.java
.setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()
  .setText("ADD CONTACT")
  .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
    .setFunction(FUNCTION_URL)
    .setInteraction("OPEN_DIALOG")
    .setParameters(List.of(
      new GoogleAppsCardV1ActionParameter().setKey("actionName").setValue("openInitialDialog"))))))))));

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/chat/contact-form-app/Code.gs
{ buttonList: { buttons: [{
  text: "ADD CONTACT",
  onClick: { action: {
    function: "openInitialDialog",
    interaction: "OPEN_DIALOG"
  }}
}]}}

פתיחת תיבת הדו-שיח הראשונית

כשמשתמש מפעיל בקשה לתיבת דו-שיח, אפליקציית הצ'אט שלכם מקבלת אובייקט אירוע עם מטען ייעודי (payload) שמציין אובייקט dialogEventType בתור REQUEST_DIALOG.

כדי לפתוח תיבת דו-שיח, אפליקציית Chat יכולה להגיב לבקשה על ידי החזרת אובייקט RenderActions עם הניווט pushCard כדי להציג כרטיס. הכרטיס צריך להכיל רכיבים של ממשק משתמש, כולל אחד או יותר sections[] של ווידג'טים. כדי לאסוף מידע מהמשתמשים, אפשר לציין ווידג'טים של שדות קלט בטופס ווידג'ט של לחצן. מידע נוסף על עיצוב שדות קלט בטפסים זמין במאמר איסוף ועיבוד מידע מהמשתמשים.

בדוגמת הקוד הבאה אפשר לראות איך אפליקציית Chat מחזירה תגובה שפותחת תיבת דו-שיח:

Node.js

node/chat/contact-form-app/index.js
/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} open the dialog.
 */
function openInitialDialog() {
  return { action: { navigations: [{ pushCard: { sections: [{ 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 }
      ]
    }},
    { buttonList: { buttons: [{
      text: "NEXT",
      onClick: { action: {
        function: FUNCTION_URL,
        parameters: [
          { key: "actionName", value: "openConfirmationDialog" }
        ]
      }}
    }]}}
  ]}]}}]}};
}

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Python

python/chat/contact-form-app/main.py
def open_initial_dialog() -> Mapping[str, Any]:
  """Opens the initial step of the dialog that lets users add contact details.

  Returns:
    Mapping[str, Any]: open the dialog.
  """
  return { 'action': { 'navigations': [{ 'pushCard': { 'sections': [{ '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 }
      ]
    }},
    { 'buttonList': { 'buttons': [{
      'text': "NEXT",
      'onClick': { 'action': {
        'function': FUNCTION_URL,
        'parameters': [
          { 'key': "actionName", 'value': "openConfirmationDialog" }
        ]
      }}
    }]}}
  ]}]}}]}}

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Java

java/chat/contact-form-app/src/main/java/com/google/chat/contact/App.java
/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} open the dialog.
 */
GenericJson openInitialDialog() {
  GoogleAppsCardV1Card cardV2 = new GoogleAppsCardV1Card()
    .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(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)))),
      new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(
        new GoogleAppsCardV1Button()
          .setText("NEXT")
          .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
            .setFunction(FUNCTION_URL)
            .setParameters(List.of(
              new GoogleAppsCardV1ActionParameter().setKey("actionName").setValue("openConfirmationDialog"))))))))))));
  return new GenericJson() {{
    put("action", new GenericJson() {{
      put("navigations", List.of(new GenericJson() {{
        put("pushCard", cardV2);
      }}));
    }});
  }};
}

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/chat/contact-form-app/Code.gs
/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 * @return {Object} open the dialog.
 */
function openInitialDialog(event) {
  return { action: { navigations: [{ pushCard: { sections: [{ 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 }
      ]
    }},
    { buttonList: { buttons: [{
      text: "NEXT",
      onClick: { action: { function : "openConfirmationDialog" }}
    }]}}
  ]}]}}]}};
}

טיפול בשליחת תיבת הדו-שיח

כשמשתמשים לוחצים על לחצן לשליחת תיבת דו-שיח, אפליקציית הצ'אט שלכם מקבלת אובייקט אירוע עם אובייקט buttonClickedPayload. במטען הייעודי (payload), הערך של dialogEventType מוגדר ל-SUBMIT_DIALOG. כדי להבין איך לאסוף ולעבד את המידע בתיבת הדו-שיח, אפשר לעיין במאמר איך אוספים ומעבדים מידע ממשתמשי Google Chat.

אפליקציית Chat צריכה להגיב לאובייקט האירוע באחת מהדרכים הבאות:

אופציונלי: חזרה לתיבת דו-שיח אחרת

אחרי שהמשתמשים שולחים את תיבת הדו-שיח הראשונית, אפליקציות ל-Chat יכולות להחזיר תיבות דו-שיח נוספות כדי לעזור למשתמשים לבדוק את המידע לפני השליחה, למלא טפסים מרובי שלבים או לאכלס את תוכן הטופס באופן דינמי.

כדי לעבד את הנתונים שהמשתמשים מזינים, אפליקציית Chat מטפלת בנתונים באובייקט commonEventObject.formInputs של האירוע. מידע נוסף על אחזור ערכים מווידג'טים של קלט זמין במאמר איסוף ועיבוד מידע מהמשתמשים.

כדי לעקוב אחרי נתונים שהמשתמשים מזינים בתיבת הדו-שיח הראשונה, צריך להוסיף פרמטרים ללחצן שפותח את תיבת הדו-שיח הבאה. פרטים נוספים זמינים במאמר בנושא העברת נתונים לכרטיס אחר.

בדוגמה הזו, אפליקציית Chat פותחת תיבת דו-שיח ראשונית שמובילה לתיבת דו-שיח שנייה לאישור לפני השליחה:

Node.js

node/chat/contact-form-app/index.js
/**
 * Responds to a message in Google Chat.
 *
 * @return {Object} response that handles dialogs.
 */
function handleMessage() {
  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    text: "To add a contact, use the `ADD CONTACT` button below.",
    accessoryWidgets: [
      { buttonList: { buttons: [{
        text: "ADD CONTACT",
        onClick: { action: {
          function: FUNCTION_URL,
          interaction: "OPEN_DIALOG",
          parameters: [
            { key: "actionName", value: "openInitialDialog" }
          ]
        }}
      }]}}
    ]
  }}}}};
}

/**
 * Responds to a button clicked in Google Chat.
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 * @return {Object} response depending on the button clicked.
 */
function handleButtonClicked(event) {
  // Initial dialog form page
  if (event.commonEventObject.parameters.actionName === "openInitialDialog") {
    return openInitialDialog();
  // Confirmation dialog form page
  } else if (event.commonEventObject.parameters.actionName === "openConfirmationDialog") {
    return openConfirmationDialog(event);
  // Submission dialog form page
  } else if (event.commonEventObject.parameters.actionName === "submitDialog") {
    return submitDialog(event);
  }
}

/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} open the dialog.
 */
function openInitialDialog() {
  return { action: { navigations: [{ pushCard: { sections: [{ 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 }
      ]
    }},
    { buttonList: { buttons: [{
      text: "NEXT",
      onClick: { action: {
        function: FUNCTION_URL,
        parameters: [
          { key: "actionName", value: "openConfirmationDialog" }
        ]
      }}
    }]}}
  ]}]}}]}};
}

/**
 * Opens the second step of the dialog that lets users confirm details.
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 * @return {Object} update the dialog.
 */
function openConfirmationDialog(event) {
  // Retrieve the form input values
  const name = event.commonEventObject.formInputs["contactName"].stringInputs.value[0];
  const birthdate = event.commonEventObject.formInputs["contactBirthdate"].dateInput.msSinceEpoch;
  const type = event.commonEventObject.formInputs["contactType"].stringInputs.value[0];
  // Display the input values for confirmation
  return { action: { navigations: [{ pushCard: { sections: [{ widgets: [
    { textParagraph: { text: "Confirm contact information and submit:" }},
    { textParagraph: { text: "<b>Name:</b> " + name }},
    { textParagraph: { text: "<b>Birthday:</b> " + new Date(birthdate) }},
    { textParagraph: { text: "<b>Type:</b> " + type }},
    { 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 בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Python

python/chat/contact-form-app/main.py
def handle_message() -> Mapping[str, Any]:
  """Responds to a message in Google Chat.

  Returns:
    Mapping[str, Any]: the response that handles dialogs.
  """
  return { 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': { 'message': {
    'text': "To add a contact, use the `ADD CONTACT` button below.",
    'accessoryWidgets': [
      { 'buttonList': { 'buttons': [{
        'text': "ADD CONTACT",
        'onClick': { 'action': {
          'function': FUNCTION_URL,
          'interaction': "OPEN_DIALOG",
          'parameters': [
            { 'key': "actionName", 'value': "openInitialDialog" }
          ]
        }}
      }]}}
    ]
  }}}}}


def handle_button_clicked(event: Mapping[str, Any]) -> Mapping[str, Any]:
  """Responds to a button clicked in Google Chat.

  Args:
    Mapping[str, Any] event: the event object from the Google Workspace add-on

  Returns:
    Mapping[str, Any]: the response depending on the button clicked.
  """
  # Initial dialog form page
  if "openInitialDialog" == event['commonEventObject']['parameters']['actionName']:
    return open_initial_dialog()
  # Confirmation dialog form page
  elif "openConfirmationDialog" == event['commonEventObject']['parameters']['actionName'] :
    return open_confirmation_dialog(event)
  # Submission dialog form page
  elif "submitDialog" == event['commonEventObject']['parameters']['actionName']:
    return submit_dialog(event)


def open_initial_dialog() -> Mapping[str, Any]:
  """Opens the initial step of the dialog that lets users add contact details.

  Returns:
    Mapping[str, Any]: open the dialog.
  """
  return { 'action': { 'navigations': [{ 'pushCard': { 'sections': [{ '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 }
      ]
    }},
    { 'buttonList': { 'buttons': [{
      'text': "NEXT",
      'onClick': { 'action': {
        'function': FUNCTION_URL,
        'parameters': [
          { 'key': "actionName", 'value': "openConfirmationDialog" }
        ]
      }}
    }]}}
  ]}]}}]}}


def open_confirmation_dialog(event: Mapping[str, Any]) -> Mapping[str, Any]:
  """Opens the second step of the dialog that lets users confirm details.

  Args:
    Mapping[str, Any] event: the event object from the Google Workspace add-on

  Returns:
    Mapping[str, Any]: update the dialog.
  """
  name = event.get('commonEventObject').get('formInputs')["contactName"].get('stringInputs').get('value')[0]
  birthdateEpoch = event.get('commonEventObject').get('formInputs')["contactBirthdate"].get('dateInput').get('msSinceEpoch')
  birthdate = datetime.fromtimestamp(int(birthdateEpoch) / 1000.0).strftime("%Y-%m-%d")
  type = event.get('commonEventObject').get('formInputs')["contactType"].get('stringInputs').get('value')[0]
  # Display the input values for confirmation
  return { 'action': { 'navigations': [{ 'pushCard': { 'sections': [{ 'widgets': [
    { 'textParagraph': { 'text': "Confirm contact information and submit:" }},
    { 'textParagraph': { 'text': "<b>Name:</b> " + name }},
    { 'textParagraph': { 'text': "<b>Birthday:</b> " + birthdate }},
    { 'textParagraph': { 'text': "<b>Type:</b> " + type }},
    { '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 בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Java

java/chat/contact-form-app/src/main/java/com/google/chat/contact/App.java
/**
 * Responds to a message in Google Chat.
 *
 * @return response that handles dialogs.
 */
GenericJson handleMessage() {
  Message message = new Message()
    .setText("To add a contact, use the `ADD CONTACT` button below.")
    .setAccessoryWidgets(List.of(new AccessoryWidget()
      .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()
        .setText("ADD CONTACT")
        .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
          .setFunction(FUNCTION_URL)
          .setInteraction("OPEN_DIALOG")
          .setParameters(List.of(
            new GoogleAppsCardV1ActionParameter().setKey("actionName").setValue("openInitialDialog"))))))))));
  return new GenericJson() {{
    put("hostAppDataAction", new GenericJson() {{
      put("chatDataAction", new GenericJson() {{
        put("createMessageAction", new GenericJson() {{
          put("message", message);
        }});
      }});
    }});
  }};
}

/**
 * Responds to a button clicked in Google Chat.
 *
 * @param event The event object from the Google Workspace add-on.
 * @return response depending on the button clicked.
 */
GenericJson handleButtonClicked(JsonNode event) {
  String actionName = event.at("/commonEventObject/parameters/actionName").asText();
  // Initial dialog form page
  if ("openInitialDialog".equals(actionName)) {
    return openInitialDialog();
  // Confirmation dialog form page
  } else if ("openConfirmationDialog".equals(actionName)) {
    return openConfirmationDialog(event);
  // Submission dialog form page
  } else if ("submitDialog".equals(actionName)) {
    return submitDialog(event);
  }
  return null; 
}

/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} open the dialog.
 */
GenericJson openInitialDialog() {
  GoogleAppsCardV1Card cardV2 = new GoogleAppsCardV1Card()
    .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(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)))),
      new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(
        new GoogleAppsCardV1Button()
          .setText("NEXT")
          .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
            .setFunction(FUNCTION_URL)
            .setParameters(List.of(
              new GoogleAppsCardV1ActionParameter().setKey("actionName").setValue("openConfirmationDialog"))))))))))));
  return new GenericJson() {{
    put("action", new GenericJson() {{
      put("navigations", List.of(new GenericJson() {{
        put("pushCard", cardV2);
      }}));
    }});
  }};
}

/**
 * Opens the second step of the dialog that lets users confirm details.
 *
 * @param event The event object from the Google Workspace add-on.
 * @return update the dialog.
 */
GenericJson openConfirmationDialog(JsonNode event) {
  // Retrieve the form input values
  String name = event.at("/commonEventObject/formInputs/contactName/stringInputs/value").get(0).asText();
  String birthdateEpoch = event.at("/commonEventObject/formInputs/contactBirthdate/dateInput/msSinceEpoch").asText();
  String birthdate = new SimpleDateFormat("MM/dd/yyyy").format(new Date((long)Double.parseDouble(birthdateEpoch)));
  String type = event.at("/commonEventObject/formInputs/contactType/stringInputs/value").get(0).asText();
  // Display the input values for confirmation
  GoogleAppsCardV1Card cardV2 = new GoogleAppsCardV1Card()
    .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("Confirm contact information and submit:")),
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("<b>Name:</b> " + name)),
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("<b>Birthday:</b> " + birthdate)),
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("<b>Type:</b> " + type)),
      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))))))))))));
  return new GenericJson() {{
    put("action", new GenericJson() {{
      put("navigations", List.of(new GenericJson() {{
        put("pushCard", cardV2);
      }}));
    }});
  }};
}

מחליפים את הערך FUNCTION_URL בנקודת הקצה (endpoint) של HTTP שמטפלת בלחיצות על הלחצן.

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/chat/contact-form-app/Code.gs
/**
 * Responds to a message in Google Chat.
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 * @return {Object} response that handles dialogs.
 */
function onMessage(event) {
  // Reply with a message that contains a button to open the initial dialog
  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    text: "To add a contact, use the `ADD CONTACT` button below.",
    accessoryWidgets: [
      { buttonList: { buttons: [{
        text: "ADD CONTACT",
        onClick: { action: {
          function: "openInitialDialog",
          interaction: "OPEN_DIALOG"
        }}
      }]}}
    ]
  }}}}};
}

/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 * @return {Object} open the dialog.
 */
function openInitialDialog(event) {
  return { action: { navigations: [{ pushCard: { sections: [{ 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 }
      ]
    }},
    { buttonList: { buttons: [{
      text: "NEXT",
      onClick: { action: { function : "openConfirmationDialog" }}
    }]}}
  ]}]}}]}};
}

/**
 * Opens the second step of the dialog that lets users confirm details.
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 * @return {Object} update the dialog.
 */
function openConfirmationDialog(event) {
  // Retrieve the form input values
  const name = event.commonEventObject.formInputs["contactName"].stringInputs.value[0];
  const birthdate = event.commonEventObject.formInputs["contactBirthdate"].dateInput.msSinceEpoch;
  const type = event.commonEventObject.formInputs["contactType"].stringInputs.value[0];
  // Display the input values for confirmation
  return { action: { navigations: [{ pushCard: { sections: [{ widgets: [
    { textParagraph: { text: "Confirm contact information and submit:" }},
    { textParagraph: { text: "<b>Name:</b> " + name }},
    { textParagraph: { text: "<b>Birthday:</b> " + new Date(birthdate) }},
    { textParagraph: { text: "<b>Type:</b> " + type }},
    { 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 }
        ]
      }}
    }]}}
  ]}]}}]}};
}

סגירה של תיבת הדו-שיח

כשמשתמשים לוחצים על לחצן השליחה בתיבת דו-שיח, אפליקציית הצ'אט מבצעת את הפעולה שמשויכת ללחצן ומספקת את אובייקט האירוע עם הערך buttonClickedPayload שמוגדר כך:

  • isDialogEvent היא true.
  • dialogEventType היא SUBMIT_DIALOG.

אפליקציית Chat צריכה להחזיר אובייקט RenderActions עם הערך CLOSE_DIALOG (endNavigation: "CLOSE_DIALOG") של endNavigation.

אופציונלי: הצגת התראה זמנית

כשסוגרים את תיבת הדו-שיח, אפשר גם להציג למשתמש הודעת טקסט זמנית שמופיעה בזמן האינטראקציה עם האפליקציה.

כדי להציג התראה, מחזירים את האובייקט RenderActions עם השדה notification מוגדר.

בדוגמה הבאה, תיבת הדו-שיח נסגרת עם התראת טקסט:

Node.js

node/chat/contact-form-app/index.js
// Validate the parameters.
if (!event.commonEventObject.parameters["contactName"]) {
  return { action: {
    navigations: [{ endNavigation: { action: "CLOSE_DIALOG"}}],
    notification: { text: "Failure, the contact name was missing!" }
  }};
}

Python

python/chat/contact-form-app/main.py
# Validate the parameters.
if event.get('commonEventObject').get('parameters')["contactName"] == "":
  return { 'action': {
    'navigations': [{ 'endNavigation': { 'action': "CLOSE_DIALOG"}}],
    'notification': { 'text': "Failure, the contact name was missing!" }
  }}

Java

java/chat/contact-form-app/src/main/java/com/google/chat/contact/App.java
    // Validate the parameters.
    if (event.at("/commonEventObject/parameters/contactName").asText().isEmpty()) {
      return new GenericJson() {{
        put("action", new GenericJson() {{
          put("navigations", List.of(new GenericJson() {{
            put("endNavigation", new GenericJson() {{
              put("action", "CLOSE_DIALOG");
            }});
          }}));
          put("notification", new GenericJson() {{
            put("text", "Failure, the contact name was missing!");
          }});
        }});
      }};
    }

    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
// Validate the parameters.
if (!event.commonEventObject.parameters["contactName"]) {
  return { action: {
    navigations: [{ endNavigation: { action: "CLOSE_DIALOG"}}],
    notification: { text: "Failure, the contact name was missing!" }
  }};
}

פרטים על העברת פרמטרים בין תיבות דו-שיח מופיעים במאמר בנושא העברת נתונים לכרטיס אחר.

אופציונלי: שליחת הודעת צ'אט

כשסוגרים את תיבת הדו-שיח, אפשר גם לשלוח הודעת צ'אט חדשה או לעדכן הודעה קיימת.

כדי לשלוח הודעה חדשה, מחזירים אובייקט DataActions עם השדה CreateMessageAction שמוגדר עם ההודעה החדשה. בדוגמה הבאה, תיבת הדו-שיח נסגרת כששולחים הודעה חדשה:

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

כדי לעדכן הודעה אחרי שהמשתמש שולח תיבת דו-שיח, מחזירים אובייקט DataActions שמכיל אחת מהפעולות הבאות:

פתרון בעיות

כשמוחזרת שגיאה מאפליקציה או מכרטיס ב-Google Chat, בממשק של Chat מוצגת ההודעה "משהו השתבש". או 'לא הצלחנו לעבד את הבקשה שלך'. לפעמים בממשק המשתמש של Chat לא מוצגת הודעת שגיאה, אבל אפליקציית Chat או הכרטיס מפיקים תוצאה לא צפויה. לדוגמה, יכול להיות שהודעה בכרטיס לא תופיע.

יכול להיות שלא תוצג הודעת שגיאה בממשק המשתמש של Chat, אבל אם הפעלתם את רישום השגיאות באפליקציות של Chat, תוכלו לראות הודעות שגיאה תיאוריות ונתוני יומן שיעזרו לכם לתקן את השגיאות. לקבלת עזרה בצפייה בשגיאות, באיתור באגים ובתיקון שגיאות, אפשר לעיין במאמר פתרון בעיות ותיקון שגיאות ב-Google Chat.

אפליקציות ל-Chat שלא מוגדרות כתוספים: פתיחת תיבות דו-שיח אינטראקטיביות

המסמכים הבאים רלוונטיים לאפליקציות ל-Chat שהן לא תוספים ל-Google Workspace. כדי להעביר אפליקציה ל-Chat שלא מוגדרת כתוסף, אפשר לעיין במאמר בנושא המרת אפליקציה ל-Google Chat לתוסף ל-Google Workspace.

הפעלת בקשה לתיבת דו-שיח

בדוגמת הקוד הבאה אפשר לראות איך אפליקציית Chat שהיא לא תוסף מפעילה בקשה לתיבת דו-שיח מכפתור בהודעה בכרטיס. כדי לפתוח את תיבת הדו-שיח, השדה button.interaction מוגדר ל-OPEN_DIALOG:

Node.js

node/contact-form-app/index.js
buttonList: { buttons: [{
  text: "Add Contact",
  onClick: { action: {
    function: "openInitialDialog",
    interaction: "OPEN_DIALOG"
  }}
}]}

Python

python/contact-form-app/main.py
'buttonList': { 'buttons': [{
  'text': "Add Contact",
  'onClick': { 'action': {
    'function': "openInitialDialog",
    'interaction': "OPEN_DIALOG"
  }}
}]}

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
.setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()
  .setText("Add Contact")
  .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
    .setFunction("openInitialDialog")
    .setInteraction("OPEN_DIALOG"))))))));

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/contact-form-app/main.gs
buttonList: { buttons: [{
  text: "Add Contact",
  onClick: { action: {
    function: "openInitialDialog",
    interaction: "OPEN_DIALOG"
  }}
}]}

פתיחת תיבת הדו-שיח הראשונית

כשמשתמש מפעיל בקשה לתיבת דו-שיח, אפליקציית Chat שהיא לא תוסף מקבלת אירוע אינטראקציה, שמיוצג כסוג event ב-Chat API. אם האינטראקציה מפעילה בקשה לתיבת דו-שיח, הערך בשדה dialogEventType של האירוע מוגדר ל-REQUEST_DIALOG.

כדי לפתוח תיבת דו-שיח, אפליקציית Chat שהיא לא תוסף יכולה להגיב לבקשה על ידי החזרת אובייקט actionResponse עם type: "DIALOG" ואובייקט Message. כדי לציין את התוכן של תיבת הדו-שיח, צריך לכלול את האובייקטים הבאים:

  • אובייקט actionResponse, עם type שמוגדר ל-DIALOG.
  • אובייקט dialogAction. השדה body מכיל את רכיבי ממשק המשתמש שיוצגו בכרטיס, כולל אחד או יותר sections של ווידג'טים. כדי לאסוף מידע מהמשתמשים, אפשר לציין ווידג'טים של שדות קלט בטופס וגם ווידג'ט של לחצן.

בדוגמת הקוד הבאה מוצג איך אפליקציית Chat שלא מוגדרת כתוסף מחזירה תגובה שפותחת תיבת דו-שיח:

Node.js

node/contact-form-app/index.js
/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} a message with an action response to open a dialog.
 */
function openInitialDialog() {
  return { actionResponse: {
    type: "DIALOG",
    dialogAction: { dialog: { body: { sections: [{
      header: "Add new contact",
      widgets: CONTACT_FORM_WIDGETS.concat([{
        buttonList: { buttons: [{
          text: "Review and submit",
          onClick: { action: { function: "openConfirmation" }}
        }]}
      }])
    }]}}}
  }};
}

Python

python/contact-form-app/main.py
def open_initial_dialog() -> dict:
  """Opens the initial step of the dialog that lets users add contact details."""
  return { 'actionResponse': {
    'type': "DIALOG",
    'dialogAction': { 'dialog': { 'body': { 'sections': [{
      'header': "Add new contact",
      'widgets': CONTACT_FORM_WIDGETS + [{
        'buttonList': { 'buttons': [{
          'text': "Review and submit",
          'onClick': { 'action': { 'function': "openConfirmation" }}
        }]}
      }]
    }]}}}
  }}

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
// Opens the initial step of the dialog that lets users add contact details.
Message openInitialDialog() {
  return new Message().setActionResponse(new ActionResponse()
    .setType("DIALOG")
    .setDialogAction(new DialogAction().setDialog(new Dialog().setBody(new GoogleAppsCardV1Card()
      .setSections(List.of(new GoogleAppsCardV1Section()
        .setHeader("Add new contact")
        .setWidgets(Stream.concat(
          CONTACT_FORM_WIDGETS.stream(),
          List.of(new GoogleAppsCardV1Widget()
            .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()
            .setText("Review and submit")
            .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
              .setFunction("openConfirmation"))))))).stream()).collect(Collectors.toList()))))))));
}

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/contact-form-app/main.gs
/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} a message with an action response to open a dialog.
 */
function openInitialDialog() {
  return { actionResponse: {
    type: "DIALOG",
    dialogAction: { dialog: { body: { sections: [{
      header: "Add new contact",
      widgets: CONTACT_FORM_WIDGETS.concat([{
        buttonList: { buttons: [{
          text: "Review and submit",
          onClick: { action: { function: "openConfirmation" }}
        }]}
      }])
    }]}}}
  }};
}

טיפול בשליחת תיבת הדו-שיח

כשמשתמשים לוחצים על לחצן לשליחת תיבת דו-שיח, אפליקציית צ'אט שאינה תוסף מקבלת אירוע אינטראקציה מסוג CARD_CLICKED שבו הערך של dialogEventType הוא SUBMIT_DIALOG.

אופציונלי: חזרה לתיבת דו-שיח אחרת

כדי לעבד את הנתונים שהמשתמשים מזינים באפליקציית Chat שהיא לא תוסף, אפליקציית Chat משתמשת באובייקט event.common.formInputs.

בדוגמה הזו, אפליקציית Chat שהיא לא תוסף פותחת תיבת דו-שיח ראשונית שמובילה לתיבת דו-שיח שנייה לאישור לפני השליחה:

Node.js

node/contact-form-app/index.js
/**
 * Responds to CARD_CLICKED interaction events in Google Chat.
 *
 * @param {Object} event the CARD_CLICKED interaction event from Google Chat.
 * @return {Object} message responses specific to the dialog handling.
 */
function onCardClick(event) {
  // Initial dialog form page
  if (event.common.invokedFunction === "openInitialDialog") {
    return openInitialDialog();
  // Confirmation dialog form page
  } else if (event.common.invokedFunction === "openConfirmation") {
    return openConfirmation(event);
  // Submission dialog form page
  } else if (event.common.invokedFunction === "submitForm") {
    return submitForm(event);
  }
}

/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} a message with an action response to open a dialog.
 */
function openInitialDialog() {
  return { actionResponse: {
    type: "DIALOG",
    dialogAction: { dialog: { body: { sections: [{
      header: "Add new contact",
      widgets: CONTACT_FORM_WIDGETS.concat([{
        buttonList: { buttons: [{
          text: "Review and submit",
          onClick: { action: { function: "openConfirmation" }}
        }]}
      }])
    }]}}}
  }};
}

/**
 * Returns the second step as a dialog or card message that lets users confirm details.
 *
 * @param {Object} event the interactive event with form inputs.
 * @return {Object} returns a dialog or private card message.
 */
function openConfirmation(event) {
  const name = fetchFormValue(event, "contactName") ?? "";
  const birthdate = fetchFormValue(event, "contactBirthdate") ?? "";
  const type = fetchFormValue(event, "contactType") ?? "";
  const cardConfirmation = {
    header: "Your contact",
    widgets: [{
      textParagraph: { text: "Confirm contact information and submit:" }}, {
      textParagraph: { text: "<b>Name:</b> " + name }}, {
      textParagraph: {
        text: "<b>Birthday:</b> " + convertMillisToDateString(birthdate)
      }}, {
      textParagraph: { text: "<b>Type:</b> " + type }}, {
      buttonList: { buttons: [{
        text: "Submit",
        onClick: { action: {
          function: "submitForm",
          parameters: [{
            key: "contactName", value: name }, {
            key: "contactBirthdate", value: birthdate }, {
            key: "contactType", value: type
          }]
        }}
      }]}
    }]
  };

  // Returns a dialog with contact information that the user input.
  if (event.isDialogEvent) {
    return { action_response: {
      type: "DIALOG",
      dialogAction: { dialog: { body: { sections: [ cardConfirmation ]}}}
    }};
  }

  // Updates existing card message with contact information that the user input.
  return {
    actionResponse: { type: "UPDATE_MESSAGE" },
    privateMessageViewer: event.user,
    cardsV2: [{
      card: { sections: [cardConfirmation]}
    }]
  }
}

Python

python/contact-form-app/main.py
def on_card_click(event: dict) -> dict:
  """Responds to CARD_CLICKED interaction events in Google Chat."""
  # Initial dialog form page
  if "openInitialDialog" == event.get('common').get('invokedFunction'):
    return open_initial_dialog()
  # Confirmation dialog form page
  elif "openConfirmation" == event.get('common').get('invokedFunction'):
    return open_confirmation(event)
  # Submission dialog form page
  elif "submitForm" == event.get('common').get('invokedFunction'):
    return submit_form(event)


def open_initial_dialog() -> dict:
  """Opens the initial step of the dialog that lets users add contact details."""
  return { 'actionResponse': {
    'type': "DIALOG",
    'dialogAction': { 'dialog': { 'body': { 'sections': [{
      'header': "Add new contact",
      'widgets': CONTACT_FORM_WIDGETS + [{
        'buttonList': { 'buttons': [{
          'text': "Review and submit",
          'onClick': { 'action': { 'function': "openConfirmation" }}
        }]}
      }]
    }]}}}
  }}


def open_confirmation(event: dict) -> dict:
  """Returns the second step as a dialog or card message that lets users confirm details."""
  name = fetch_form_value(event, "contactName") or ""
  birthdate = fetch_form_value(event, "contactBirthdate") or ""
  type = fetch_form_value(event, "contactType") or ""
  card_confirmation = {
    'header': "Your contact",
    'widgets': [{
      'textParagraph': { 'text': "Confirm contact information and submit:" }}, {
      'textParagraph': { 'text': "<b>Name:</b> " + name }}, {
      'textParagraph': {
        'text': "<b>Birthday:</b> " + convert_millis_to_date_string(birthdate)
      }}, {
      'textParagraph': { 'text': "<b>Type:</b> " + type }}, {
      'buttonList': { 'buttons': [{
        'text': "Submit",
        'onClick': { 'action': {
          'function': "submitForm",
          'parameters': [{
            'key': "contactName", 'value': name }, {
            'key': "contactBirthdate", 'value': birthdate }, {
            'key': "contactType", 'value': type
          }]
        }}
      }]}
    }]
  }

  # Returns a dialog with contact information that the user input.
  if event.get('isDialogEvent'): 
    return { 'action_response': {
      'type': "DIALOG",
      'dialogAction': { 'dialog': { 'body': { 'sections': [card_confirmation] }}}
    }}

  # Updates existing card message with contact information that the user input.
  return {
    'actionResponse': { 'type': "UPDATE_MESSAGE" },
    'privateMessageViewer': event.get('user'),
    'cardsV2': [{
      'card': { 'sections': [card_confirmation] }
    }]
  }

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
// Responds to CARD_CLICKED interaction events in Google Chat.
Message onCardClick(JsonNode event) {
  String invokedFunction = event.at("/common/invokedFunction").asText();
  // Initial dialog form page
  if ("openInitialDialog".equals(invokedFunction)) {
    return openInitialDialog();
  // Confirmation dialog form page
  } else if ("openConfirmation".equals(invokedFunction)) {
    return openConfirmation(event);
  // Submission dialog form page
  } else if ("submitForm".equals(invokedFunction)) {
    return submitForm(event);
  }
  return null; 
}

// Opens the initial step of the dialog that lets users add contact details.
Message openInitialDialog() {
  return new Message().setActionResponse(new ActionResponse()
    .setType("DIALOG")
    .setDialogAction(new DialogAction().setDialog(new Dialog().setBody(new GoogleAppsCardV1Card()
      .setSections(List.of(new GoogleAppsCardV1Section()
        .setHeader("Add new contact")
        .setWidgets(Stream.concat(
          CONTACT_FORM_WIDGETS.stream(),
          List.of(new GoogleAppsCardV1Widget()
            .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()
            .setText("Review and submit")
            .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()
              .setFunction("openConfirmation"))))))).stream()).collect(Collectors.toList()))))))));
}

// Returns the second step as a dialog or card message that lets users confirm details.
Message openConfirmation(JsonNode event) {
  String name = fetchFormValue(event, "contactName") != null ?
    fetchFormValue(event, "contactName") : "";
  String birthdate = fetchFormValue(event, "contactBirthdate") != null ?
    fetchFormValue(event, "contactBirthdate") : "";
  String type = fetchFormValue(event, "contactType") != null ?
    fetchFormValue(event, "contactType") : "";
  GoogleAppsCardV1Section cardConfirmationSection = new GoogleAppsCardV1Section()
    .setHeader("Your contact")
    .setWidgets(List.of(
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("Confirm contact information and submit:")),
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("<b>Name:</b> " + name)),
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("<b>Birthday:</b> " + convertMillisToDateString(birthdate))),
      new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()
        .setText("<b>Type:</b> " + type)),
      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))))))))));

  // Returns a dialog with contact information that the user input.
  if (event.at("/isDialogEvent") != null && event.at("/isDialogEvent").asBoolean()) {
    return new Message().setActionResponse(new ActionResponse()
      .setType("DIALOG")
      .setDialogAction(new DialogAction().setDialog(new Dialog().setBody(new GoogleAppsCardV1Card()
        .setSections(List.of(cardConfirmationSection))))));
  }

  // Updates existing card message with contact information that the user input.
  return new Message()
    .setActionResponse(new ActionResponse()
      .setType("UPDATE_MESSAGE"))
    .setPrivateMessageViewer(new User().setName(event.at("/user/name").asText()))
    .setCardsV2(List.of(new CardWithId().setCard(new GoogleAppsCardV1Card()
      .setSections(List.of(cardConfirmationSection)))));
}

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/contact-form-app/main.gs
/**
 * Responds to CARD_CLICKED interaction events in Google Chat.
 *
 * @param {Object} event the CARD_CLICKED interaction event from Google Chat.
 * @return {Object} message responses specific to the dialog handling.
 */
function onCardClick(event) {
  // Initial dialog form page
  if (event.common.invokedFunction === "openInitialDialog") {
    return openInitialDialog();
  // Confirmation dialog form page
  } else if (event.common.invokedFunction === "openConfirmation") {
    return openConfirmation(event);
  // Submission dialog form page
  } else if (event.common.invokedFunction === "submitForm") {
    return submitForm(event);
  }
}

/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} a message with an action response to open a dialog.
 */
function openInitialDialog() {
  return { actionResponse: {
    type: "DIALOG",
    dialogAction: { dialog: { body: { sections: [{
      header: "Add new contact",
      widgets: CONTACT_FORM_WIDGETS.concat([{
        buttonList: { buttons: [{
          text: "Review and submit",
          onClick: { action: { function: "openConfirmation" }}
        }]}
      }])
    }]}}}
  }};
}

/**
 * Returns the second step as a dialog or card message that lets users confirm details.
 *
 * @param {Object} event the interactive event with form inputs.
 * @return {Object} returns a dialog or private card message.
 */
function openConfirmation(event) {
  const name = fetchFormValue(event, "contactName") ?? "";
  const birthdate = fetchFormValue(event, "contactBirthdate") ?? "";
  const type = fetchFormValue(event, "contactType") ?? "";
  const cardConfirmation = {
    header: "Your contact",
    widgets: [{
      textParagraph: { text: "Confirm contact information and submit:" }}, {
      textParagraph: { text: "<b>Name:</b> " + name }}, {
      textParagraph: {
        text: "<b>Birthday:</b> " + convertMillisToDateString(birthdate)
      }}, {
      textParagraph: { text: "<b>Type:</b> " + type }}, {
      buttonList: { buttons: [{
        text: "Submit",
        onClick: { action: {
          function: "submitForm",
          parameters: [{
            key: "contactName", value: name }, {
            key: "contactBirthdate", value: birthdate }, {
            key: "contactType", value: type
          }]
        }}
      }]}
    }]
  };

  // Returns a dialog with contact information that the user input.
  if (event.isDialogEvent) {
    return { action_response: {
      type: "DIALOG",
      dialogAction: { dialog: { body: { sections: [ cardConfirmation ]}}}
    }};
  }

  // Updates existing card message with contact information that the user input.
  return {
    actionResponse: { type: "UPDATE_MESSAGE" },
    privateMessageViewer: event.user,
    cardsV2: [{
      card: { sections: [cardConfirmation]}
    }]
  }
}

סגירה של תיבת הדו-שיח

כשמשתמשים לוחצים על לחצן בתיבת דו-שיח, אפליקציית Chat שלא מוגדרת כתוסף מבצעת את הפעולה שמשויכת ללחצן ומספקת את אובייקט האירוע עם המידע הבא:

אפליקציית Chat שהיא לא תוסף צריכה להחזיר אובייקט ActionResponse עם הערך DIALOG בהגדרה type, ועם הערך dialogAction. אם הפעולה לא נכשלה, הערך של dialogAction.actionStatus צריך להיות OK, כמו בדוגמה הבאה:

Node.js

node/contact-form-app/index.js
// The Chat app indicates that it received form data from the dialog or card.
// Sends private text message that confirms submission.
const confirmationMessage = "✅ " + contactName + " has been added to your contacts.";
if (event.dialogEventType === "SUBMIT_DIALOG") {
  return {
    actionResponse: {
      type: "DIALOG",
      dialogAction: { actionStatus: {
        statusCode: "OK",
        userFacingMessage: "Success " + contactName
      }}
    }
  };
}

Python

python/contact-form-app/main.py
# The Chat app indicates that it received form data from the dialog or card.
# Sends private text message that confirms submission.
confirmation_message = "✅ " + contact_name + " has been added to your contacts.";
if "SUBMIT_DIALOG" == event.get('dialogEventType'):
  return {
    'actionResponse': {
      'type': "DIALOG",
      'dialogAction': { 'actionStatus': {
        'statusCode': "OK",
        'userFacingMessage': "Success " + contact_name
      }}
    }
  }

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
// The Chat app indicates that it received form data from the dialog or card.
// Sends private text message that confirms submission.
String confirmationMessage = "✅ " + contactName + " has been added to your contacts.";
if (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("OK")
      .setUserFacingMessage("Success " + contactName))));
}

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/contact-form-app/main.gs
// The Chat app indicates that it received form data from the dialog or card.
// Sends private text message that confirms submission.
const confirmationMessage = "✅ " + contactName + " has been added to your contacts.";
if (event.dialogEventType === "SUBMIT_DIALOG") {
  return {
    actionResponse: {
      type: "DIALOG",
      dialogAction: { actionStatus: {
        statusCode: "OK",
        userFacingMessage: "Success " + contactName
      }}
    }
  };
}
אופציונלי: הצגת התראה זמנית

אפליקציית Chat שהיא לא תוסף יכולה להחזיר ActionResponse עם הערך actionStatus כדי להשיב עם הודעת הצלחה או שגיאה.

בדוגמה הבאה נבדק אם הפרמטרים תקפים, ואם הם לא תקפים, תיבת הדו-שיח נסגרת ומוצגת הודעת טקסט:

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

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של 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
    }}
  }};
}
אופציונלי: שליחת הודעת צ'אט

כדי לשלוח הודעה חדשה כשסוגרים תיבת דו-שיח באפליקציית צ'אט שהיא לא תוסף, מחזירים אובייקט ActionResponse עם type: "NEW_MESSAGE". בדוגמה הבאה, תיבת הדו-שיח נסגרת עם הודעת טקסט לאישור:

Node.js

node/contact-form-app/index.js
return {
  actionResponse: { type: "NEW_MESSAGE" },
  privateMessageViewer: event.user,
  text: confirmationMessage
};

Python

python/contact-form-app/main.py
return {
  'actionResponse': { 'type': "NEW_MESSAGE" },
  'privateMessageViewer': event.get('user'),
  'text': confirmation_message
}

Java

java/contact-form-app/src/main/java/com/google/chat/contact/App.java
return new Message()
  .setActionResponse(new ActionResponse().setType("NEW_MESSAGE"))
  .setPrivateMessageViewer(new User().setName(event.at("/user/name").asText()))
  .setText(confirmationMessage);

Apps Script

בדוגמה הזו, הודעת הכרטיס נשלחת על ידי החזרת JSON של הכרטיס. אפשר גם להשתמש בשירות הכרטיסים של Apps Script.

apps-script/contact-form-app/main.gs
return {
  actionResponse: { type: "NEW_MESSAGE" },
  privateMessageViewer: event.user,
  text: confirmationMessage
};

כדי לעדכן הודעה, מחזירים אובייקט actionResponse שמכיל את ההודעה המעודכנת ומגדירים את type לאחת מהאפשרויות הבאות: