Menambahkan elemen UI interaktif ke kartu

Halaman ini menjelaskan cara menambahkan widget dan elemen UI ke kartu agar pengguna dapat berinteraksi dengan aplikasi Google Chat Anda, misalnya dengan mengklik tombol atau mengirimkan informasi.

Aplikasi Chat dapat menggunakan antarmuka Chat berikut untuk membuat kartu interaktif:

  • Pesan yang berisi satu atau beberapa kartu.
  • Halaman beranda, yang merupakan kartu yang muncul dari tab Beranda dalam pesan langsung dengan aplikasi Chat.
  • Dialog, yaitu kartu yang terbuka di jendela baru dari pesan dan halaman beranda.

Saat pengguna berinteraksi dengan kartu, aplikasi Chat dapat menggunakan data yang diterima untuk memproses dan meresponsnya dengan tepat. Untuk mengetahui detailnya, lihat Mengumpulkan dan memproses informasi dari pengguna Google Chat.


Gunakan Pembuat Kartu untuk mendesain dan melihat pratinjau pesan dan antarmuka pengguna untuk aplikasi Chat:

Buka Pembuat Kartu

Prasyarat

Aplikasi Google Chat yang dikonfigurasi untuk menerima dan merespons peristiwa interaksi. Untuk membuat aplikasi Chat interaktif, selesaikan salah satu panduan memulai berikut berdasarkan arsitektur aplikasi yang ingin Anda gunakan:

Menambahkan tombol

Widget ButtonList menampilkan serangkaian tombol. Tombol dapat menampilkan teks, ikon, atau teks dan ikon. Setiap Button mendukung tindakan OnClick yang terjadi saat pengguna mengklik tombol. Contoh:

  • Buka hyperlink dengan OpenLink, untuk memberikan informasi tambahan kepada pengguna.
  • Jalankan action yang menjalankan fungsi kustom, seperti memanggil API.

Untuk aksesibilitas, tombol mendukung teks alternatif.

Menambahkan tombol yang menjalankan fungsi kustom

Berikut adalah kartu yang terdiri dari widget ButtonList dengan dua tombol. Satu tombol membuka dokumentasi developer Google Chat di tab baru. Tombol lainnya menjalankan fungsi kustom yang disebut goToView() dan meneruskan parameter viewType="BIRD EYE VIEW".

Menambahkan tombol dengan gaya Desain Material

Berikut menampilkan serangkaian tombol dalam berbagai gaya tombol Desain Material.

Untuk menerapkan gaya Desain Material, jangan sertakan atribut 'warna' [color].

Menambahkan tombol dengan warna khusus dan tombol yang dinonaktifkan

Anda dapat mencegah pengguna mengklik tombol dengan menyetel "disabled": "true".

Berikut menampilkan kartu yang terdiri dari widget ButtonList dengan dua tombol. Satu tombol menggunakan kolom Color untuk menyesuaikan warna latar belakang tombol. Tombol lainnya dinonaktifkan dengan kolom Disabled, yang mencegah pengguna mengklik tombol dan menjalankan fungsi.

Menambahkan tombol dengan ikon

Berikut menampilkan kartu yang terdiri dari widget ButtonList dengan dua widget ikon Button. Satu tombol menggunakan kolom knownIcon untuk menampilkan ikon email bawaan Google Chat. Tombol lainnya menggunakan kolom iconUrl untuk menampilkan widget ikon kustom.

Menambahkan tombol dengan ikon dan teks

Berikut menampilkan kartu yang terdiri dari widget ButtonList yang meminta pengguna untuk mengirim email. Tombol pertama menampilkan ikon email dan tombol kedua menampilkan teks. Pengguna dapat mengklik tombol ikon atau teks untuk menjalankan fungsi sendEmail.

Menyesuaikan tombol untuk bagian yang dapat diciutkan

Sesuaikan tombol kontrol yang menciutkan dan meluaskan bagian dalam kartu. Pilih dari berbagai ikon atau gambar untuk merepresentasikan konten bagian secara visual, sehingga memudahkan pengguna memahami dan berinteraksi dengan informasi tersebut.

Menambahkan Menu Tambahan

Overflow menu dapat digunakan di kartu Chat untuk menawarkan opsi dan tindakan tambahan. Opsi ini memungkinkan Anda menyertakan lebih banyak opsi tanpa membuat antarmuka kartu menjadi berantakan, sehingga memastikan desain yang bersih dan teratur.

Menambahkan daftar Chip

Widget ChipList menyediakan cara yang serbaguna dan menarik secara visual untuk menampilkan informasi. Gunakan daftar chip untuk merepresentasikan tag, kategori, atau data relevan lainnya, sehingga pengguna lebih mudah menjelajahi dan berinteraksi dengan konten Anda.

Mengumpulkan informasi dari pengguna

Bagian ini menjelaskan cara menambahkan widget yang mengumpulkan informasi, seperti teks atau pilihan.

Untuk mempelajari cara memproses input pengguna, lihat Mengumpulkan dan memproses informasi dari pengguna Google Chat.

Mengumpulkan teks

Widget TextInput menyediakan kolom tempat pengguna dapat memasukkan teks. Widget mendukung saran, yang membantu pengguna memasukkan data seragam, dan tindakan saat perubahan, yang merupakan Actions yang berjalan saat perubahan terjadi di kolom input teks, seperti pengguna menambahkan atau menghapus teks.

Jika Anda perlu mengumpulkan data abstrak atau tidak diketahui dari pengguna, gunakan widget TextInput ini. Untuk mengumpulkan data yang ditentukan dari pengguna, gunakan widget SelectionInput sebagai gantinya.

Berikut adalah kartu yang terdiri dari widget TextInput:

Mengumpulkan tanggal atau waktu

Widget DateTimePicker memungkinkan pengguna memasukkan tanggal, waktu, atau keduanya. Atau, pengguna dapat menggunakan pemilih untuk memilih tanggal dan waktu. Jika pengguna memasukkan tanggal atau waktu yang tidak valid, pemilih akan menampilkan error yang meminta pengguna untuk memasukkan informasi dengan benar.

Berikut ini menampilkan kartu yang terdiri dari tiga jenis widget DateTimePicker yang berbeda:

Mengizinkan pengguna memilih item

Widget SelectionInput menyediakan sekumpulan item yang dapat dipilih, seperti kotak centang, tombol pilihan, tombol geser, atau menu drop-down. Anda dapat menggunakan widget ini untuk mengumpulkan data yang ditentukan dan distandardisasi dari pengguna. Untuk mengumpulkan data yang tidak ditentukan dari pengguna, gunakan widget TextInput.

Widget SelectionInput mendukung saran, yang membantu pengguna memasukkan data seragam, dan tindakan saat perubahan, yang merupakan Actions yang berjalan saat perubahan terjadi di kolom input pilihan, seperti pengguna memilih atau membatalkan pilihan item.

Aplikasi chat dapat menerima dan memproses nilai item yang dipilih. Untuk mengetahui detail tentang cara menggunakan input formulir, lihat Memproses informasi yang dimasukkan oleh pengguna.

Bagian ini memberikan contoh kartu yang menggunakan widget SelectionInput. Contoh menggunakan berbagai jenis input bagian:

Menambahkan kotak centang

Berikut menampilkan kartu yang meminta pengguna menentukan apakah kontak bersifat profesional, pribadi, atau keduanya, dengan widget SelectionInput yang menggunakan kotak centang:

Menambahkan tombol pilihan

Berikut menampilkan kartu yang meminta pengguna untuk menentukan apakah kontak bersifat profesional atau pribadi dengan widget SelectionInput yang menggunakan tombol pilihan:

Menambahkan tombol

Berikut menampilkan kartu yang meminta pengguna menentukan apakah kontak bersifat profesional, pribadi, atau keduanya dengan widget SelectionInput yang menggunakan tombol:

Berikut menampilkan kartu yang meminta pengguna untuk menentukan apakah kontak bersifat profesional atau pribadi dengan widget SelectionInput yang menggunakan menu drop-down:

Menambahkan menu pilihan multipel

Berikut menampilkan kartu yang meminta pengguna memilih kontak dari menu pilihan ganda:

Anda dapat mengisi item untuk menu pilihan ganda dari sumber data berikut di Google Workspace:

  • Pengguna Google Workspace: Anda hanya dapat mengisi pengguna dalam organisasi Google Workspace yang sama.
  • Ruang Chat: Pengguna yang memasukkan item di menu multiselect hanya dapat melihat dan memilih ruang tempat mereka berada dalam organisasi Google Workspace mereka.

Untuk menggunakan sumber data Google Workspace, Anda menentukan kolom platformDataSource. Tidak seperti jenis input pilihan lainnya, Anda menghilangkan SelectionItem objek, karena item pilihan ini bersumber secara dinamis dari Google Workspace.

Kode berikut menampilkan menu multi-pilihan pengguna Google Workspace. Untuk mengisi pengguna, input pilihan menetapkan commonDataSource ke USER:

JSON

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

Kode berikut menampilkan menu pilihan ganda ruang Chat. Untuk mengisi ruang, input pilihan menentukan kolom hostAppDataSource. Menu pilihan multipel juga menetapkan defaultToCurrentSpace ke true, yang menjadikan ruang saat ini sebagai pilihan default di menu:

JSON

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

Menu pilihan ganda juga dapat mengisi item dari sumber data pihak ketiga atau eksternal. Misalnya, Anda dapat menggunakan menu pilihan ganda untuk membantu pengguna memilih dari daftar prospek penjualan dari sistem pengelolaan hubungan pelanggan (CRM).

Untuk menggunakan sumber data eksternal, Anda menggunakan kolom externalDataSource untuk menentukan fungsi yang menampilkan item dari sumber data.

Untuk mengurangi permintaan ke sumber data eksternal, Anda dapat menyertakan item yang disarankan yang muncul di menu pilihan ganda sebelum pengguna mengetik di menu. Misalnya, Anda dapat mengisi kontak yang baru saja ditelusuri untuk pengguna. Untuk mengisi item yang disarankan dari sumber data eksternal, tentukan objek SelectionItem.

Kode berikut menampilkan menu multi-pilihan item dari kumpulan kontak eksternal untuk pengguna. Menu menampilkan satu kontak secara default dan menjalankan fungsi getContacts untuk mengambil dan mengisi item dari sumber data eksternal:

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

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

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

Untuk sumber data eksternal, Anda juga dapat melengkapi otomatis item yang mulai diketik pengguna di menu pilihan ganda. Misalnya, jika pengguna mulai mengetik Atl untuk menu yang menampilkan kota-kota di Amerika Serikat, aplikasi Chat Anda dapat menyarankan Atlanta secara otomatis sebelum pengguna selesai mengetik. Anda dapat melengkapi otomatis hingga 100 item.

Untuk melengkapi item secara otomatis, Anda membuat fungsi yang membuat kueri sumber data eksternal dan menampilkan item setiap kali pengguna mengetik di menu pilihan ganda. Fungsi harus melakukan hal berikut:

  • Teruskan objek peristiwa yang mewakili interaksi pengguna dengan menu.
  • Mengidentifikasi bahwa nilai invokedFunction peristiwa interaksi cocok dengan fungsi dari kolom externalDataSource.
  • Jika fungsi cocok, tampilkan item yang disarankan dari sumber data eksternal. Untuk menyarankan item berdasarkan apa yang diketik pengguna, dapatkan nilai untuk kunci autocomplete_widget_query. Nilai ini mewakili apa yang diketik pengguna di menu.

Kode berikut melengkapi otomatis item dari resource data eksternal. Dengan menggunakan contoh sebelumnya, aplikasi Chat menyarankan item berdasarkan waktu fungsi getContacts dipicu:

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

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

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

Memvalidasi data yang dimasukkan ke kartu

Halaman ini menjelaskan cara memvalidasi data yang dimasukkan ke action dan widget kartu. Misalnya, Anda dapat memvalidasi bahwa kolom input teks memiliki teks yang dimasukkan oleh pengguna, atau bahwa kolom tersebut berisi sejumlah karakter tertentu.

Menetapkan widget yang diperlukan untuk tindakan

Sebagai bagian dari action kartu, tambahkan nama widget yang diperlukan tindakan ke daftar requiredWidgets-nya.

Jika ada widget yang tercantum di sini tidak memiliki nilai saat tindakan ini dipanggil, pengiriman tindakan formulir akan dibatalkan.

Jika "all_widgets_are_required": "true" ditetapkan untuk suatu tindakan, semua widget di kartu akan diperlukan oleh tindakan ini.

Menetapkan tindakan all_widgets_are_required dalam pilihan ganda

JSON

{
  "sections": [
    {
      "header": "Select contacts",
      "widgets": [
        {
          "selectionInput": {
            "type": "MULTI_SELECT",
            "label": "Selected contacts",
            "name": "contacts",
            "multiSelectMaxSelectedItems": 3,
            "multiSelectMinQueryLength": 1,
            "onChangeAction": {
              "all_widgets_are_required": 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
              }
            ]
          }
        }
      ]
    }
  ]
}
Menetapkan tindakan all_widgets_are_required di 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":{
              "all_widgets_are_required": 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"
          }
        }
      ]
    }
  ]
}
Menetapkan tindakan all_widgets_are_required di menu drop-down

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 1,
      "widgets": [
        {
          "selectionInput": {
            "name": "location",
            "label": "Select Color",
            "type": "DROPDOWN",
            "onChangeAction": {
              "all_widgets_are_required": 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
              }
            ]
          }
        }
      ]
    }
  ]
}

Menetapkan validasi untuk widget input teks

Di kolom validasi widget textInput, batas karakter dan jenis input untuk widget input teks ini dapat ditentukan.

Menetapkan batas karakter untuk widget input teks

JSON

{
  "sections": [
    {
      "header": "Tell us about yourself",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 2,
      "widgets": [
        {
          "textInput": {
            "name": "favoriteColor",
            "label": "Favorite color",
            "type": "SINGLE_LINE",
            "validation": {"character_limit":15},
            "onChangeAction":{
              "all_widgets_are_required": true
            }
          }
        }
      ]
    }
  ]
}
Menetapkan jenis input untuk widget input teks

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": {
              "input_type": "EMAIL"
            },
            "onChangeAction": {
              "all_widgets_are_required": true
            }
          }
        },
        {
          "textInput": {
            "name": "validate_integer",
            "label": "Please enter a number",
              "type": "SINGLE_LINE",
            "validation": {
              "input_type": "INTEGER"
            }
          }
        },
        {
          "textInput": {
            "name": "validate_float",
            "label": "Please enter a number with a decimal",
            "type": "SINGLE_LINE",
            "validation": {
              "input_type": "FLOAT"
            }
          }
        }
      ]
    }
  ]
}

Memecahkan masalah

Saat aplikasi atau kartu Google Chat menampilkan error, antarmuka Chat akan menampilkan pesan yang mengatakan "Terjadi masalah". atau "Tidak dapat memproses permintaan Anda". Terkadang UI Chat tidak menampilkan pesan error apa pun, tetapi aplikasi atau kartu Chat menghasilkan hasil yang tidak terduga; misalnya, pesan kartu mungkin tidak muncul.

Meskipun pesan error mungkin tidak ditampilkan di UI Chat, pesan error deskriptif dan data log tersedia untuk membantu Anda memperbaiki error jika logging error untuk aplikasi Chat diaktifkan. Untuk mendapatkan bantuan dalam melihat, men-debug, dan memperbaiki error, lihat Memecahkan masalah dan memperbaiki error Google Chat.