איסוף ועיבוד מידע ממשתמשים ב-Google Chat

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

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

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

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

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

HTTP

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

Apps Script

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

יצירת טפסים באמצעות כרטיסים

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

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

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

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

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

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

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

הוספת תפריט נפתח

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

אפשר לאכלס פריטים בתפריט נפתח ממקורות הנתונים הבאים:

איכלוס פריטים ממקור נתונים של Google Workspace

כדי לאכלס פריטים ממקורות נתונים של Google Workspace, כמו משתמשי Google Workspace, צריך לציין את השדה platformDataSource באובייקט DataSourceConfig. בניגוד לסוגים אחרים של קלט לבחירה, לא צריך להוסיף אובייקטים של SelectionItem, כי פריטי הבחירה האלה נלקחים באופן דינמי מ-Google Workspace.

בקטע הקוד הבא מוצג תפריט נפתח של משתמשי Google Workspace:

JSON

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

איכלוס פריטים ממקור נתונים חיצוני

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

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

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

JSON

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

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

הוספת תפריט בחירה מרובה

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

אפשר לאכלס פריטים בתפריט בחירה מרובה מתוך מקורות הנתונים הבאים:

  • נתוני Google Workspace, כולל משתמשים או מרחבים ב-Chat שהמשתמש חבר בהם. התפריט כולל רק פריטים מאותו ארגון ב-Google Workspace.
  • מקורות נתונים חיצוניים, כמו מסד נתונים רלציוני. לדוגמה, אפשר להשתמש בתפריטים עם אפשרות לבחירה מרובה כדי לעזור למשתמש לבחור מתוך רשימה של לידים למכירות ממערכת לניהול קשרי לקוחות (CRM).

איכלוס פריטים ממקור נתונים של Google Workspace

כדי להשתמש במקורות נתונים של Google Workspace, צריך לציין את השדה platformDataSource בווידג'ט SelectionInput. בניגוד לסוגים אחרים של קלט לבחירה, לא צריך להוסיף אובייקטים של SelectionItem, כי פריטי הבחירה האלה נלקחים באופן דינמי מ-Google Workspace.

בדוגמה הבאה אפשר לראות תפריט בחירה מרובה של משתמשי Google Workspace. כדי לאכלס משתמשים, קלט הבחירה מגדיר את commonDataSource ל-USER:

JSON

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

בדוגמה הבאה מוצג תפריט בחירה מרובה של מרחבים ב-Chat. כדי לאכלס את המרחבים, קלט הבחירה מציין את השדה hostAppDataSource. בתפריט לבחירה מרובה מוגדר גם defaultToCurrentSpace ל-true, כך שהמרחב הנוכחי הוא ברירת המחדל בתפריט:

JSON

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

איכלוס פריטים ממקור נתונים חיצוני

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

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

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

Node.js

node/chat/selection-input/index.js
selectionInput: {
  name: "contacts",
  type: "MULTI_SELECT",
  label: "Selected contacts",
  multiSelectMaxSelectedItems: 3,
  multiSelectMinQueryLength: 1,
  externalDataSource: { function: FUNCTION_URL },
  // Suggested items loaded by default.
  // The list is static here but it could be dynamic.
  items: [getSuggestedContact("3")]
}

מחליפים את FUNCTION_URL בנקודת הקצה של HTTP ששולחת שאילתה למקור הנתונים החיצוני.

Python

python/chat/selection-input/main.py
'selectionInput': {
  'name': "contacts",
  'type': "MULTI_SELECT",
  'label': "Selected contacts",
  'multiSelectMaxSelectedItems': 3,
  'multiSelectMinQueryLength': 1,
  'externalDataSource': { 'function': FUNCTION_URL },
  # Suggested items loaded by default.
  # The list is static here but it could be dynamic.
  'items': [get_suggested_contact("3")]
}

מחליפים את FUNCTION_URL בנקודת הקצה של HTTP ששולחת שאילתה למקור הנתונים החיצוני.

Java

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

מחליפים את FUNCTION_URL בנקודת הקצה של HTTP ששולחת שאילתה למקור הנתונים החיצוני.

Apps Script

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

apps-script/chat/selection-input/selection-input.gs
selectionInput: {
  name: "contacts",
  type: "MULTI_SELECT",
  label: "Selected contacts",
  multiSelectMaxSelectedItems: 3,
  multiSelectMinQueryLength: 1,
  externalDataSource: { function: "queryContacts" },
  // Suggested items loaded by default.
  // The list is static here but it could be dynamic.
  items: [getSuggestedContact("3")]
}

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

קבלת נתונים מווידג'טים אינטראקטיביים

בכל פעם שמשתמשים לוחצים על לחצן, הפעולה של אפליקציות Chat מופעלת עם מידע על האינטראקציה. באובייקט formInputs במטען הייעודי (payload) של האירוע commonEventObject, מופיעים כל הערכים שהמשתמש מזין.

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

בדוגמה הבאה מוצג חלק מאובייקט אירוע שבו משתמש הזין ערכים לכל ווידג'ט:

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

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

ווידג'ט של קלט בטופס סוג נתוני הקלט ערך קלט מאובייקט האירוע ערך לדוגמה
textInput stringInputs event.commonEventObject.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs כדי לקבל את הערך הראשון או היחיד, event.commonEventObject.formInputs.contactType.stringInputs.value[0] Personal
‫dateTimePicker שמקבל רק תאריכים. dateInput event.commonEventObject.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

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

הצעת פריטים לבחירה

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

כדי להציע פריטים ולמלא אותם באופן דינמי בשדה קלט לבחירה, בווידג'ט SelectionInput בכרטיס צריך לציין פונקציה שמבצעת שאילתה במקור הנתונים החיצוני. בתפריטים עם אפשרות לבחירת כמה פריטים, מציינים את השדה externalDataSource. בתפריטים נפתחים, מציינים את השדה remoteDataSource באובייקט DataSourceConfig.

אפשר גם להגדיר את מספר התווים שהמשתמש מקליד לפני שהתפריט מציג הצעות. בתפריטים עם אפשרות לבחירה מרובה, מגדירים את השדה multiSelectMinQueryLength. בתפריטים נפתחים, מגדירים את השדה min_characters_trigger בתוך DataSourceConfig.

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

  1. טיפול באובייקט אירוע שאפליקציית Chat מקבלת כשמשתמשים מקלידים בתפריט.
  2. מתוך אובייקט האירוע, מקבלים את הערך שהמשתמש הקליד, שמיוצג בשדה event.commonEventObject.parameters["autocomplete_widget_query"].
  3. שליחת שאילתה למקור הנתונים באמצעות ערך קלט של משתמשים כדי לקבל SelectionItems אחת או יותר להצעה למשתמש.
  4. כדי להחזיר פריטים מוצעים, מחזירים את הפעולה RenderActions עם אובייקט modifyCard.

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

Node.js

node/chat/selection-input/index.js
/**
 * Web app that responds to events sent from a Google Chat space.
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
app.post('/', async (req, res) => {
  // Stores the Google Chat event
  const chatEvent = req.body.chat;

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

  // Replies with a card that contains the multiselect menu.
  return res.json({ hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    cardsV2: [{
      cardId: "contactSelector",
      card: { sections:[{ widgets: [{
        selectionInput: {
          name: "contacts",
          type: "MULTI_SELECT",
          label: "Selected contacts",
          multiSelectMaxSelectedItems: 3,
          multiSelectMinQueryLength: 1,
          externalDataSource: { function: FUNCTION_URL },
          // Suggested items loaded by default.
          // The list is static here but it could be dynamic.
          items: [getSuggestedContact("3")]
        }
      }]}]}
    }]
  }}}}});
});

/**
 * Get contact suggestions based on text typed by users.
 *
 * @param {Object} event the event object that contains the user's query
 * @return {Object} suggestions
 */
function queryContacts(event) {
  const query = event.commonEventObject.parameters["autocomplete_widget_query"];
  return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [
    // The list is static here but it could be dynamic.
    getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("5")
  // Only return items based on the query from the user.
  ].filter(e => !query || e.text.includes(query)) }}}]}};
}

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

מחליפים את FUNCTION_URL בנקודת הקצה של HTTP ששולחת שאילתה למקור הנתונים החיצוני.

Python

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

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

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

  # Replies with a card that contains the multiselect menu.
  return json.jsonify({ 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': {
    'message': { 'cardsV2': [{
      'cardId': "contactSelector",
      'card': { 'sections':[{ 'widgets': [{
        'selectionInput': {
          'name': "contacts",
          'type': "MULTI_SELECT",
          'label': "Selected contacts",
          'multiSelectMaxSelectedItems': 3,
          'multiSelectMinQueryLength': 1,
          'externalDataSource': { 'function': FUNCTION_URL },
          # Suggested items loaded by default.
          # The list is static here but it could be dynamic.
          'items': [get_suggested_contact("3")]
        }
      }]}]}
    }]}
  }}}})


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

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

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


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

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

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

מחליפים את FUNCTION_URL בנקודת הקצה של HTTP ששולחת שאילתה למקור הנתונים החיצוני.

Java

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

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

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

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

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

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

  /**
   * Get contact suggestions based on text typed by users.
   *
   * @param event the event object that contains the user's query.
   * @return The response with contact suggestions.
   */
  GenericJson queryContacts(JsonNode event) throws Exception {
    String query = event.at("/commonEventObject/parameters/autocomplete_widget_query").asText();
    List<GoogleAppsCardV1SelectionItem> suggestions = List.of(
      // The list is static here but it could be dynamic.
      getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("5")
    // Only return items based on the query from the user
    ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList();

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

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

מחליפים את FUNCTION_URL בנקודת הקצה של HTTP ששולחת שאילתה למקור הנתונים החיצוני.

Apps Script

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

apps-script/chat/selection-input/selection-input.gs
/**
* Responds to a Message trigger in Google Chat.
*
* @param {Object} event the event object from Google Chat
* @return {Object} Response from the Chat app.
*/
function onMessage(event) {
  // Replies with a card that contains the multiselect menu.
  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    cardsV2: [{
      cardId: "contactSelector",
      card: { sections:[{ widgets: [{
        selectionInput: {
          name: "contacts",
          type: "MULTI_SELECT",
          label: "Selected contacts",
          multiSelectMaxSelectedItems: 3,
          multiSelectMinQueryLength: 1,
          externalDataSource: { function: "queryContacts" },
          // Suggested items loaded by default.
          // The list is static here but it could be dynamic.
          items: [getSuggestedContact("3")]
        }
      }]}]}
    }]
  }}}}};
}

/**
* Get contact suggestions based on text typed by users.
*
* @param {Object} event the event object that contains the user's query
* @return {Object} suggestions
*/
function queryContacts(event) {
  const query = event.commonEventObject.parameters["autocomplete_widget_query"];
  return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [
    // The list is static here but it could be dynamic.
    getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("5")
  // Only return items based on the query from the user.
  ].filter(e => !query || e.text.includes(query)) }}}]}};
}

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

העברת נתונים לכרטיס אחר

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

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

כדי להעביר את קלט הנתונים מהכרטיס הראשוני, אפשר ליצור את הווידג'ט button עם actionParameters שמכיל את name של הווידג'ט ואת הערך שהמשתמש מזין, כמו בדוגמה הבאה:

Node.js

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

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

Python

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

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

Java

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

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

Apps Script

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

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

כשמשתמש לוחץ על הלחצן, אפליקציית הצ'אט שלכם מקבלת אובייקט אירוע שממנו אפשר לקבל נתונים.

איך מגיבים לתשובות שנשלחו בטופס

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

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

Node.js

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

Python

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

Java

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

Apps Script

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

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

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

פתרון בעיות

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

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

באינטראקציה עם תיבת דו-שיח, אם מופיעים יומני שגיאות עם ההודעה Unspecified error invoking the add-on.‎ וקוד 13, בדרך כלל מדובר בשגיאה פנימית או בנקודת קצה של אפליקציית Chat מסוג HTTP שלא הצליחה לעבד את הבקשה או להחזיר תגובה תקינה.

כדי לפתור את השגיאה:

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

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

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

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

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

יצירת טפסים באמצעות כרטיסים

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

Node.js

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

Python

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

Java

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

Apps Script

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

קבלת נתונים מווידג'טים אינטראקטיביים

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

  • אם הכפתור נמצא בהודעה או בתיבת דו-שיח, אפליקציות ל-Chat שהן לא תוספים מקבלות CARD_CLICKED אירוע אינטראקציה שמכיל מידע על האינטראקציה. המטען הייעודי (payload) של אירועי האינטראקציה CARD_CLICKED מכיל אובייקט common.formInputs (event.common.formInputs) עם כל הערכים שהמשתמש מזין.

    אפשר לאחזר את הערכים מהאובייקט common.formInputs.WIDGET_NAME, כאשר WIDGET_NAME הוא השדה name שציינתם בווידג'ט. הערכים מוחזרים כסוג נתונים ספציפי לווידג'ט (מיוצג כאובייקט Inputs).

    הדוגמה הבאה מציגה חלק מCARD_CLICKEDאירוע אינטראקציה שבו משתמש הזין ערכים לכל ווידג'ט:

    HTTP

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

    Apps Script

    {
      "type": "CARD_CLICKED",
      "common": { "formInputs": {
        "contactName": { "": { "stringInputs": {
          "value": ["Kai 0"]
        }}},
        "contactBirthdate": { "": { "dateInput": {
          "msSinceEpoch": 1000425600000
        }}},
          "contactType": { "": { "stringInputs": {
          "value": ["Personal"]
        }}}
      }}
    }
    
  • אם הלחצן נמצא בדף הבית, אפליקציות ל-Chat שאינן תוספים מקבלות אירוע אינטראקציה SUBMIT_FORM. המטען הייעודי (payload) של אירוע האינטראקציה מכיל אובייקט commonEventObject.formInputs (event.commonEventObject.formInputs) עם כל הערכים שהמשתמש מזין.

    אפשר לאחזר את הערכים מהאובייקט commonEventObject.formInputs.WIDGET_NAME, כאשר WIDGET_NAME הוא השדה name שציינתם בווידג'ט. הערכים מוחזרים כסוג נתונים ספציפי לווידג'ט (מיוצג כאובייקט Inputs).

    הדוגמה הבאה מציגה חלק מSUBMIT_FORMאירוע אינטראקציה שבו משתמש הזין ערכים לכל ווידג'ט:

    HTTP

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

    Apps Script

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

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

ווידג'ט של קלט בטופס סוג נתוני הקלט ערך קלט מאירוע האינטראקציה ערך לדוגמה
textInput stringInputs event.common.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs כדי לקבל את הערך הראשון או היחיד, event.common.formInputs.contactType.stringInputs.value[0] Personal
‫dateTimePicker שמקבל רק תאריכים. dateInput event.common.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

העברת נתונים לכרטיס אחר

כדי להעביר את קלט הנתונים מהכרטיס הראשוני באפליקציית Chat שהיא לא תוסף, צריך ליצור את הווידג'ט button עם actionParameters שמכיל את name של הווידג'ט ואת הערך שהמשתמש מזין, כמו בדוגמה הבאה:

Node.js

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

Python

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

Java

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

Apps Script

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

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

איך מגיבים לתשובות שנשלחו בטופס

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

Node.js

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

Python

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

Java

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

Apps Script

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

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