إضافة عناصر واجهة مستخدم تفاعلية إلى البطاقات

توضّح هذه الصفحة كيفية إضافة التطبيقات المصغّرة وعناصر واجهة المستخدم إلى البطاقات ليتمكّن المستخدمون من التفاعل مع تطبيق Google Chat، مثلاً من خلال النقر على زر أو إرسال معلومات.

يمكن لتطبيقات Chat استخدام واجهات Chat التالية لإنشاء بطاقات تفاعلية:

  • الرسائل التي تحتوي على بطاقة واحدة أو أكثر
  • الصفحات الرئيسية، وهي بطاقة تظهر من علامة التبويب الصفحة الرئيسية في الرسائل المباشرة مع تطبيق Chat.
  • مربّعات الحوار، وهي بطاقات يتم فتحها في نافذة جديدة من الرسائل والصفحات الرئيسية

عندما يتفاعل المستخدمون مع البطاقات، يمكن لتطبيقات Chat استخدام البيانات التي تتلقّاها لمعالجتها والردّ وفقًا لذلك. لمعرفة التفاصيل، يُرجى الاطّلاع على مقالة جمع المعلومات من مستخدمي Google Chat ومعالجتها.


استخدِم "أداة إنشاء البطاقات" لتصميم ومعاينة واجهات المستخدم والرسائل لتطبيقات Chat:

فتح "أداة إنشاء البطاقات"

المتطلبات الأساسية

تطبيق Google Chat تم ضبطه لتلقّي تفاعلات المستخدمين والردّ عليها لإنشاء تطبيق Chat تفاعلي، أكمِل أحد أدلة البدء السريع التالية استنادًا إلى بنية التطبيق التي تريد استخدامها:

إضافة زر

تعرض أداة ButtonList مجموعة من الأزرار. يمكن أن تعرض الأزرار نصًا أو رمزًا أو كلاً من النص والرمز. يتيح كل Button إجراء OnClick يحدث عندما ينقر المستخدمون على الزر. على سبيل المثال:

  • افتح رابطًا تشعبيًا باستخدام OpenLink لتزويد المستخدمين بمعلومات إضافية.
  • تشغيل action يُشغِّل دالة مخصّصة، مثل طلب بيانات من واجهة برمجة التطبيقات

لتسهيل الاستخدام، تتيح الأزرار استخدام نص بديل.

إضافة زر يشغّل دالة مخصّصة

في ما يلي بطاقة تتألف من تطبيق مصغّر ButtonList يتضمّن زرَّين. يفتح أحد الزرَّين مستندات مطوّري Google Chat في علامة تبويب جديدة. ويشغّل الزر الآخر دالة مخصّصة باسم goToView() ويمرّر المَعلمة viewType="BIRD EYE VIEW".

إضافة زر بنمط "التصميم المتعدد الأبعاد"

يعرض ما يلي مجموعة من الأزرار بأنماط مختلفة من أزرار التصميم المتعدد الأبعاد.

لتطبيق نمط "التصميم المتعدد الأبعاد"، لا تُدرِج سمة "اللون".

إضافة زر بلون مخصّص وزر غير مفعَّل

يمكنك منع المستخدمين من النقر على زر من خلال ضبط السمة "disabled": "true".

يعرض ما يلي بطاقة تتألف من تطبيق مصغّر ButtonList مع زرَّين. يستخدم أحد الأزرار الحقل Color لتخصيص لون خلفية الزر. يتم إلغاء تفعيل الزر الآخر باستخدام الحقل Disabled، ما يمنع المستخدم من النقر على الزر وتنفيذ الدالة.

إضافة زرّ يتضمّن رمزًا

يعرض ما يلي بطاقة تتألف من تطبيق مصغّر ButtonList مع تطبيقَين مصغّرَين للرمز Button. يستخدم أحد الأزرار الحقل knownIcon لعرض رمز البريد الإلكتروني المضمّن في Google Chat. يستخدم الزر الآخر الحقل iconUrl لعرض أداة رمز مخصّص.

إضافة زر يتضمّن رمزًا ونصًا

يعرض ما يلي بطاقة تتألف من تطبيق مصغّر ButtonList يطلب من المستخدم إرسال رسالة إلكترونية. يعرض الزر الأول رمز بريد إلكتروني ويعرض الزر الثاني نصًا. يمكن للمستخدم النقر على الرمز أو زر النص لتشغيل دالة sendEmail.

تخصيص الزر لقسم قابل للطي

تخصيص زر التحكم الذي يطوي ويوسع الأقسام داخل بطاقة. يمكنك الاختيار من بين مجموعة من الرموز أو الصور لتمثيل محتوى القسم بشكل مرئي، ما يسهّل على المستخدمين فهم المعلومات والتفاعل معها.

إضافة قائمة كاملة

يمكن استخدام Overflow menu في بطاقات Chat لتقديم خيارات وإجراءات إضافية. يتيح لك تضمين المزيد من الخيارات دون تشويش واجهة البطاقة، مما يضمن تصميمًا نظيفًا ومنظمًا.

إضافة قائمة شرائح

توفّر أداة ChipList طريقة متعدّدة الاستخدامات وجذابة من الناحية المرئية لعرض المعلومات. استخدِم قوائم الشرائح لتمثيل العلامات أو الفئات أو البيانات الأخرى ذات الصلة، ما يسهّل على المستخدمين التنقّل في المحتوى والتفاعل معه.

جمع المعلومات من المستخدمين

يوضّح هذا القسم كيفية إضافة التطبيقات المصغّرة التي تجمع المعلومات، مثل النصوص أو الاختيارات.

لمعرفة كيفية معالجة ما يُدخله المستخدمون، يُرجى الاطّلاع على المقالة جمع المعلومات من مستخدمي Google Chat ومعالجتها.

جمع النص

توفّر أداة TextInput حقلاً يمكن للمستخدمين إدخال نص فيه. تتيح هذه الأداة استخدام الاقتراحات التي تساعد المستخدمين في إدخال بيانات موحّدة، كما تتيح استخدام إجراءات عند التغيير، وهي عبارة عن Actions يتم تنفيذها عند حدوث تغيير في حقل إدخال النص، مثل إضافة نص أو حذفه من قِبل المستخدم.

عندما تحتاج إلى جمع بيانات مجردة أو غير معروفة من المستخدمين، استخدم عنصر واجهة المستخدم TextInput هذا. لجمع بيانات محدّدة من المستخدمين، استخدِم تطبيق SelectionInput المصغّر بدلاً من ذلك.

في ما يلي بطاقة تتألف من تطبيق مصغّر TextInput:

جمع التواريخ أو الأوقات

تتيح أداة DateTimePicker للمستخدمين إدخال تاريخ أو وقت أو كليهما. أو يمكن للمستخدمين استخدام أداة الاختيار لتحديد التواريخ والأوقات. إذا أدخل المستخدمون تاريخًا أو وقتًا غير صالح، فستظهر أداة الانتقاء خطأ يطالب المستخدمين بإدخال المعلومات بشكل صحيح.

يعرض ما يلي بطاقة تتألف من ثلاثة أنواع مختلفة من تطبيقات DateTimePicker المصغّرة:

السماح للمستخدمين باختيار العناصر

يوفّر تطبيق SelectionInput المصغّر مجموعة من العناصر القابلة للاختيار، مثل مربّعات الاختيار أو أزرار الاختيار أو مفاتيح التبديل أو قائمة منسدلة. يمكنك استخدام هذا التطبيق المصغّر لجمع بيانات محددة وموحدة من المستخدمين. لجمع بيانات غير محدّدة من المستخدمين، استخدِم تطبيق TextInput المصغّر بدلاً من ذلك.

تتيح أداة SelectionInput استخدام الاقتراحات التي تساعد المستخدمين في إدخال بيانات موحّدة، وإجراءات عند التغيير، وهي عبارة عن Actions يتم تنفيذها عند حدوث تغيير في حقل إدخال الاختيار، مثل اختيار مستخدم لعنصر أو إلغاء اختياره.

يمكن لتطبيقات Chat تلقّي قيمة العناصر المحدّدة ومعالجتها. لمعرفة التفاصيل حول التعامل مع مدخلات النماذج، يُرجى الاطّلاع على معالجة المعلومات التي يُدخلها المستخدمون.

يقدّم هذا القسم أمثلة على البطاقات التي تستخدم تطبيق SelectionInput المصغّر. تستخدم الأمثلة أنواعًا مختلفة من مدخلات الأقسام:

إضافة مربّع اختيار

يعرض ما يلي بطاقة تطلب من المستخدم تحديد ما إذا كانت جهة الاتصال مهنية أو شخصية أو كليهما، باستخدام تطبيق مصغّر SelectionInput يستخدم مربّعات الاختيار:

إضافة زر اختيار

يعرض ما يلي بطاقة تطلب من المستخدم تحديد ما إذا كانت جهة الاتصال مهنية أو شخصية باستخدام تطبيق مصغّر SelectionInput يستخدم أزرار الاختيار:

إضافة مفتاح تحكّم

يعرض ما يلي بطاقة تطلب من المستخدم تحديد ما إذا كانت جهة الاتصال مهنية أو شخصية أو كليهما باستخدام تطبيق مصغّر SelectionInput يستخدم مفاتيح التبديل:

يعرض ما يلي بطاقة تطلب من المستخدم تحديد ما إذا كانت جهة الاتصال مهنية أو شخصية باستخدام تطبيق مصغّر SelectionInput يستخدم قائمة منسدلة:

تعبئة القوائم المنسدلة ديناميكيًا

تتوفّر هذه الميزة لتطبيقات Google Chat.

يمكنك تعبئة العناصر ديناميكيًا لقائمة منسدلة من مصادر البيانات في Google Workspace أو من مصدر بيانات خارجي. لاستخدام مصادر البيانات الديناميكية، عليك تحديد الحقل dataSourceConfigs، وهو مصفوفة من عناصر DataSourceConfig. يمكن أن يحتوي كل DataSourceConfig على platformDataSource أو remoteDataSource. يمكن استخدام DataSourceConfig واحد فقط في الوقت الحالي.

تعبئة العناصر من Google Workspace

لتعبئة العناصر من مصادر بيانات Google Workspace، مثل مستخدمي Google Workspace، عليك تحديد الحقل platformDataSource ضمن DataSourceConfig. على عكس استخدام items الثابتة، يمكنك حذف عناصر SelectionItem، لأنّ عناصر الاختيار هذه يتم الحصول عليها ديناميكيًا من Google Workspace.

يعرض الرمز التالي قائمة منسدلة تملأ مستخدمي Google Workspace:

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "widgets": [
        {
          "selectionInput": {
            "name": "contacts",
            "type": "DROPDOWN",
            "label": "Select contact from organization",
            "dataSourceConfigs": [
              {
                "platformDataSource": {
                  "commonDataSource": "USER"
                },
                "minCharactersTrigger": 1
              }
            ]
          }
        }
      ]
    }
  ]
}
تعبئة العناصر من مصدر بيانات خارجي

لتعبئة العناصر من مصدر بيانات خارجي أو تابع لجهة خارجية، مثل نظام إدارة علاقات العملاء (CRM)، يمكنك استخدام الحقل remoteDataSource ضمن DataSourceConfig لتحديد دالة تعرض العناصر من مصدر البيانات.

يعرض الرمز التالي قائمة منسدلة تملأ العناصر من مجموعة خارجية من جهات الاتصال من خلال تشغيل الدالة getCrmLeads:

JSON

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

لتقليل الطلبات إلى مصدر بيانات ديناميكي، يمكنك تضمين العناصر المقترَحة التي تظهر في القائمة المنسدلة قبل أن يكتب المستخدمون. يمكنك أيضًا ضبط القائمة المنسدلة لإكمال العناصر تلقائيًا استنادًا إلى ما يكتبه المستخدمون من خلال ضبط minCharactersTrigger ضمن DataSourceConfig. عندما يكتب المستخدم عددًا من الأحرف يساوي على الأقل العدد المحدّد في minCharactersTrigger، يتم تفعيل الدالة المحدّدة في remoteDataSource. يتضمّن كائن الحدث الذي تم تمريره إلى الدالة إدخال المستخدم في المفتاح autocomplete_widget_query.

إضافة قائمة متعددة الاختيارات

يعرض ما يلي بطاقة تطلب من المستخدم اختيار جهات اتصال من قائمة اختيار متعدد:

يمكنك تعبئة عناصر قائمة الاختيار المتعدد من مصادر البيانات التالية في Google Workspace:

  • مستخدمو Google Workspace: يمكنك فقط إضافة مستخدمين ضمن مؤسسة Google Workspace نفسها.
  • مساحات Chat: يمكن للمستخدم الذي يُدخل العناصر في قائمة الاختيار المتعدد الاطّلاع على المساحات التي ينتمي إليها واختيارها فقط ضمن مؤسسته على Google Workspace.

لاستخدام مصادر بيانات Google Workspace، عليك تحديد الحقل platformDataSource. على عكس أنواع الإدخال الأخرى، يمكنك حذف عناصر SelectionItem، لأنّ عناصر الاختيار هذه يتم الحصول عليها ديناميكيًا من Google Workspace.

يعرض الرمز التالي قائمة متعددة الاختيارات لمستخدمي Google Workspace. لتعبئة المستخدمين، يضبط حقل الإدخال commonDataSource على USER:

JSON

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

يوضّح الرمز التالي قائمة متعددة الاختيارات لمساحات Chat. لتعبئة المساحات، يحدّد إدخال الاختيار الحقل hostAppDataSource. تضبط قائمة الاختيار المتعدّد أيضًا defaultToCurrentSpace على true، ما يجعل المساحة الحالية هي الاختيار التلقائي في القائمة:

JSON

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

يمكن أيضًا أن تملأ قوائم الاختيار المتعدد العناصر من مصدر بيانات خارجي أو تابع لجهة خارجية. على سبيل المثال، يمكنك استخدام قوائم الاختيار المتعدّد لمساعدة المستخدم في الاختيار من قائمة العملاء المحتملين من نظام إدارة علاقات العملاء (CRM).

لاستخدام مصدر بيانات خارجي، يمكنك استخدام الحقل externalDataSource لتحديد دالة تعرض العناصر من مصدر البيانات.

لتقليل الطلبات إلى مصدر بيانات خارجي، يمكنك تضمين العناصر المقترَحة التي تظهر في قائمة الاختيار المتعدّد قبل أن يكتب المستخدمون في القائمة. على سبيل المثال، يمكنك تعبئة جهات الاتصال التي تم البحث عنها مؤخرًا للمستخدم. لتعبئة العناصر المقترَحة من مصدر بيانات خارجي، حدِّد عناصر SelectionItem.

تعرض عينة التعليمات البرمجية التالية قائمة متعددة الاختيارات تستعلم عن العناصر وتعبئتها من مصدر بيانات خارجي:

Node.js

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

استبدِل FUNCTION_URL بنقطة نهاية HTTP التي ترسل طلبات البحث إلى مصدر البيانات الخارجي.

Python

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

استبدِل FUNCTION_URL بنقطة نهاية HTTP التي ترسل طلبات البحث إلى مصدر البيانات الخارجي.

جافا

java/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 التي ترسل طلبات البحث إلى مصدر البيانات الخارجي.

برمجة التطبيقات

يرسل هذا المثال رسالة بطاقة من خلال عرض ملف JSON للبطاقة. يمكنك أيضًا استخدام خدمة بطاقات "برمجة تطبيقات Google".

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

تعبئة العناصر المقترَحة من مصدر بيانات ديناميكي

بالنسبة إلى مصادر البيانات الخارجية، يمكنك أيضًا الإكمال التلقائي واقتراح العناصر التي يبدأ المستخدمون في كتابتها في قائمة اختيار متعدد أو قائمة منسدلة. على سبيل المثال، إذا بدأ المستخدم في كتابة Atl لقائمة تملأ المدن في الولايات المتحدة، يمكن لتطبيق Chat أن يقترح تلقائيًا Atlanta قبل أن ينتهي المستخدم من الكتابة. يمكنك اقتراح ما يصل إلى 100 عنصر.

لإرجاع العناصر المقترَحة، يجب أن تنفّذ الدالة التي تطلب البحث في مصدر البيانات الخارجي ما يلي:

  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/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 التي ترسل طلبات البحث إلى مصدر البيانات الخارجي.

برمجة التطبيقات

يرسل هذا المثال رسالة بطاقة من خلال عرض ملف JSON للبطاقة. يمكنك أيضًا استخدام خدمة بطاقات "برمجة تطبيقات Google".

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

التحقّق من صحة البيانات التي تم إدخالها في البطاقات

توضّح هذه الصفحة كيفية التحقّق من صحة البيانات التي تم إدخالها في action والتطبيقات المصغّرة للبطاقة. على سبيل المثال، يمكنك التحقّق من أنّ حقل إدخال النص يحتوي على نص أدخله المستخدم، أو أنّه يحتوي على عدد معيّن من الأحرف.

ضبط التطبيقات المصغّرة المطلوبة للإجراءات

كجزء من action للبطاقة، أضِف أسماء التطبيقات المصغّرة التي يحتاجها الإجراء إلى قائمة requiredWidgets.

إذا لم تكن أي من التطبيقات المصغّرة المدرَجة هنا تحتوي على قيمة عند استدعاء هذا الإجراء، سيتم إلغاء إرسال إجراء النموذج.

عند ضبط "allWidgetsAreRequired": true لإجراء معيّن، تصبح جميع التطبيقات المصغّرة في البطاقة مطلوبة لهذا الإجراء.

ضبط إجراء allWidgetsAreRequired في ميزة الاختيار المتعدّد

JSON

{
  "sections": [
    {
      "header": "Select contacts",
      "widgets": [
        {
          "selectionInput": {
            "type": "MULTI_SELECT",
            "label": "Selected contacts",
            "name": "contacts",
            "multiSelectMaxSelectedItems": 3,
            "multiSelectMinQueryLength": 1,
            "onChangeAction": {
              "allWidgetsAreRequired": true
            },
            "items": [
              {
                "value": "contact-1",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 1",
                "bottomText": "Contact one description",
                "selected": false
              },
              {
                "value": "contact-2",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 2",
                "bottomText": "Contact two description",
                "selected": false
              },
              {
                "value": "contact-3",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 3",
                "bottomText": "Contact three description",
                "selected": false
              },
              {
                "value": "contact-4",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 4",
                "bottomText": "Contact four description",
                "selected": false
              },
              {
                "value": "contact-5",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 5",
                "bottomText": "Contact five description",
                "selected": false
              }
            ]
          }
        }
      ]
    }
  ]
}
ضبط إجراء allWidgetsAreRequired في dateTimePicker

JSON

{
  "sections": [
    {
      "widgets": [
        {
          "textParagraph": {
            "text": "A datetime picker widget with both date and time:"
          }
        },
        {
          "divider": {}
        },
        {
          "dateTimePicker": {
            "name": "date_time_picker_date_and_time",
            "label": "meeting",
            "type": "DATE_AND_TIME"
          }
        },
        {
          "textParagraph": {
            "text": "A datetime picker widget with just date:"
          }
        },
        {
          "divider": {}
        },
        {
          "dateTimePicker": {
            "name": "date_time_picker_date_only",
            "label": "Choose a date",
            "type": "DATE_ONLY",
            "onChangeAction":{
              "allWidgetsAreRequired": true
            }
          }
        },
        {
          "textParagraph": {
            "text": "A datetime picker widget with just time:"
          }
        },
        {
          "divider": {}
        },
        {
          "dateTimePicker": {
            "name": "date_time_picker_time_only",
            "label": "Select a time",
            "type": "TIME_ONLY"
          }
        }
      ]
    }
  ]
}
ضبط إجراء "allWidgetsAreRequired" في القائمة المنسدلة

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 1,
      "widgets": [
        {
          "selectionInput": {
            "name": "location",
            "label": "Select Color",
            "type": "DROPDOWN",
            "onChangeAction": {
              "allWidgetsAreRequired": true
            },
            "items": [
              {
                "text": "Red",
                "value": "red",
                "selected": false
              },
              {
                "text": "Green",
                "value": "green",
                "selected": false
              },
              {
                "text": "White",
                "value": "white",
                "selected": false
              },
              {
                "text": "Blue",
                "value": "blue",
                "selected": false
              },
              {
                "text": "Black",
                "value": "black",
                "selected": false
              }
            ]
          }
        }
      ]
    }
  ]
}

ضبط عملية التحقّق من صحة البيانات في تطبيق مصغّر لإدخال النص

في حقل التحقّق من صحة البيانات في التطبيق المصغّر textInput، يمكن تحديد عدد الأحرف المسموح به ونوع الإدخال لهذا التطبيق المصغّر لإدخال النص.

ضبط عدد الأحرف المسموح به لتطبيق مصغّر لإدخال النص

JSON

{
  "sections": [
    {
      "header": "Tell us about yourself",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 2,
      "widgets": [
        {
          "textInput": {
            "name": "favoriteColor",
            "label": "Favorite color",
            "type": "SINGLE_LINE",
            "validation": {"characterLimit": 15},
            "onChangeAction":{
              "allWidgetsAreRequired": true
            }
          }
        }
      ]
    }
  ]
}
ضبط نوع الإدخال لودجت إدخال نص

JSON

{
  "sections": [
    {
      "header": "Validate text inputs by input types",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 2,
      "widgets": [
        {
          "textInput": {
            "name": "mailing_address",
            "label": "Please enter a valid email address",
            "type": "SINGLE_LINE",
            "validation": {
              "inputType": "EMAIL"
            },
            "onChangeAction": {
              "allWidgetsAreRequired": true
            }
          }
        },
        {
          "textInput": {
            "name": "validate_integer",
            "label": "Please enter a number",
              "type": "SINGLE_LINE",
            "validation": {
              "inputType": "INTEGER"
            }
          }
        },
        {
          "textInput": {
            "name": "validate_float",
            "label": "Please enter a number with a decimal",
            "type": "SINGLE_LINE",
            "validation": {
              "inputType": "FLOAT"
            }
          }
        }
      ]
    }
  ]
}

تحديد المشاكل وحلّها

عندما يعرض تطبيق Google Chat أو بطاقة خطأً، تعرض واجهة Chat رسالة "حدث خطأ". أو "تتعذّر معالجة طلبك". في بعض الأحيان، لا تعرض واجهة مستخدم Chat أي رسالة خطأ، ولكن ينتج عن تطبيق Chat أو البطاقة نتيجة غير متوقعة، على سبيل المثال، قد لا تظهر رسالة بطاقة.

على الرغم من أنّ رسالة الخطأ قد لا تظهر في واجهة مستخدم Chat، تتوفّر رسائل خطأ وصفية وبيانات السجلّ لمساعدتك في إصلاح الأخطاء عند تفعيل تسجيل الأخطاء لتطبيقات Chat. للحصول على مساعدة في عرض الأخطاء وتصحيحها وإصلاحها، يُرجى الاطّلاع على مقالة تحديد أخطاء Google Chat وحلّها.

تطبيقات Chat التي ليست إضافات: تصميم البطاقات ومربّعات الحوار التفاعلية

تنطبق المستندات التالية على تطبيقات Chat التي ليست إضافات Google Workspace. لنقل تطبيق Chat ليس إضافة، يُرجى الاطّلاع على المقالة تحويل تطبيق Google Chat إلى إضافة Google Workspace.

يعرض الرمز التالي قائمة عناصر متعددة الاختيارات من مجموعة خارجية من جهات الاتصال للمستخدم في تطبيق Chat ليس إضافة. تعرض القائمة جهة اتصال واحدة تلقائيًا وتشغّل الدالة getContacts لاسترداد العناصر وتعبئتها من مصدر البيانات الخارجي:

Node.js

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

Python

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

جافا

java/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("getContacts"))
  .setItems(List.of(getContact("3")))))))))));

برمجة التطبيقات

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

لإكمال العناصر تلقائيًا في تطبيق Chat ليس إضافة، أنشئ دالة تطلب مصدر البيانات الخارجي وتعرض العناصر عندما يكتب المستخدم في قائمة الاختيار المتعدد. يجب أن تنفّذ الدالة ما يلي:

  • مرِّر كائن حدث يمثّل تفاعل المستخدم مع القائمة.
  • تأكَّد من أنّ قيمة invokedFunction لحدث التفاعل تطابق الدالة من الحقل externalDataSource.
  • عندما تتطابق الوظائف، يتم عرض العناصر المقترَحة من مصدر البيانات الخارجي. لاقتراح عناصر استنادًا إلى ما يكتبه المستخدم، يمكنك الحصول على قيمة المفتاح autocomplete_widget_query. تمثّل هذه القيمة ما يكتبه المستخدم في القائمة.

يُكمل الرمز التالي تلقائيًا العناصر من مصدر بيانات خارجي. باستخدام المثال السابق، يقترح تطبيق Chat الذي لا يُعدّ إضافةً العناصر استنادًا إلى وقت تشغيل الدالة getContacts:

Node.js

node/selection-input/index.js
/**
 * Responds to a WIDGET_UPDATE event in Google Chat.
 *
 * @param {Object} event The event object from Chat API.
 * @return {Object} Response from the Chat app.
 */
function onWidgetUpdate(event) {
  if (event.common["invokedFunction"] === "getContacts") {
    const query = event.common.parameters["autocomplete_widget_query"];
    return { actionResponse: {
      type: "UPDATE_WIDGET",
      updatedWidget: { suggestions: { items: [
        // The list is static here but it could be dynamic.
        getContact("1"), getContact("2"), getContact("3"), getContact("4"), getContact("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 suggested item for selectors.
 */
function getContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

Python

python/selection-input/main.py
def on_widget_update(event: dict) -> dict:
  """Responds to a WIDGET_UPDATE event in Google Chat."""
  if "getContacts" == event.get("common").get("invokedFunction"):
    query = event.get("common").get("parameters").get("autocomplete_widget_query")
    return { 'actionResponse': {
      'type': "UPDATE_WIDGET",
      'updatedWidget': { 'suggestions': { 'items': list(filter(lambda e: query is None or query in e["text"], [
        # The list is static here but it could be dynamic.
        get_contact("1"), get_contact("2"), get_contact("3"), get_contact("4"), get_contact("5")
      # Only return items based on the query from the user
      ]))}}
    }}


def get_contact(id: str) -> dict:
  """Generate a suggested contact given an ID."""
  return {
    'value': id,
    'startIconUri': "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    'text': "Contact " + id
  }

جافا

java/selection-input/src/main/java/com/google/chat/selectionInput/App.java
// Responds to a WIDGET_UPDATE event in Google Chat.
Message onWidgetUpdate(JsonNode event) {
  if ("getContacts".equals(event.at("/invokedFunction").asText())) {
    String query = event.at("/common/parameters/autocomplete_widget_query").asText();
    return new Message().setActionResponse(new ActionResponse()
      .setType("UPDATE_WIDGET")
      .setUpdatedWidget(new UpdatedWidget()
        .setSuggestions(new SelectionItems().setItems(List.of(
          // The list is static here but it could be dynamic.
          getContact("1"), getContact("2"), getContact("3"), getContact("4"), getContact("5")
        // Only return items based on the query from the user
        ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList()))));
  }
  return null;
}

// Generate a suggested contact given an ID.
GoogleAppsCardV1SelectionItem getContact(String id) {
  return new GoogleAppsCardV1SelectionItem()
    .setValue(id)
    .setStartIconUri("https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png")
    .setText("Contact " + id);
}

برمجة التطبيقات

apps-script/selection-input/selection-input.gs
/**
 * Responds to a WIDGET_UPDATE event in Google Chat.
 *
 * @param {Object} event The event object from Chat API.
 * @return {Object} Response from the Chat app.
 */
function onWidgetUpdate(event) {
  if (event.common["invokedFunction"] === "getContacts") {
    const query = event.common.parameters["autocomplete_widget_query"];
    return { actionResponse: {
      type: "UPDATE_WIDGET",
      updatedWidget: { suggestions: { items: [
        // The list is static here but it could be dynamic.
        getContact("1"), getContact("2"), getContact("3"), getContact("4"), getContact("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 suggested item for selectors.
 */
function getContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}