收集及管理 Google Chat 聯絡人

本教學課程將說明如何建構 Google Chat 應用程式,協助 Google Chat 使用者管理個人和業務聯絡人。為了收集資訊,Chat 應用程式會在資訊卡訊息和對話方塊中提示使用者填寫聯絡表單。

查看 Chat 應用程式的實際運作情形:

  • 透過斜線指令傳送的聯絡表單。
    圖 1.Chat 應用程式會回應斜線指令 /about,並傳送文字訊息和按鈕,開啟聯絡人表單。
  • 對話方塊中的聯絡表單。
    圖 2. Chat 應用程式會開啟一個對話方塊,方便使用者輸入聯絡人的相關資訊。
  • 確認和檢查對話方塊。
    圖 3. Chat 應用程式傳回確認對話方塊,方便使用者在提交前查看並確認資訊。
  • 確認新聯絡人的簡訊。
    圖 4. 使用者提交表單後,Chat 應用程式會傳送私人簡訊,確認提交內容。
  • 資訊卡訊息中的聯絡表單。
    圖 5. Chat 應用程式也會提示使用者從訊息中的資訊卡新增聯絡人。

必要條件

目標

架構

Chat 應用程式是使用 Google Apps Script 建構,並使用互動事件來處理及回應 Chat 使用者。

以下是使用者通常與 Chat 應用程式互動的情形:

  1. 使用者開啟與 Chat 應用程式互傳的即時訊息,或將 Chat 應用程式新增至現有聊天室。

  2. Chat 應用程式會建立並顯示聯絡表單,做為 card 物件,提示使用者新增聯絡人。如要呈現聯絡表單,Chat 應用程式會以下列方式回應使用者:

    • 使用內含聯絡表單的資訊卡訊息回覆 @提及和即時訊息。
    • 開啟含有聯絡表單的對話方塊,回應斜線指令 /addContact
    • 回應斜線指令 /about,其中包含含有「Add a contact」按鈕的訊息,使用者可以點選這個按鈕開啟聯絡表單對話方塊。
  3. 提供聯絡表單時,使用者會在下列欄位和小工具中輸入聯絡資訊:

    • 名字和姓氏:接受字串的 textInput 小工具。
    • 出生日期:只接受日期的 dateTimePicker 小工具。
    • 聯絡人類型:圓形按鈕的 selectionInput 小工具,可讓使用者選取及提交單一字串值 (PersonalWork)。
    • 「Review and submit」按鈕:buttonList 陣列,其中包含使用者按下後可提交輸入值的 button 小工具。
  4. Google Chat 應用程式會處理 CARD_CLICKED 互動事件,處理使用者輸入的值,並在確認資訊卡中顯示值。

  5. 使用者查看確認資訊卡,然後按一下「Submit」按鈕以完成聯絡資訊。

  6. Google Chat 應用程式會傳送私人簡訊,確認提交作業。

準備環境

本節說明如何為 Chat 應用程式建立及設定 Google Cloud 專案。

建立 Google Cloud 專案

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,依序前往「選單」 >「IAM 與管理」 >「建立專案」

    前往「建立專案」

  2. 在「Project Name」欄位中,輸入專案的描述性名稱。

    選用步驟:如要編輯「Project ID」,請按一下「Edit」。專案建立後就無法變更專案 ID,因此請選擇符合專案執行期間需求的 ID。

  3. 在「Location」欄位中,按一下「Browse」,即可顯示專案的潛在地點。然後按一下「選取」
  4. 按一下「建立」,Google Cloud 控制台會前往「Dashboard」(資訊主頁) 頁面,並在幾分鐘內建立專案。

gcloud CLI

在下列任一開發環境中,存取 Google Cloud CLI (gcloud):

  • Cloud Shell:如要使用已設定 gcloud CLI 的線上終端機,請啟用 Cloud Shell。
    啟用 Cloud Shell
  • 本機 Shell:如要使用本機開發環境,請安裝初始化 gcloud CLI。
    如要建立 Cloud 專案,請使用 gcloud projects create 指令:
    gcloud projects create PROJECT_ID
    將要建立的專案 ID 設為 PROJECT_ID

設定驗證與授權

Google Chat 應用程式需要您設定 OAuth 同意畫面,讓使用者能夠在 Google Workspace 應用程式 (包括 Google Chat) 中授權您的應用程式。

在本教學課程中,您部署的 Chat 應用程式僅供測試和內部使用,因此可以使用同意畫面的預留位置資訊。發布 Chat 應用程式前,請將所有預留位置資訊替換為實際資訊。

  1. 在 Google Cloud 控制台中,依序前往「選單」>「API 和服務」>「OAuth 同意畫面」

    前往 OAuth 同意畫面

  2. 在「使用者類型」下方選取「內部」,然後點選「建立」

  3. 在「App name」中輸入 Contact Manager

  4. 在「User support email」(使用者支援電子郵件) 中,選取您的電子郵件地址或適當的 Google 群組。

  5. 在「開發人員聯絡資訊」下方輸入電子郵件地址。

  6. 按一下 [儲存並繼續]。

  7. 在「範圍」頁面上,按一下「儲存並繼續」。(Chat 應用程式不需要任何 OAuth 範圍)。

  8. 查看摘要,然後按一下「返回資訊主頁」

建立及部署 Chat 應用程式

在下一節中,您將複製及更新整個 Apps Script 專案,其中包含您 Chat 應用程式所需的所有應用程式程式碼,因此不需要複製及貼上每個檔案。

您也可以選擇在 GitHub 上查看整個專案。

前往 GitHub 查看

以下簡要說明各檔案:

main.gs

處理所有應用程式邏輯,包括有關使用者傳送訊息至 Chat 應用程式的時間、Chat 應用程式訊息中的按鈕,或是開啟及關閉對話方塊的互動事件。

查看 main.gs 程式碼

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: "openInitialDialog",
                interaction: "OPEN_DIALOG"
              }}
            }]}
          }]
        }
      case 2:
        // If the slash command is "/addContact", opens a dialog.
        return openInitialDialog();
    }
  }

  // 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 : "openConfirmation" }}
          }]}
        }])}]
      }
    }]
  };
}

/**
 * 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 === "openInitialDialog") {
    return openInitialDialog();
  // Confirmation dialog form page
  } else if (event.common.invokedFunction === "openConfirmation") {
    return openConfirmation(event);
  // Submission dialog form page
  } else if (event.common.invokedFunction === "submitForm") {
    return submitForm(event);
  }
}

/**
 * Opens the initial step of the dialog that lets users add contact details.
 *
 * @return {Object} a message with an action response to open a dialog.
 */
function openInitialDialog() {
  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: "openConfirmation" }}
        }]}
      }])
    }]}}}
  }};
}

/**
 * Returns the second step as a dialog or card message that lets users confirm details.
 *
 * @param {Object} event the interactive event with form inputs.
 * @return {Object} returns a dialog or private card message.
 */
function openConfirmation(event) {
  const name = fetchFormValue(event, "contactName") ?? "";
  const birthdate = fetchFormValue(event, "contactBirthdate") ?? "";
  const type = fetchFormValue(event, "contactType") ?? "";
  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 (event.isDialogEvent) {
    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: event.user,
    cardsV2: [{
      card: { sections: [cardConfirmation]}
    }]
  }
}

/**
  * Validates and submits information from a dialog or card message
  * and notifies status.
  *
  * @param {Object} event the interactive event with parameters.
  * @return {Object} a message response that opens a dialog or posts a private
  *                  message.
  */
function submitForm(event) {
  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.
  if (!contactName) {
    const errorMessage = "Don't forget to name your new contact!";
    if (event.dialogEventType === "SUBMIT_DIALOG") {
      return { actionResponse: {
        type: "DIALOG",
        dialogAction: { actionStatus: {
          statusCode: "INVALID_ARGUMENT",
          userFacingMessage: errorMessage
        }}
      }};
    } else {
      return {
        privateMessageViewer: event.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 (event.dialogEventType === "SUBMIT_DIALOG") {
    return {
      actionResponse: {
        type: "DIALOG",
        dialogAction: { actionStatus: {
          statusCode: "OK",
          userFacingMessage: "Success " + contactName
        }}
      }
    }
  } else {
    return {
      actionResponse: { type: "NEW_MESSAGE" },
      privateMessageViewer: event.user,
      text: confirmationMessage
    };
  }
}

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

/**
 * 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

包含接收使用者表單資料的小工具。這些表單輸入小工具會顯示在訊息和對話方塊中的資訊卡中。

查看 contactForm.gs 程式碼

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

Apps Script 資訊清單:定義並設定 Chat 應用程式的 Apps Script 專案。

查看 appsscript.json 程式碼

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

找出 Cloud 專案編號和 ID

  1. 在 Google Cloud 控制台中,前往您的 Cloud 專案。

    前往 Google Cloud 控制台

  2. 依序按一下「Settings and Utilities」圖示 >「Project settings」

  3. 記下「專案編號」和「專案 ID」欄位中的值。您會在後續各節中使用這些值。

建立 Apps Script 專案

如要建立 Apps Script 專案並連結至 Cloud 專案,請按照下列步驟操作:

  1. 點選下方按鈕,開啟「在 Google Chat 中管理聯絡人」 Apps Script 專案。
    開啟專案
  2. 按一下 「總覽」
  3. 在總覽頁面中,按一下 建立副本的圖示「建立副本」
  4. 為 Apps 指令碼專案副本命名:

    1. 按一下「在 Google Chat 中管理聯絡人的副本」

    2. 在「Project title」中輸入 Contact Manager - Google Chat app

    3. 按一下 [重新命名]

設定 Apps Script 專案的 Cloud 專案

  1. 在 Apps Script 專案中,按一下 專案設定圖示「Project Settings」
  2. 在「Google Cloud Platform (GCP) 專案」下方,按一下「變更專案」
  3. 在「GCP 專案編號」中,貼上 Cloud 專案的專案編號。
  4. 按一下「設定專案」。Cloud 專案和 Apps Script 專案現已連結。

建立 Apps Script 部署作業

所有程式碼都已就定位後,請部署 Apps Script 專案。在 Google Cloud 中設定 Chat 應用程式時,您會使用部署 ID。

  1. 在 Apps Script 中,開啟 Chat 應用程式的專案。

    前往 Apps Script

  2. 依序按一下「部署」 >「新部署作業」

  3. 如果尚未選取「外掛程式」,請按一下「選取類型」旁的部署類型 專案設定圖示,然後選取「外掛程式」

  4. 在「說明」中輸入這個版本的說明,例如 Test of Contact Manager

  5. 按一下「Deploy」。Apps Script 會回報部署作業成功,並提供部署 ID。

  6. 按一下 「Copy」(複製) 來複製部署作業 ID,然後按一下「Done」(完成)

在 Google Cloud 控制台中設定 Chat 應用程式

本節說明如何在 Google Cloud 控制台中設定 Google Chat API,並提供 Chat 應用程式相關資訊,包括您剛剛透過 Apps Script 專案建立的部署 ID。

  1. 在 Google Cloud 控制台中,依序點選「選單」 >「更多產品」 >「Google Workspace」 >「產品程式庫」 >「Google Chat API」 >「管理」 >「設定」

    前往 Chat API 設定

  2. 在「App name」中輸入 Contact Manager

  3. 在「顯示圖片網址」中輸入 https://developers.google.com/chat/images/contact-icon.png

  4. 在「Description」中輸入 Manage your personal and business contacts

  5. 將「啟用互動功能」切換鈕設為開啟。

  6. 在「功能」下方,勾選「接收一對一訊息」和「加入聊天室和群組對話」核取方塊。

  7. 在「連線設定」下方,選取「Apps Script」

  8. 在「Deployment ID」中,貼上您在建立 Apps Script 部署作業時,在上一節中複製的 Apps Script 部署作業 ID。

  9. 在「斜線指令」下方,設定斜線指令 /about/addContact

    1. 按一下「新增斜線指令」,設定第一個斜線指令。
    2. 在「Name」 中輸入 /about
    3. 在「指令 ID」中輸入 1
    4. 在「Description」中輸入 Learn how to use this Chat app to manage your contacts
    5. 選取「開啟對話方塊」
    6. 按一下 [完成]。
    7. 按一下「新增斜線指令」即可設定其他斜線指令。
    8. 在「名稱」中輸入 /addContact
    9. 在「Command ID」中輸入 2
    10. 在「Description」(說明) 中輸入 Submit information about a contact
    11. 選取「開啟對話方塊」
    12. 按一下 [完成]。
  10. 在「顯示設定」下方,選取「將這個 Chat 擴充應用程式提供給 YOUR DOMAIN 中的特定使用者和群組」核取方塊,然後輸入電子郵件地址。

  11. 在「Logs」下方,選取「Log errors to Logging」

  12. 按一下 [儲存]。系統會顯示已儲存的設定訊息。

即時通訊應用程式已準備就緒,可在即時通訊中安裝及測試。

測試 Chat 應用程式

如要測試 Chat 應用程式,請透過 Chat 應用程式開啟即時訊息聊天室,並傳送訊息:

  1. 使用您新增自己為信任測試人員時提供的 Google Workspace 帳戶,開啟 Google Chat。

    前往 Google Chat

  2. 按一下「新的即時通訊」圖示
  3. 在「新增 1 位或多位使用者」欄位中,輸入 Chat 應用程式的名稱。
  4. 從搜尋結果中選取 Chat 應用程式。即時訊息會隨即開啟。

  1. 在與 Chat 應用程式的新即時訊息中輸入 /addContact,然後按下 Enter 鍵。

  2. 在開啟的對話方塊中輸入聯絡資訊:

    1. 在「姓名」文字欄位中輸入名稱。
    2. 在「出生日期」日期挑選器中選取日期。
    3. 在「聯絡人類型」下方,選取「公司」或「個人」圓形按鈕。
  3. 按一下「檢閱並提交」

  4. 在確認對話方塊中,檢查您提交的資訊,然後按一下「Submit」。Chat 應用程式會回覆文字訊息,內容為 CONTACT NAME has been added to your contacts.

  5. 您也可以選擇以下列方式測試及提交聯絡表單:

    • 使用 /about 斜線指令。Chat 應用程式會以文字訊息和 Add a contact 配件小工具按鈕進行回覆。您可以按一下按鈕,開啟聯絡表單對話方塊。
    • 傳送即時訊息給 Chat 應用程式,但不使用斜線指令 (例如 Hello)。Chat 應用程式會回覆文字和含有聯絡表單的資訊卡。

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程中所用資源的相關費用,建議您刪除 Cloud 專案。

  1. 在 Google Cloud 控制台中前往「管理資源」頁面。依序按一下「選單」圖示 >「IAM 與管理」 >「管理資源」

    前往 Resource Manager

  2. 在專案清單中選取要刪除的專案,然後按一下「Delete」圖示
  3. 在對話方塊中輸入專案 ID,然後按一下「Shut down」(關閉) 即可刪除專案。