Collect and manage contacts in Google Chat

This tutorial shows how to build a Google Chat app that helps Google Chat users manage their personal and business contacts. To collect information, the Chat app prompts users to complete a contact form in card messages and dialogs.

See the Chat app in action:

  • Contact form from slash command.
    Figure 1. The Chat app responds to the slash command /about with a text message and button that opens a contact form.
  • Contact form in a dialog.
    Figure 2. The Chat app opens a dialog where users can input information about a contact.
  • Confirm and review dialog.
    Figure 3. The Chat app returns a confirmation dialog so that users can review and confirm the information before submitting.
  • A text message that confirms the new contact.
    Figure 4. After the user submits the form, the Chat app sends a private text message to confirm the submission.
  • Contact form from a card message.
    Figure 5. The Chat app also prompts users to add a contact from a card in a message.

Prerequisites

Objectives

Architecture

The Chat app is built in Google Apps Script and uses interaction events to process and respond to Chat users.

The following shows how a user might typically interact with the Chat app:

  1. A user opens a direct message with the Chat app, or adds the Chat app to an existing space.

  2. The Chat app prompts the user to add a contact by building and displaying a contact form as a card object. To present the contact form, the Chat app responds to users in the following ways:

    • Responds to @mentions and direct messages with a card message that contains the contact form.
    • Responds to the slash command /addContact by opening a dialog with the contact form.
    • Responds to the slash command /about with a text message that has a Add a contact button that users can click to open a dialog with the contact form.
  3. When presented with the contact form, the user inputs contact information into the following fields and widgets:

    • First and last name: a textInput widget that accepts strings.
    • Birthdate: a dateTimePicker widget that accepts only dates.
    • Contact type: a selectionInput widget of radio buttons that lets users select and submit a single string value (either Personal or Work).
    • Review and submit button: a buttonList array with button widget that the user clicks to submit the values that they input.
  4. The Google Chat app handles a CARD_CLICKED interaction event to process the values that the user inputs, and displays the values in a confirmation card.

  5. The user reviews the confirmation card and clicks the Submit button to finalize the contact information.

  6. The Google Chat app sends a private text message that confirms the submission.

Prepare the environment

This section shows how to create and configure a Google Cloud project for the Chat app.

Create a Google Cloud project

Google Cloud console

  1. In the Google Cloud console, go to Menu > IAM & Admin > Create a Project.

    Go to Create a Project

  2. In the Project Name field, enter a descriptive name for your project.

    Optional: To edit the Project ID, click Edit. The project ID can't be changed after the project is created, so choose an ID that meets your needs for the lifetime of the project.

  3. In the Location field, click Browse to display potential locations for your project. Then, click Select.
  4. Click Create. The Google Cloud console navigates to the Dashboard page and your project is created within a few minutes.

gcloud CLI

In one of the following development environments, access the Google Cloud CLI (gcloud):

  • Cloud Shell: To use an online terminal with the gcloud CLI already set up, activate Cloud Shell.
    Activate Cloud Shell
  • Local Shell: To use a local development environment, install and initialize the gcloud CLI.
    To create a Cloud project, use the gcloud projects create command:
    gcloud projects create PROJECT_ID
    Replace PROJECT_ID by setting the ID for the project you want to create.

Set up authentication and authorization

Google Chat apps require you to configure an OAuth consent screen so that users can authorize your app in Google Workspace applications, including Google Chat.

In this tutorial, you deploy a Chat app that's only for testing and internal use, so it's OK to use placeholder information for the consent screen. Before publishing the Chat app, replace any placeholder information with real information.

  1. In the Google Cloud console, go to Menu > APIs & Services > OAuth consent screen.

    Go to OAuth consent screen

  2. Under User type, select Internal, then click Create.

  3. In App name, type Contact Manager.

  4. In User support email, select your email address or an appropriate Google group.

  5. Under Developer contact information, enter your email address.

  6. Click Save and Continue.

  7. On the Scopes page, click Save and Continue. (The Chat app doesn't require any OAuth scopes.)

  8. Review the summary, and then click Back to Dashboard.

Create and deploy the Chat app

In the following section, you copy and update an entire Apps Script project that contains all the required application code for your Chat app, so there's no need to copy and paste each file.

Optionally, you can view the entire project on GitHub.

View on GitHub

Here's an overview of each file:

main.gs

Handles all app logic, including interaction events about when users send messages to the Chat app, click buttons from a Chat app message, or open and close dialogs.

View main.gs code

apps-script/contact-form-app/main.gs
/**
 * Copyright 2024 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Responds to a MESSAGE interaction event in Google Chat.
 *
 * @param {Object} event the MESSAGE interaction event from Chat API.
 * @return {Object} message response that opens a dialog or sends private
 *                          message with text and card.
 */
function onMessage(event) {
  if (event.message.slashCommand) {
    switch (event.message.slashCommand.commandId) {
      case 1:
        // If the slash command is "/about", responds with a text message and button
        // that opens a dialog.
        return {
          text: "Manage your personal and business contacts 📇. To add a " +
                  "contact, use the slash command `/addContact`.",
          accessoryWidgets: [{ buttonList: { buttons: [{
            text: "Add Contact",
            onClick: { action: {
              function: "openDialog",
              interaction: "OPEN_DIALOG"
            }}
          }]}}]
        }
      case 2:
        // If the slash command is "/addContact", opens a dialog.
        return openDialog(event);
    }
  }

  // If user sends the Chat app a message without a slash command, the app responds
  // privately with a text and card to add a contact.
  return {
    privateMessageViewer: event.user,
    text: "To add a contact, try `/addContact` or complete the form below:",
    cardsV2: [{
      cardId: "addContactForm",
      card: {
        header: { title: "Add a contact" },
        sections:[{ widgets: CONTACT_FORM_WIDGETS.concat([{
          buttonList: { buttons: [{
            text: "Review and submit",
            onClick: { action: { function : "openNextCard" }}
          }]}
        }])}]
      }
    }]
  };
}

/**
 * Responds to CARD_CLICKED interaction events in Google Chat.
 *
 * @param {Object} event the CARD_CLICKED interaction event from Google Chat.
 * @return {Object} message responses specific to the dialog handling.
 */
function onCardClick(event) {
  // Initial dialog form page
  if (event.common.invokedFunction === "openDialog") {
    return openDialog(event);
  // Second dialog form page
  } else if (event.common.invokedFunction === "openNextCard") {
    return openNextCard(
      event.user,
      fetchFormValue(event, "contactName"),
      fetchFormValue(event, "contactBirthdate"),
      fetchFormValue(event, "contactType"),
      event.isDialogEvent
    );
  // Dialog form submission
  } else if (event.common.invokedFunction === "submitForm") {
    const userInputs = event.common.parameters;
    return submitForm(event.user, userInputs, event.dialogEventType);
  }
}

/**
 * Extracts form input value for a given widget.
 *
 * @param {Object} event the CARD_CLICKED interaction event from Google Chat.
 * @param {String} widgetName a unique ID for the widget, specified in the widget's name field.
 * @returns the value inputted by the user, null if no value can be found.
 */
function fetchFormValue(event, widgetName) {
  const formItem = event.common.formInputs[widgetName][""];
  // For widgets that receive StringInputs data, the value input by the user.
  if (formItem.hasOwnProperty("stringInputs")) {
    const stringInput = event.common.formInputs[widgetName][""].stringInputs.value[0];
    if (stringInput != null) {
      return stringInput;
    }
  // For widgets that receive dateInput data, the value input by the user.
  } else if (formItem.hasOwnProperty("dateInput")) {
    const dateInput = event.common.formInputs[widgetName][""].dateInput.msSinceEpoch;
     if (dateInput != null) {
       return dateInput;
     }
  }

  return null;
}

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

/**
 * Returns a dialog or card message that displays a confirmation of contact
 * details before users submit.
 *
 * @param {String} user the user who submitted the information.
 * @param {String} contactName the contact name from the previous dialog or card.
 * @param {String} contactBirthdate the birthdate from the previous dialog or card.
 * @param {String} contactType the contact type from the previous dialog or card.
 * @param {boolean} fromDialog whether the information was submitted from a dialog.
 *
 * @return {Object} returns a dialog or private card message.
 */
function openNextCard(user, contactName, contactBirthdate, contactType, fromDialog) {
  const name = contactName ?? "<i>Not provided</i>";
  const birthdate = contactBirthdate ?? "<i>Not provided</i>";
  const type = contactType ?? "<i>Not provided</i>";
  const cardConfirmation = {
    header: "Your contact",
    widgets: [{
      textParagraph: { text: "Confirm contact information and submit:" }}, {
      textParagraph: { text: "<b>Name:</b> " + name }}, {
      textParagraph: {
        text: "<b>Birthday:</b> " + convertMillisToDateString(birthdate)
      }}, {
      textParagraph: { text: "<b>Type:</b> " + type }}, {
      buttonList: { buttons: [{
        text: "Submit",
        onClick: { action: {
          function: "submitForm",
          parameters: [{
            key: "contactName", value: name }, {
            key: "contactBirthdate", value: birthdate }, {
            key: "contactType", value: type
          }]
        }}
      }]}
    }]
  };

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

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

/**
  * Submits information from a dialog or card message.
  *
  * @param {Object} user the person who submitted the information.
  * @param {Object} userInputs the form input values from event parameters.
  * @param {boolean} dialogEventType "SUBMIT_DIALOG" if from a dialog.
  * @return {Object} a message response that opens a dialog or posts a private
  *                  message.
  */
function submitForm(user, userInputs, dialogEventType) {
  const contactName = userInputs["contactName"];
  // Checks to make sure the user entered a contact name.
  // If no name value detected, returns an error message.
  if (!contactName) {
    const errorMessage = "Don't forget to name your new contact!";
    if (dialogEventType === "SUBMIT_DIALOG") {
      return { actionResponse: {
        type: "DIALOG",
        dialogAction: { actionStatus: {
          statusCode: "INVALID_ARGUMENT",
          userFacingMessage: errorMessage
        }}
      }};
    } else {
      return {
        privateMessageViewer: user,
        text: errorMessage
      };
    }
  }

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

/**
 * Converts date in milliseconds since epoch to user-friendly string.
 *
 * @param {Object} millis the milliseconds since epoch time.
 * @return {string} Display-friend date (English US).
 */
function convertMillisToDateString(millis) {
  const date = new Date(millis);
  const options = { year: 'numeric', month: 'long', day: 'numeric' };
  return date.toLocaleDateString('en-US', options);
}
contactForm.gs

Contains the widgets that receive form data from users. These form input widgets are displayed in cards that appear in messages and dialogs.

View contactForm.gs code

apps-script/contact-form-app/contactForm.gs
/**
 * Copyright 2024 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 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
        }
      ]
    }
  }
];
appsscript.json

The Apps Script manifest that defines and configures the Apps Script project for the Chat app.

View appsscript.json code

apps-script/contact-form-app/appsscript.json
{
  "timeZone": "America/Los_Angeles",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "chat": {}
}

Find your Cloud project number and ID

  1. In the Google Cloud console, go to your Cloud project.

    Go to Google Cloud console

  2. Click Settings and Utilities > Project settings.

  3. Note the values in the Project number and Project ID fields. You use them in the following sections.

Create the Apps Script project

To create an Apps Script project and connect it with your Cloud project:

  1. Click the following button to open the Manage contacts in Google Chat Apps Script project.
    Open the project
  2. Click Overview.
  3. On the overview page, click The icon for making a copy Make a copy.
  4. Name your copy of the Apps Script project:

    1. Click Copy of Manage contacts in Google Chat.

    2. In Project title, type Contact Manager - Google Chat app

    3. Click Rename.

Set the Apps Script project's Cloud project

  1. In your Apps Script project, click The icon for project settings Project Settings.
  2. Under Google Cloud Platform (GCP) Project, click Change project.
  3. In GCP project number, paste the project number of your Cloud project.
  4. Click Set project. The Cloud project and Apps Script project are now connected.

Create an Apps Script deployment

Now that all the code is in place, deploy the Apps Script project. You use the deployment ID when you configure the Chat app in the Google Cloud.

  1. In Apps Script, open the Chat app's project.

    Go to Apps Script

  2. Click Deploy > New deployment.

  3. If Add-on isn't already selected, next to Select type, click deployment types The icon for project settings and select Add-on.

  4. In Description, enter a description for this version, like Test of Contact Manager.

  5. Click Deploy. Apps Script reports successful deployment and provides a deployment ID.

  6. Click Copy to copy the deployment ID and then click Done.

Configure the Chat app in the Google Cloud console

This section shows how to configure the Google Chat API in the Google Cloud console with information about your Chat app, including the ID of the deployment that you just created from your Apps Script project.

  1. In the Google Cloud console, click Menu > More products > Google Workspace > Product Library > Google Chat API > Manage > Configuration.

    Go to Chat API configuration

  2. In App name, type Contact Manager.

  3. In Avatar URL, type https://developers.google.com/chat/images/contact-icon.png.

  4. In Description, type Manage your personal and business contacts.

  5. Click the Enable Interactive features toggle to the on position.

  6. Under Functionality, select the checkboxes Receive 1:1 messages and Join spaces and group conversations.

  7. Under Connection settings, select Apps Script.

  8. In Deployment ID, paste the Apps Script Deployment ID that you copied in the previous section when you created the Apps Script deployment.

  9. Under Slash commands, set up the slash commands /about and /addContact:

    1. Click Add a slash command to set up the first slash command.
    2. In Name, type /about.
    3. In Command ID, type 1.
    4. In Description, type Learn how to use this Chat app to manage your contacts.
    5. Select Opens a dialog.
    6. Click Done.
    7. Click Add a slash command to set up another slash command.
    8. In Name, type /addContact
    9. In Command ID, type 2.
    10. In Description, type Submit information about a contact.
    11. Select Opens a dialog.
    12. Click Done.
  10. Under Visibility, select the Make this Chat app available to specific people and groups in YOUR DOMAIN checkbox and enter your email address.

  11. Under Logs, select Log errors to Logging.

  12. Click Save. A configuration saved message appears.

The Chat app is ready to install and test in Chat.

Test the Chat app

To test your Chat app, open a direct message space with the Chat app and send a message:

  1. Open Google Chat using the Google Workspace account that you provided when you added yourself as a trusted tester.

    Go to Google Chat

  2. Click New chat.
  3. In the Add 1 or more people field, type the name of your Chat app.
  4. Select your Chat app from the results. A direct message opens.

  1. In the new direct message with the Chat app, type /addContactand press enter.

  2. In the dialog that opens, enter contact information:

    1. In the First and last name text field, enter a name.
    2. In the Birthdate date picker, select a date.
    3. Under Contact type, select the Work or Personal radio button.
  3. Click Review and submit.

  4. In the confirmation dialog, review the information that you submitted and click Submit. The Chat app replies with a text message that says CONTACT NAME has been added to your contacts..

  5. Optionally, you can also test and submit the contact form in the following ways:

    • Use the /about slash command. Chat app replies with a text message and an accessory widget button that says Add a contact. You can click the button to open a dialog with the contact form.
    • Send the Chat app a direct message without a slash command, such as Hello. The Chat app replies with a text and card that contains the contact form.

Clean up

To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, we recommend that you delete the Cloud project.

  1. In the Google Cloud console, go to the Manage resources page. Click Menu > IAM & Admin > Manage Resources.

    Go to Resource Manager

  2. In the project list, select the project you want to delete and then click Delete .
  3. In the dialog, type the project ID and then click Shut down to delete the project.