收集及管理 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 斜線指令,使用者點選該按鈕即可開啟含有聯絡人表單的對話方塊。
  3. 看到聯絡表單後,使用者會在下列欄位和小工具中輸入聯絡資訊:

    • 名字和姓氏:接受字串的 textInput 小工具。
    • 出生日期:只能輸入日期的 dateTimePicker 小工具。
    • 聯絡人類型selectionInput 圓形按鈕小工具,可供使用者選取並提交單一字串值 (PersonalWork)。
    • 「檢查並提交」按鈕:a buttonList 陣列,內含 button 小工具,使用者點選即可提交輸入的值。
  4. Google Chat 應用程式會處理 CARD_CLICKED 互動事件,以便處理使用者輸入的值,並在確認資訊卡中顯示這些值。

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

  6. Google Chat 應用程式會傳送私人訊息,確認已提交要求。

準備環境

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

建立 Google Cloud 專案

Google Cloud 控制台

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

    前往「建立專案」

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

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

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

gcloud CLI

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

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

設定驗證與授權

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

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

  1. 在 Google Cloud 控制台中,依序前往「選單」圖示 > > 「品牌」

    前往「品牌宣傳」

  2. 如果您已設定,可以在「品牌」、「目標對象」和「資料存取權」中,設定下列 OAuth 同意畫面設定。 如果看到「尚未設定」 訊息,請按一下「開始使用」

    1. 在「App Information」(應用程式資訊) 下方的「App name」(應用程式名稱) 中,輸入 Contact Manager
    2. 在「使用者支援電子郵件」中,選取您的電子郵件地址或適當的 Google 群組。
    3. 點選 [下一步]。
    4. 在「目標對象」下方,選取「內部」。如果無法選取「Internal」(內部),請選取「External」(外部)
    5. 點選 [下一步]。
    6. 在「聯絡資訊」下方,輸入「電子郵件地址」,以便在專案有任何異動時收到通知。
    7. 點選 [下一步]。
    8. 在「完成」下方,詳閱《Google API 服務使用者資料政策》,然後選取「我同意《Google API 服務:使用者資料政策》」
    9. 按一下「繼續」
    10. 點選「建立」
    11. 如果為使用者類型選取「外部」,請新增測試使用者:
      1. 按一下「目標對象」
      2. 在「測試使用者」下方,按一下「新增使用者」
      3. 輸入您的電子郵件地址和任何其他授權測試使用者,然後按一下「儲存」

建立及部署 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.
  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
      }}
    }};
  }
  if (!contactName) {
    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
        }}
      }
    };
  }
  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

定義及設定 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. 依序點選「設定和公用程式」 >「專案設定」

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

建立 Apps Script 專案

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

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

    1. 按一下「Copy of Manage contacts in Google Chat」(Google Chat 中的管理聯絡人副本)

    2. 在「專案標題」中輸入 Contact Manager - Google Chat app

    3. 按一下 [重新命名]

設定 Apps Script 專案的 Cloud 專案

  1. 在 Apps Script 專案中,按一下 專案設定圖示「專案設定」
  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. 依序按一下「Deploy」(部署) >「New deployment」(新部署作業)

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

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

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

  6. 按一下「複製」圖示 複製部署 ID,然後按一下「完成」

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

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

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

    前往 Chat API 設定

  2. 在「應用程式名稱」中輸入 Contact Manager

  3. 在「Avatar URL」中輸入 https://developers.google.com/chat/images/contact-icon.png

  4. 在「Description」(說明) 中輸入 Manage your personal and business contacts

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

  6. 在「功能」下方,選取「加入聊天室和群組對話」

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

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

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

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

  11. 在「記錄」下方,選取「將錯誤記錄至 Logging」

  12. 按一下 [儲存]。畫面上會顯示「已儲存設定」訊息。

您可以在 Chat 中安裝及測試 Chat 應用程式。

測試 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. 在確認對話方塊中,查看您提交的資訊,然後按一下「提交」。Chat 應用程式會回覆「CONTACT NAME has been added to your contacts.」的訊息。

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

    • 使用 /about 斜線指令。即時通訊應用程式會回覆文字訊息,以及顯示「Add a contact」的配件小工具按鈕。按一下這個按鈕,即可開啟內含聯絡表單的對話方塊。
    • 傳送即時訊息給 Chat 應用程式,不必使用斜線指令,例如 Hello。Chat 應用程式會回覆文字和含有聯絡表單的資訊卡。

清除所用資源

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

  1. 在 Google Cloud 控制台中,前往「管理資源」頁面。依序點選「選單」「IAM 與管理」「管理資源」

    前往 Resource Manager

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