Google Chat kullanıcılarından bilgi toplama ve işleme

Bu kılavuzda, Google Chat uygulamalarının kart tabanlı arayüzlerde form girişleri oluşturarak kullanıcılardan nasıl bilgi toplayıp işleyebileceği açıklanmaktadır.

Çeşitli widget'ların yer aldığı bir iletişim kutusu.
Şekil 1: Kişi bilgilerini toplamak için iletişim kutusu açan bir
örnek Chat uygulaması.

Chat uygulamaları, Chat'te veya Chat dışında işlem yapmak için kullanıcılardan bilgi ister. Örneğin:

  • Ayarları yapılandırın. Örneğin, kullanıcıların bildirim ayarlarını özelleştirmesine veya Chat uygulamasını yapılandırıp bir ya da daha fazla alana eklemesine izin vermek için.
  • Diğer Google Workspace uygulamalarında bilgi oluşturma veya güncelleme Örneğin, kullanıcıların Google Takvim etkinliği oluşturmasına izin verin.
  • Kullanıcıların diğer uygulamalardaki veya web hizmetlerindeki kaynaklara erişmesine ve bunları güncellemesine izin verin. Örneğin, bir Chat uygulaması, kullanıcıların destek kaydının durumunu doğrudan bir Chat alanından güncellemesine yardımcı olabilir.

Ön koşullar

HTTP

Kullanıcı etkileşimlerini alan ve bunlara yanıt veren bir Google Chat uygulaması. Oluşturmak için HTTP hızlı başlangıç bölümünü tamamlayın.

Apps Komut Dosyası

Kullanıcı etkileşimlerini alan ve bunlara yanıt veren bir Google Chat uygulaması. Oluşturmak için Apps Komut Dosyası hızlı başlangıç kılavuzunu tamamlayın.

Kartları kullanarak form oluşturma

Chat uygulamaları, bilgi toplamak için formlar ve girişler tasarlayıp bunları kartlara yerleştirir. Chat uygulamaları, kullanıcılara kart göstermek için aşağıdaki Chat arayüzlerini kullanabilir:

  • Bir veya daha fazla kart içeren mesajlar.
  • Chat uygulamasıyla doğrudan mesajlarda Ana Sayfa sekmesinden görünen bir kart olan Ana Sayfalar
  • İletişim kutuları: İletilerden ve ana sayfalardan yeni bir pencerede açılan kartlardır.

Sohbet uygulamaları, aşağıdaki widget'ları kullanarak kart oluşturabilir:

  • Kullanıcılardan bilgi isteyen form girişi widget'ları. İsteğe bağlı olarak, kullanıcıların bilgileri doğru şekilde girmesini ve biçimlendirmesini sağlamak için form girişi widget'larına doğrulama ekleyebilirsiniz. Sohbet uygulamaları aşağıdaki form girişi widget'larını kullanabilir:

    • Serbest biçimli veya önerilen metin için metin girişleri (textInput)
    • Seçim girişleri (selectionInput), onay kutuları, radyo düğmeleri ve açılır menüler gibi seçilebilir kullanıcı arayüzü öğeleridir. Seçim giriş widget'ları, Google Workspace verilerindeki (ör. Chat alanı) veya dinamik bir veri kaynağındaki öğeleri de doldurup önerebilir. Ayrıntılı bilgi için Açılır menü ekleme ve Çoklu seçim menüsü ekleme başlıklı bölümleri inceleyin.
    • Tarih ve saat girişleri için tarih/saat seçiciler (dateTimePicker).
  • Kullanıcıların karta girdikleri değerleri gönderebilmeleri için düğme widget'ı. Kullanıcı düğmeyi tıkladıktan sonra Chat uygulaması aldığı bilgileri işleyebilir.

Aşağıdaki örnekte, bir kart metin girişi, tarih/saat seçici ve seçim girişi kullanarak iletişim bilgilerini topluyor:

Bilgi toplamak için kullanabileceğiniz etkileşimli widget'larla ilgili daha fazla örnek için Etkileşimli kart veya iletişim kutusu tasarlama başlıklı makaleyi inceleyin.

Açılır menü ekleme

Chat uygulamaları, seçim öğelerini özelleştirmek veya kullanıcıların dinamik bir veri kaynağından tek bir öğe seçmesine izin vermek için SelectionInput türü bir widget olan açılır menüleri kullanabilir. Örneğin, aşağıdaki kartta kullanıcıların kişi listesinden dinamik olarak seçim yapabileceği bir açılır menü gösterilmektedir:

Açılır menüdeki öğeleri aşağıdaki veri kaynaklarından doldurabilirsiniz:

Google Workspace veri kaynağından öğe doldurma

Google Workspace kullanıcıları gibi Google Workspace veri kaynaklarındaki öğeleri doldurmak için DataSourceConfig nesnesinde platformDataSource alanını belirtin. Diğer seçim girişi türlerinin aksine, bu seçim öğeleri Google Workspace'ten dinamik olarak alındığı için SelectionItem nesnelerini atlıyorsunuz.

Aşağıdaki kodda, Google Workspace kullanıcılarının yer aldığı bir açılır menü gösterilmektedir:

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

Öğeleri harici bir veri kaynağından doldurma

Açılır menüler, üçüncü taraf veya harici bir veri kaynağındaki öğelerle de doldurulabilir. Harici bir veri kaynağını kullanmak için veri kaynağındaki öğeleri sorgulayan ve döndüren işlevi içeren bir DataSourceConfig nesnesindeki remoteDataSource alanını belirtirsiniz.

Harici bir veri kaynağına yapılan istekleri azaltmak için kullanıcılar menüye yazmadan önce açılır menüde görünen önerilen öğeleri ekleyebilirsiniz. Önerilen öğeleri harici bir veri kaynağından doldurmak için statik SelectionItem nesneleri belirtin.

Aşağıdaki kodda, harici bir veri kaynağından öğeleri sorgulayan ve dolduran bir açılır menü gösterilmektedir:

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

Önerilen öğelerin nasıl döndürüleceğini gösteren eksiksiz bir örnek için Seçim öğeleri önerme bölümüne bakın.

Çoklu seçim menüsü ekleme

Chat uygulamaları, seçim öğelerini özelleştirmek veya kullanıcıların dinamik bir veri kaynağındaki öğeleri seçmesine izin vermek için SelectionInput türünde bir widget olan çoklu seçim menülerini kullanabilir. Örneğin, aşağıdaki kartta kullanıcıların kişi listesinden dinamik olarak seçim yapabileceği bir çoklu seçim menüsü gösterilmektedir:

Çoklu seçim menüsündeki öğeleri aşağıdaki veri kaynaklarından doldurabilirsiniz:

  • Kullanıcının üyesi olduğu kullanıcılar veya Chat alanları da dahil olmak üzere Google Workspace verileri Menüye yalnızca aynı Google Workspace kuruluşundaki öğeler eklenir.
  • İlişkisel veritabanı gibi harici veri kaynakları Örneğin, bir kullanıcının müşteri ilişkileri yönetimi (CRM) sistemindeki satış potansiyel müşterileri listesinden seçim yapmasına yardımcı olmak için çoklu seçim menülerini kullanabilirsiniz.

Google Workspace veri kaynağından öğe doldurma

Google Workspace veri kaynaklarını kullanmak için SelectionInput widget'ında platformDataSource alanını belirtin. Diğer seçim girişi türlerinin aksine, bu seçim öğeleri Google Workspace'ten dinamik olarak alındığı için SelectionItem nesnelerini atlıyorsunuz.

Aşağıdaki kod, Google Workspace kullanıcılarının çoklu seçim menüsünü gösterir. Kullanıcıları doldurmak için seçim girişi, commonDataSource değerini USER olarak ayarlar:

JSON

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

Aşağıdaki kodda, sohbet alanlarının çoklu seçim menüsü gösterilmektedir. Boşlukları doldurmak için seçim girişi hostAppDataSource alanını belirtir. Çoklu seçim menüsü, defaultToCurrentSpace öğesini true olarak da ayarlar. Bu sayede, mevcut alan menüdeki varsayılan seçim olur:

JSON

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

Öğeleri harici bir veri kaynağından doldurma

Çoklu seçim menüleri, üçüncü taraf veya harici veri kaynaklarındaki öğelerle de doldurulabilir. Harici bir veri kaynağı kullanmak için SelectionInput widget'ında externalDataSource alanını belirtirsiniz. Bu alan, veri kaynağındaki öğeleri sorgulayan ve döndüren işlevi içerir.

Harici bir veri kaynağına yapılan istekleri azaltmak için kullanıcılar menüye yazmadan önce çoklu seçim menüsünde görünen önerilen öğeleri ekleyebilirsiniz. Örneğin, kullanıcı için yakın zamanda aranan kişileri doldurabilirsiniz. Önerilen öğeleri harici bir veri kaynağından doldurmak için statik SelectionItem nesneleri belirtin.

Aşağıdaki kod örneğinde, harici bir veri kaynağından öğeleri sorgulayan ve dolduran çoklu seçim menüsü gösterilmektedir:

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 yerine harici veri kaynağını sorgulayan HTTP uç noktasını girin.

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 yerine harici veri kaynağını sorgulayan HTTP uç noktasını girin.

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 yerine harici veri kaynağını sorgulayan HTTP uç noktasını girin.

Apps Komut Dosyası

Bu örnek, card JSON döndürerek kart mesajı gönderir. Apps Komut Dosyası kart hizmetini de kullanabilirsiniz.

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

Önerilen öğelerin nasıl döndürüleceğini gösteren eksiksiz bir örnek için Seçim öğeleri önerme bölümüne bakın.

Etkileşimli widget'lardan veri alma

Kullanıcılar bir düğmeyi tıkladığında etkileşimle ilgili bilgilerle birlikte Chat uygulamaları işlemi tetiklenir. Etkinlik yükünün commonEventObject bölümünde, formInputs nesnesi kullanıcının girdiği tüm değerleri içerir.

Değerleri, nesneden alabilirsiniz. event.commonEventObject.formInputs.WIDGET_NAME, burada WIDGET_NAME, widget için belirttiğiniz name alanıdır. Değerler, widget için belirli bir veri türü olarak döndürülür.

Aşağıda, bir kullanıcının her widget için değer girdiği bir etkinlik nesnesinin bir bölümü gösterilmektedir:

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

Chat uygulamanız, verileri almak için kullanıcıların widget'lara girdiği değerleri almak üzere etkinlik nesnesini işler. Aşağıdaki tabloda, belirli bir form girişi widget'ının değerinin nasıl alınacağı gösterilmektedir. Tabloda, her bir widget için widget'ın kabul ettiği veri türü, değerin etkinlik nesnesinde depolandığı yer ve örnek bir değer gösterilir.

Form girişi widget'ı Giriş verilerinin türü Etkinlik nesnesinden giriş değeri Örnek değer
textInput stringInputs event.commonEventObject.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs İlk veya tek değeri almak için event.commonEventObject.formInputs.contactType.stringInputs.value[0] Personal
Yalnızca tarihleri kabul eden dateTimePicker. dateInput event.commonEventObject.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Chat uygulaması verileri aldıktan sonra aşağıdakilerden herhangi birini yapabilir:

Seçim öğeleri öner

Bir kartta öğeleri harici bir veri kaynağından dolduran çoklu seçim menüsü veya açılır menü varsa Chat uygulaması, kullanıcıların menüye yazdıklarına göre önerilen öğeleri döndürebilir. Örneğin, bir kullanıcı ABD'deki şehirleri dolduran bir menü için Atl yazmaya başladığında Chat uygulamanız, kullanıcı yazmayı bitirmeden önce Atlanta otomatik olarak önerebilir. Chat uygulaması 100 adede kadar öğe önerebilir.

Bir seçim girişindeki öğeleri önermek ve dinamik olarak doldurmak için karttaki SelectionInput widget'ı, harici veri kaynağını sorgulayan bir işlev belirtmelidir. Çoklu seçim menüleri için externalDataSource alanını belirtirsiniz. Açılır menüler için DataSourceConfig nesnesindeki remoteDataSource alanını belirtirsiniz.

Ayrıca, menünün öneri döndürmeden önce kullanıcının yazdığı karakter sayısını da yapılandırabilirsiniz. Çoklu seçim menüleri için multiSelectMinQueryLength alanını ayarlayın. Açılır menüler için DataSourceConfig içindeki min_characters_trigger alanını ayarlayın.

İşlev, önerilen öğeleri döndürmek için aşağıdakileri yapmalıdır:

  1. Kullanıcılar menüye yazdığında Chat uygulamasının aldığı etkinlik nesnesini işleyin.
  2. Etkinlik nesnesinden, kullanıcının yazdığı değeri alın. Bu değer, event.commonEventObject.parameters["autocomplete_widget_query"] alanında gösterilir.
  3. Kullanıcıya önerilecek bir veya daha fazla SelectionItems almak için kullanıcı girişi değerini kullanarak veri kaynağını sorgulayın.
  4. modifyCard nesnesiyle birlikte action RenderActions döndürerek önerilen öğeleri döndürün.

Aşağıdaki kod örneğinde, bir Chat uygulamasının karttaki çoklu seçim menüsünde öğeleri nasıl dinamik olarak önerdiği gösterilmektedir. Kullanıcı menüye yazdığında, widget'ın externalDataSource alanında sağlanan işlev veya uç nokta, harici bir veri kaynağını sorgular ve kullanıcının seçebileceği öğeler önerir.

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 yerine harici veri kaynağını sorgulayan HTTP uç noktasını girin.

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 yerine harici veri kaynağını sorgulayan HTTP uç noktasını girin.

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 yerine harici veri kaynağını sorgulayan HTTP uç noktasını girin.

Apps Komut Dosyası

Bu örnek, card JSON döndürerek kart mesajı gönderir. Apps Komut Dosyası kart hizmetini de kullanabilirsiniz.

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

Verileri başka bir karta aktarma

Bir kullanıcı karttan bilgi gönderdikten sonra, aşağıdakilerden herhangi birini yapmak için ek kartlar döndürmeniz gerekebilir:

  • Farklı bölümler oluşturarak kullanıcıların daha uzun formları doldurmasına yardımcı olun.
  • Kullanıcıların, ilk karttaki bilgileri önizleyip onaylamasına izin verin. Böylece, yanıtlarını göndermeden önce inceleyebilirler.
  • Formun kalan kısımlarını dinamik olarak doldurur. Örneğin, kullanıcıları randevu oluşturmaya yönlendirmek için bir sohbet uygulaması, randevu nedenini isteyen bir ilk kart gösterebilir ve ardından randevu türüne göre uygun zamanları sağlayan başka bir kartı doldurabilir.

İlk karttan girilen verileri aktarmak için aşağıdaki örnekte gösterildiği gibi, button widget'ını actionParameters ile oluşturabilirsiniz. Bu widget'lar, widget'ın name ve kullanıcının girdiği değeri içerir:

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 kısmını, düğme tıklamalarını işleyen HTTP uç noktasıyla değiştirin.

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 kısmını, düğme tıklamalarını işleyen HTTP uç noktasıyla değiştirin.

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 kısmını, düğme tıklamalarını işleyen HTTP uç noktasıyla değiştirin.

Apps Komut Dosyası

Bu örnek, card JSON döndürerek kart mesajı gönderir. Apps Komut Dosyası kart hizmetini de kullanabilirsiniz.

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

Kullanıcı düğmeyi tıkladığında Chat uygulamanız, veri alabileceğiniz bir etkinlik nesnesi alır.

Form gönderimine yanıt verme

Chat uygulaması, kart mesajından veya iletişim kutusundan verileri aldıktan sonra ya alındığını onaylayarak ya da hata döndürerek yanıt verir.

Aşağıdaki örnekte, bir sohbet uygulaması, kart mesajından gönderilen bir formu başarıyla aldığını onaylamak için kısa mesaj gönderiyor.

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 Komut Dosyası

Bu örnek, card JSON döndürerek kart mesajı gönderir. Apps Komut Dosyası kart hizmetini de kullanabilirsiniz.

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

Bir iletişim kutusunu işleyip kapatmak için onay mesajı göndermek, orijinal mesajı veya kartı güncellemek ya da yalnızca iletişim kutusunu kapatmak isteyip istemediğinizi belirten bir RenderActions nesnesi döndürürsünüz. Adımlar için İletişim kutusunu kapatma başlıklı makaleyi inceleyin.

Sorun giderme

Bu bölümde, Chat'teki iletişim kutularıyla etkileşimde bulunurken karşılaşılan belirli hata kodları ve çalışma zamanı davranışlarıyla ilgili sorun giderme adımları sağlanmaktadır.

İletişim kutusu etkileşimleri "Eklenti çağrılırken, belirtilmemiş hata oluştu" hatasını döndürüyor

Bir iletişim kutusuyla etkileşimde bulunurken "Eklenti çağrılırken belirtilmemiş hata." mesajını ve 13 kodunu içeren hata günlükleri görürseniz bu durum genellikle dahili bir hatayı veya Chat uygulamasının HTTP uç noktasının isteği işlemeyi ya da geçerli bir yanıt döndürmeyi başaramadığını gösterir.

Bu hatayı gidermek için:

  • HTTP uç noktanızın günlüklerinde işlenmemiş istisnalar veya kilitlenmeler olup olmadığını kontrol edin.
  • Uç noktanızın isteklere 30 saniye içinde yanıt verdiğini doğrulayın. Uç nokta 30 saniyeden uzun sürerse Chat yanıtı işleyemez ve etkileşim başarısız olur. Ayrıntılı bilgi için Hız sınırları ve en iyi uygulamalar başlıklı makaleyi inceleyin.
  • Uç noktanızın geçerli bir yanıt döndürdüğünden emin olun. Diyalog gönderimleri için, uç nokta, doğru JSON biçiminde bir RenderActions nesnesi döndürmelidir. Yanıt yanlış biçimlendirilmişse veya gerekli alanları içermiyorsa iletişim kutusu etkileşimi başarısız olabilir.

Bir Google Chat uygulaması veya kartı hata döndürdüğünde Chat arayüzünde "Bir hata oluştu" veya "İsteğiniz işlenemiyor" mesajı gösterilir. Bazen Chat kullanıcı arayüzünde hata mesajı gösterilmez ancak Chat uygulaması veya kartı beklenmedik bir sonuç üretir. Örneğin, kart mesajı görünmeyebilir.

Chat kullanıcı arayüzünde hata mesajı gösterilmese de Chat uygulamaları için hata günlüğü kaydı etkinleştirildiğinde hataları düzeltmenize yardımcı olacak açıklayıcı hata mesajları ve günlük verileri kullanılabilir. Hataları görüntüleme, hata ayıklama ve düzeltmeyle ilgili yardım için Google Chat hatalarını giderme ve düzeltme başlıklı makaleyi inceleyin.

Eklenti olmayan Chat uygulamaları: Kullanıcıların kartlara girdiği form verilerini okuma

Aşağıdaki belgeler, Google Workspace eklentisi olmayan Chat uygulamaları için geçerlidir. Eklenti olmayan bir Chat uygulamasını taşımak için Google Chat uygulamasını Google Workspace eklentisine dönüştürme başlıklı makaleyi inceleyin.

Kartları kullanarak form oluşturma

Metin girişi, tarih/saat seçici ve seçim girişi içeren bir iletişim formu kullanan, eklenti olmayan bir Chat uygulaması örneği için aşağıdaki koda bakın:

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 Komut Dosyası

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

Etkileşimli widget'lardan veri alma

Kullanıcılar bir düğmeyi her tıkladığında, eklenti olmayan Chat uygulamaları, düğmenin konumuna bağlı bir etkileşim etkinliği alır:

  • Düğme bir mesajda veya iletişim kutusundaysa eklenti olmayan Chat uygulamaları, etkileşimle ilgili bilgileri içeren bir CARD_CLICKED etkileşim etkinliği alır. Etkileşim etkinliklerinin CARD_CLICKED yükünde, kullanıcının girdiği tüm değerlere sahip bir common.formInputs nesnesi (event.common.formInputs) bulunur.

    Değerleri, nesneden alabilirsiniz. common.formInputs.WIDGET_NAME, burada WIDGET_NAME, widget için belirttiğiniz name alanıdır. Değerler, widget için belirli bir veri türü olarak döndürülür (Inputs nesnesi olarak gösterilir).

    Aşağıda, bir kullanıcının her widget için değer girdiği bir CARD_CLICKED etkileşim etkinliğinin bir bölümü gösterilmektedir:

    HTTP

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

    Apps Komut Dosyası

    {
      "type": "CARD_CLICKED",
      "common": { "formInputs": {
        "contactName": { "": { "stringInputs": {
          "value": ["Kai 0"]
        }}},
        "contactBirthdate": { "": { "dateInput": {
          "msSinceEpoch": 1000425600000
        }}},
          "contactType": { "": { "stringInputs": {
          "value": ["Personal"]
        }}}
      }}
    }
    
  • Düğme bir ana sayfadaysa, Eklenti olmayan Chat uygulamaları SUBMIT_FORM etkileşim etkinliği alır. Etkileşim etkinliğinin yükü, kullanıcının girdiği değerleri içeren bir commonEventObject.formInputs nesnesi (event.commonEventObject.formInputs) içerir.

    Değerleri, nesneden alabilirsiniz. commonEventObject.formInputs.WIDGET_NAME, burada WIDGET_NAME, widget için belirttiğiniz name alanıdır. Değerler, widget için belirli bir veri türü olarak döndürülür (Inputs nesnesi olarak gösterilir).

    Aşağıda, bir kullanıcının her widget için değer girdiği bir SUBMIT_FORM etkileşim etkinliğinin bir bölümü gösterilmektedir:

    HTTP

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

    Apps Komut Dosyası

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

Verileri almak için, eklenti olmayan bir Chat uygulaması, kullanıcıların widget'lara girdiği değerleri almak üzere etkileşim etkinliğini işler. Aşağıdaki tabloda, belirli bir form girişi widget'ının değerinin nasıl alınacağı gösterilmektedir. Tabloda, her widget için widget'ın kabul ettiği veri türü, değerin etkileşim etkinliğinde depolandığı yer ve örnek bir değer gösterilir.

Form girişi widget'ı Giriş verilerinin türü Etkileşim etkinliğinden alınan giriş değeri Örnek değer
textInput stringInputs event.common.formInputs.contactName.stringInputs.value[0] Kai O
selectionInput stringInputs İlk veya tek değeri almak için event.common.formInputs.contactType.stringInputs.value[0] Personal
Yalnızca tarihleri kabul eden dateTimePicker. dateInput event.common.formInputs.contactBirthdate.dateInput.msSinceEpoch. 1000425600000

Verileri başka bir karta aktarma

Eklenti olmayan bir sohbet uygulamasında ilk karttan girilen verileri aktarmak için aşağıdaki örnekte gösterildiği gibi, widget'ın name ve kullanıcının girdiği değeri içeren actionParameters ile button widget'ını oluşturun:

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 Komut Dosyası

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

Kullanıcı düğmeyi tıkladığında, eklenti olmayan Chat uygulaması bir CARD_CLICKED etkileşim etkinliği alır. Bu etkinlikten veri alabilirsiniz.

Form gönderimine yanıt verme

Aşağıdaki örnekte, eklenti olmayan bir Chat uygulaması, iletişim kutusu veya kart mesajından gönderilen bir formu başarıyla aldığını onaylamak için kısa mesaj gönderiyor:

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 Komut Dosyası

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

Eklenti olmayan bir Chat uygulamasında iletişim kutusunu işleyip kapatmak için onay mesajı göndermek, orijinal mesajı veya kartı güncellemek ya da yalnızca iletişim kutusunu kapatmak isteyip istemediğinizi belirten bir ActionResponse nesnesi döndürün. Adımlar için İletişim kutusunu kapatma başlıklı makaleyi inceleyin.