ทำให้อีเมลใช้งานได้จริงยิ่งขึ้นด้วยส่วนเสริมของ Google Workspace

1. ภาพรวม

ในโค้ดแล็บนี้ คุณจะได้ใช้ Google Apps Script เพื่อเขียนส่วนเสริม Google Workspace สำหรับ Gmail ที่ช่วยให้ผู้ใช้เพิ่มข้อมูลใบเสร็จจากอีเมลไปยังสเปรดชีตได้โดยตรงภายใน Gmail เมื่อผู้ใช้ได้รับใบเสร็จทางอีเมล ผู้ใช้จะเปิดส่วนเสริมซึ่งจะดึงข้อมูลค่าใช้จ่ายที่เกี่ยวข้องจากอีเมลโดยอัตโนมัติ ผู้ใช้สามารถแก้ไขข้อมูลค่าใช้จ่าย แล้วส่งเพื่อบันทึกค่าใช้จ่ายลงในสเปรดชีต Google ชีต

สิ่งที่คุณจะได้เรียนรู้

  • สร้างส่วนเสริม Google Workspace สำหรับ Gmail โดยใช้ Google Apps Script
  • แยกวิเคราะห์อีเมลด้วย Google Apps Script
  • โต้ตอบกับ Google ชีตผ่าน Google Apps Script
  • จัดเก็บค่าของผู้ใช้โดยใช้บริการพร็อพเพอร์ตี้ของ Google Apps Script

สิ่งที่คุณต้องมี

  • สิทธิ์เข้าถึงอินเทอร์เน็ตและเว็บเบราว์เซอร์
  • บัญชี Google
  • ข้อความบางอย่างใน Gmail โดยเฉพาะใบเสร็จทางอีเมล

2. รับโค้ดตัวอย่าง

ขณะที่คุณทำตาม Codelab นี้ การอ้างอิงโค้ดเวอร์ชันที่ใช้งานได้ซึ่งคุณจะเขียนอาจเป็นประโยชน์ ที่เก็บ GitHub มีตัวอย่างโค้ดที่คุณใช้เป็นข้อมูลอ้างอิงได้

หากต้องการรับโค้ดตัวอย่าง ให้เรียกใช้คำสั่งต่อไปนี้จากบรรทัดคำสั่ง

git clone https://github.com/googleworkspace/gmail-add-on-codelab.git

3. สร้างส่วนเสริมพื้นฐาน

เริ่มด้วยการเขียนโค้ดสำหรับส่วนเสริมเวอร์ชันง่ายๆ ที่แสดงแบบฟอร์มค่าใช้จ่ายข้างอีเมล

ก่อนอื่นให้สร้างโปรเจ็กต์ Apps Script ใหม่และเปิดไฟล์ Manifest

  1. ไปที่ script.google.com จากที่นี่ คุณจะสร้าง จัดการ และตรวจสอบโปรเจ็กต์ Apps Script ได้
  2. หากต้องการสร้างโปรเจ็กต์ใหม่ ให้คลิกโปรเจ็กต์ใหม่ที่ด้านซ้ายบน โปรเจ็กต์ใหม่จะเปิดขึ้นพร้อมไฟล์เริ่มต้นชื่อ Code.gs ปล่อยให้ Code.gs อยู่เฉยๆ ก่อน แล้วค่อยจัดการในภายหลัง
  3. คลิกโปรเจ็กต์ที่ไม่มีชื่อ ตั้งชื่อโปรเจ็กต์เป็น Expense It! แล้วคลิกเปลี่ยนชื่อ
  4. คลิกการตั้งค่าโปรเจ็กต์ การตั้งค่าโปรเจ็กต์ทางด้านซ้าย
  5. เลือกช่องทำเครื่องหมายแสดงไฟล์ Manifest "appscript.json" ในเครื่องมือแก้ไข"
  6. คลิกตัดต่อวิดีโอ ผู้แก้ไข
  7. หากต้องการเปิดไฟล์ Manifest ให้คลิก appscript.json ทางด้านซ้าย

ใน appscript.json ให้ระบุข้อมูลเมตาที่เชื่อมโยงกับส่วนเสริม เช่น ชื่อและสิทธิ์ที่ต้องใช้ แทนที่เนื้อหาของ appsscript.json ด้วยการตั้งค่าการกำหนดค่าต่อไปนี้

{
  "timeZone": "GMT",
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute"
  ],
  "gmail": {
    "name": "Expense It!",
    "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
    "contextualTriggers": [{
      "unconditional": {
      },
      "onTriggerFunction": "getContextualAddOn"
    }],
    "primaryColor": "#41f470",
    "secondaryColor": "#94f441"
  }
}

โปรดให้ความสนใจเป็นพิเศษกับส่วนของไฟล์ Manifest ที่เรียกว่า contextualTriggers ส่วนนี้ของไฟล์ Manifest จะระบุฟังก์ชันที่ผู้ใช้กำหนดเพื่อเรียกใช้เมื่อเปิดใช้งานส่วนเสริมเป็นครั้งแรก ในกรณีนี้ ฟังก์ชันจะเรียกใช้ getContextualAddOn ซึ่งจะรับรายละเอียดเกี่ยวกับอีเมลที่เปิดอยู่และแสดงชุดการ์ดเพื่อแสดงต่อผู้ใช้

หากต้องการสร้างฟังก์ชัน getContextualAddOn ให้ทำตามขั้นตอนต่อไปนี้

  1. ทางด้านซ้าย ให้วางเมาส์เหนือ Code.gs แล้วคลิกเมนู เมนูเพิ่มเติม > เปลี่ยนชื่อ
  2. พิมพ์ GetContextualAddOn แล้วกดแป้น Enter Apps Script จะต่อท้าย .gs ในชื่อไฟล์โดยอัตโนมัติ คุณจึงไม่จำเป็นต้องพิมพ์นามสกุลไฟล์ หากคุณพิมพ์ GetContextualAddOn.gs Apps Script จะตั้งชื่อไฟล์เป็น GetContextualAddOn.gs.gs
  3. ใน GetContextualAddOn.gs ให้แทนที่โค้ดเริ่มต้นด้วยฟังก์ชัน getContextualAddOn ดังนี้
/**
 * Returns the contextual add-on data that should be rendered for
 * the current e-mail thread. This function satisfies the requirements of
 * an 'onTriggerFunction' and is specified in the add-on's manifest.
 *
 * @param {Object} event Event containing the message ID and other context.
 * @returns {Card[]}
 */
function getContextualAddOn(event) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));

  var section = CardService.newCardSection();
  section.addWidget(CardService.newTextInput()
    .setFieldName('Date')
    .setTitle('Date'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Amount')
    .setTitle('Amount'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Description')
    .setTitle('Description'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Spreadsheet URL')
    .setTitle('Spreadsheet URL'));

  card.addSection(section);

  return [card.build()];
}

อินเทอร์เฟซผู้ใช้ของส่วนเสริม Google Workspace ทุกส่วนเสริมประกอบด้วยการ์ดที่แบ่งออกเป็นส่วนเดียวหรือหลายส่วน โดยแต่ละส่วนจะมีวิดเจ็ตที่แสดงและรับข้อมูลจากผู้ใช้ได้ ฟังก์ชัน getContextualAddOn จะสร้างการ์ดเดียวซึ่งมีรายละเอียดเกี่ยวกับค่าใช้จ่ายที่พบในอีเมล การ์ดมีส่วนเดียวซึ่งมีช่องป้อนข้อความสำหรับข้อมูลที่เกี่ยวข้อง ฟังก์ชันจะแสดงผลอาร์เรย์ของการ์ดของส่วนเสริม ในกรณีนี้ อาร์เรย์ที่แสดงผลจะมีเพียงการ์ดเดียว

ก่อนที่จะติดตั้งใช้งานส่วนเสริม Expense It! คุณต้องมีโปรเจ็กต์ Google Cloud Platform (GCP) ซึ่งโปรเจ็กต์ Apps Script ใช้เพื่อจัดการการให้สิทธิ์ บริการขั้นสูง และรายละเอียดอื่นๆ ดูข้อมูลเพิ่มเติมได้ที่โปรเจ็กต์ Google Cloud Platform

หากต้องการติดตั้งใช้งานและเรียกใช้ส่วนเสริม ให้ทำตามขั้นตอนต่อไปนี้

  1. เปิดโปรเจ็กต์ GCP แล้วคัดลอกหมายเลขโปรเจ็กต์
  2. จากโปรเจ็กต์ Apps Script ให้คลิกการตั้งค่าโปรเจ็กต์ การตั้งค่าโปรเจ็กต์ทางด้านซ้าย
  3. ในส่วน "โปรเจ็กต์ Google Cloud Platform (GCP)" ให้คลิกเปลี่ยนโปรเจ็กต์
  4. ป้อนหมายเลขโปรเจ็กต์ของโปรเจ็กต์ GCP แล้วคลิกตั้งค่าโปรเจ็กต์
  5. คลิกทำให้ใช้งานได้> การทดสอบการติดตั้งใช้งาน
  6. ตรวจสอบว่าประเภทการทำให้ใช้งานได้คือส่วนเสริมของ Google Workspace หากจำเป็น ให้คลิกเปิดใช้ประเภทการทำให้ใช้งานได้ เปิดใช้ประเภทการทำให้ใช้งานได้ ที่ด้านบนของกล่องโต้ตอบ แล้วเลือกส่วนเสริมของ Google Workspace เป็นประเภทการทำให้ใช้งานได้
  7. คลิกติดตั้งข้างแอปพลิเคชัน: Gmail
  8. คลิกเสร็จสิ้น

ตอนนี้คุณจะเห็นส่วนเสริมในกล่องจดหมาย Gmail แล้ว

  1. เปิด Gmail ในคอมพิวเตอร์
  2. ในแผงด้านขวา ให้คลิก Expense It! ส่วนเสริม ไอคอนใบเสร็จของ Expense It! จะปรากฏขึ้น คุณอาจต้องคลิกส่วนเสริมเพิ่มเติม ส่วนเสริมอื่นๆ เพื่อค้นหาส่วนเสริม
  3. เปิดอีเมล โดยควรเป็นใบเสร็จที่มีค่าใช้จ่าย
  4. หากต้องการเปิดส่วนเสริม ให้คลิก Expense It! ในแผงด้านข้างทางขวา ไอคอนใบเสร็จของ Expense It!
  5. ให้สิทธิ์เข้าถึงบัญชี Google แก่ Expense It! โดยคลิกให้สิทธิ์เข้าถึง แล้วทำตามข้อความแจ้ง

ส่วนเสริมจะแสดงแบบฟอร์มง่ายๆ ข้างข้อความ Gmail ที่เปิดอยู่ ตอนนี้ยังไม่มีฟังก์ชันอื่น แต่คุณจะสร้างฟังก์ชันการทำงานของปุ่มนี้ในส่วนถัดไป

หากต้องการดูการอัปเดตส่วนเสริมขณะที่คุณทำแล็บนี้ต่อไป คุณเพียงแค่ต้องบันทึกโค้ดและรีเฟรช Gmail โดยไม่จำเป็นต้องติดตั้งใช้งานเพิ่มเติม

4. เข้าถึงข้อความอีเมล

เพิ่มโค้ดที่ดึงเนื้อหาอีเมลและแยกโค้ดออกเป็นโมดูลเพื่อให้จัดระเบียบได้ดียิ่งขึ้น

คลิกเพิ่ม เพิ่มไฟล์ > สคริปต์ข้างไฟล์ แล้วสร้างไฟล์ชื่อ Cards สร้างไฟล์สคริปต์ที่ 2 ชื่อ Helpers Cards.gs สร้างการ์ดและใช้ฟังก์ชันจาก Helpers.gs เพื่อป้อนข้อมูลในช่องในแบบฟอร์มตามเนื้อหาของอีเมล

แทนที่โค้ดเริ่มต้นใน Cards.gs ด้วยโค้ดนี้

var FIELDNAMES = ['Date', 'Amount', 'Description', 'Spreadsheet URL'];

/**
 * Creates the main card users see with form inputs to log expenses.
 * Form can be prefilled with values.
 *
 * @param {String[]} opt_prefills Default values for each input field.
 * @param {String} opt_status Optional status displayed at top of card.
 * @returns {Card}
 */
function createExpensesCard(opt_prefills, opt_status) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
  
  if (opt_status) {
    if (opt_status.indexOf('Error: ') == 0) {
      opt_status = '<font color=\'#FF0000\'>' + opt_status + '</font>';
    } else {
      opt_status = '<font color=\'#228B22\'>' + opt_status + '</font>';
    }
    var statusSection = CardService.newCardSection();
    statusSection.addWidget(CardService.newTextParagraph()
      .setText('<b>' + opt_status + '</b>'));
    card.addSection(statusSection);
  }
  
  var formSection = createFormSection(CardService.newCardSection(),
                                      FIELDNAMES, opt_prefills);
  card.addSection(formSection);
  
  return card;
}

/**
 * Creates form section to be displayed on card.
 *
 * @param {CardSection} section The card section to which form items are added.
 * @param {String[]} inputNames Names of titles for each input field.
 * @param {String[]} opt_prefills Default values for each input field.
 * @returns {CardSection}
 */
function createFormSection(section, inputNames, opt_prefills) {
  for (var i = 0; i < inputNames.length; i++) {
    var widget = CardService.newTextInput()
      .setFieldName(inputNames[i])
      .setTitle(inputNames[i]);
    if (opt_prefills && opt_prefills[i]) {
      widget.setValue(opt_prefills[i]);
    }
    section.addWidget(widget);
  }
  return section;
}

ฟังก์ชัน createExpensesCard จะรับอาร์เรย์ของค่าเพื่อกรอกข้อมูลในแบบฟอร์มล่วงหน้าเป็นอาร์กิวเมนต์ที่ไม่บังคับ ฟังก์ชันสามารถแสดงข้อความสถานะที่ไม่บังคับ ซึ่งจะเป็นสีแดงหากสถานะขึ้นต้นด้วย "ข้อผิดพลาด:" และจะเป็นสีเขียวในกรณีอื่นๆ ฟังก์ชันช่วยที่ชื่อ createFormSection จะวนซ้ำกระบวนการสร้างวิดเจ็ตอินพุตข้อความ ตั้งค่าเริ่มต้นแต่ละค่าด้วย setValue แล้วเพิ่มวิดเจ็ตลงในส่วนที่เกี่ยวข้องในการ์ดแทนที่จะเพิ่มแต่ละฟิลด์ลงในแบบฟอร์มด้วยตนเอง

ตอนนี้ ให้แทนที่โค้ดเริ่มต้นใน Helpers.gs ด้วยโค้ดนี้

/**
 * Finds largest dollar amount from email body.
 * Returns null if no dollar amount is found.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getLargestAmount(message) {
  return 'TODO';
}

/**
 * Determines date the email was received.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getReceivedDate(message) {
  return 'TODO';
}

/**
 * Determines expense description by joining sender name and message subject.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getExpenseDescription(message) {
  return 'TODO';
}

/**
 * Determines most recent spreadsheet URL.
 * Returns null if no URL was previously submitted.
 *
 * @returns {String}
 */
function getSheetUrl() {
  return 'TODO';
}

Helpers.gs จะเรียกใช้ฟังก์ชันใน getContextualAddon เพื่อกำหนดค่าที่กรอกไว้ล่วงหน้าในแบบฟอร์ม ในตอนนี้ ฟังก์ชันเหล่านี้จะแสดงผลสตริง "TODO" เท่านั้น เนื่องจากคุณจะใช้ตรรกะการป้อนข้อมูลล่วงหน้าในขั้นตอนถัดไป

จากนั้นอัปเดตโค้ดใน GetContextualAddon.gs เพื่อให้ใช้ประโยชน์จากโค้ดใน Cards.gs และ Helpers.gs แทนที่โค้ดใน GetContextualAddon.gs ด้วยโค้ดนี้

/**
 * Copyright 2017 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
 *
 *   https://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.
 */

/**
 * Returns the contextual add-on data that should be rendered for
 * the current e-mail thread. This function satisfies the requirements of
 * an 'onTriggerFunction' and is specified in the add-on's manifest.
 *
 * @param {Object} event Event containing the message ID and other context.
 * @returns {Card[]}
 */
function getContextualAddOn(event) {
  var message = getCurrentMessage(event);
  var prefills = [getReceivedDate(message),
                  getLargestAmount(message),
                  getExpenseDescription(message),
                  getSheetUrl()];
  var card = createExpensesCard(prefills);

  return [card.build()];
}

/**
 * Retrieves the current message given an action event object.
 * @param {Event} event Action event object
 * @return {Message}
 */
function getCurrentMessage(event) {
  var accessToken = event.messageMetadata.accessToken;
  var messageId = event.messageMetadata.messageId;
  GmailApp.setCurrentMessageAccessToken(accessToken);
  return GmailApp.getMessageById(messageId);
}

โปรดสังเกตฟังก์ชัน getCurrentMessage ใหม่ ซึ่งใช้เหตุการณ์ที่ Gmail จัดหาให้เพื่ออ่านข้อความที่ผู้ใช้เปิดอยู่ หากต้องการให้ฟังก์ชันนี้ทำงานได้ ให้เพิ่มขอบเขตเพิ่มเติมลงในไฟล์ Manifest ของสคริปต์ที่อนุญาตให้เข้าถึงข้อความ Gmail แบบอ่านอย่างเดียว

ใน appscript.json ให้อัปเดต oauthScopes เพื่อให้ขอขอบเขต https://www.googleapis.com/auth/gmail.addons.current.message.readonly ด้วย

"oauthScopes": [
  "https://www.googleapis.com/auth/gmail.addons.execute",
   "https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
],

ใน Gmail ให้เรียกใช้ส่วนเสริมและให้สิทธิ์เข้าถึง Expense It! เพื่อดูข้อความอีเมล ตอนนี้ระบบจะกรอกข้อมูลในช่องของแบบฟอร์มล่วงหน้าด้วย "TODO"

5. โต้ตอบกับ Google ชีต

ส่วนเสริม Expense It! มีแบบฟอร์มให้ผู้ใช้ป้อนรายละเอียดเกี่ยวกับค่าใช้จ่าย แต่ไม่มีที่เก็บรายละเอียดเหล่านั้น เรามาเพิ่มปุ่มที่จะส่งข้อมูลแบบฟอร์มไปยัง Google ชีตกัน

หากต้องการเพิ่มปุ่ม เราจะใช้คลาส ButtonSet หากต้องการเชื่อมต่อกับ Google ชีต เราจะใช้บริการ Google ชีต

แก้ไข createFormSection เพื่อแสดงปุ่มที่มีป้ายกำกับว่า "ส่ง" เป็นส่วนหนึ่งของส่วนแบบฟอร์มของการ์ด โปรดทำตามขั้นตอนต่อไปนี้

  1. สร้างปุ่มข้อความโดยใช้ CardService.newTextButton() ติดป้ายกำกับปุ่มว่า "ส่ง" โดยใช้ CardService.TextButton.setText()
  2. ออกแบบปุ่มเพื่อให้เมื่อคลิกแล้ว ระบบจะเรียกใช้submitFormการดำเนินการต่อไปนี้ผ่าน CardService.TextButton.setOnClickAction()
/**
 * Logs form inputs into a spreadsheet given by URL from form.
 * Then displays edit card.
 *
 * @param {Event} e An event object containing form inputs and parameters.
 * @returns {Card}
 */
function submitForm(e) {
  var res = e['formInput'];
  try {
    FIELDNAMES.forEach(function(fieldName) {
      if (! res[fieldName]) {
        throw 'incomplete form';
      }
    });
    var sheet = SpreadsheetApp
      .openByUrl((res['Spreadsheet URL']))
      .getActiveSheet();
    sheet.appendRow(objToArray(res, FIELDNAMES.slice(0, FIELDNAMES.length - 1)));
    return createExpensesCard(null, 'Logged expense successfully!').build();
  }
  catch (err) {
    if (err == 'Exception: Invalid argument: url') {
      err = 'Invalid URL';
      res['Spreadsheet URL'] = null;
    }
    return createExpensesCard(objToArray(res, FIELDNAMES), 'Error: ' + err).build();
  }
}

/**
 * Returns an array corresponding to the given object and desired ordering of keys.
 *
 * @param {Object} obj Object whose values will be returned as an array.
 * @param {String[]} keys An array of key names in the desired order.
 * @returns {Object[]}
 */
function objToArray(obj, keys) {
  return keys.map(function(key) {
    return obj[key];
  });
}
  1. สร้างวิดเจ็ตชุดปุ่มโดยใช้ CardService.newButtonSet() แล้วเพิ่มปุ่มข้อความลงในชุดปุ่มด้วย CardService.ButtonSet.addButton()
  2. เพิ่มวิดเจ็ตชุดปุ่มลงในส่วนแบบฟอร์มของบัตรโดยใช้ CardService.CardSection.addWidget()

เพียงใช้โค้ดไม่กี่บรรทัด เราก็สามารถเปิดสเปรดชีตด้วย URL แล้วต่อท้ายแถวของข้อมูลในชีตนั้นได้ โปรดทราบว่าระบบจะส่งอินพุตของแบบฟอร์มไปยังฟังก์ชันเป็นส่วนหนึ่งของเหตุการณ์ e และเราจะตรวจสอบว่าผู้ใช้ได้ระบุทุกช่องแล้ว หากไม่มีข้อผิดพลาด เราจะสร้างบัตรค่าใช้จ่ายที่ว่างเปล่าซึ่งมีสถานะเป็น "พร้อมใช้งาน" ในกรณีที่เราพบข้อผิดพลาด เราจะส่งคืนการ์ดที่กรอกข้อมูลเดิมพร้อมกับข้อความแสดงข้อผิดพลาด ฟังก์ชันตัวช่วย objToArray ช่วยให้แปลงคำตอบของแบบฟอร์มเป็นอาร์เรย์ได้ง่ายขึ้น ซึ่งจากนั้นจะนำไปต่อท้ายสเปรดชีตได้

สุดท้าย ให้อัปเดตส่วน oauthScopes ใน appsscript.json อีกครั้งเพื่อขอขอบเขต https://www.googleapis.com/auth/spreadsheets เมื่อได้รับอนุญาต ขอบเขตนี้จะช่วยให้ส่วนเสริมอ่านและแก้ไข Google ชีตของผู้ใช้ได้

"oauthScopes": [
  "https://www.googleapis.com/auth/gmail.addons.execute",
  "https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
  "https://www.googleapis.com/auth/spreadsheets"
],

หากยังไม่ได้สร้างสเปรดชีตใหม่ ให้สร้างที่ https://docs.google.com/spreadsheets/

ตอนนี้ให้เรียกใช้ส่วนเสริมอีกครั้งแล้วลองส่งแบบฟอร์ม ตรวจสอบว่าคุณป้อน URL แบบเต็มของ URL ปลายทางลงในช่องแบบฟอร์ม URL ของสเปรดชีต

6. จัดเก็บค่าด้วยบริการพร็อพเพอร์ตี้

บ่อยครั้งที่ผู้ใช้จะบันทึกค่าใช้จ่ายจำนวนมากลงในสเปรดชีตเดียวกัน ดังนั้นการเสนอ URL ของสเปรดชีตล่าสุดเป็นค่าเริ่มต้นในบัตรจึงสะดวก เราจะต้องจัดเก็บข้อมูลดังกล่าวทุกครั้งที่มีการใช้ส่วนเสริมเพื่อให้ทราบ URL ของสเปรดชีตล่าสุด

บริการพร็อพเพอร์ตี้ช่วยให้เราจัดเก็บคู่คีย์-ค่าได้ ในกรณีของเรา คีย์ที่เหมาะสมคือ "SPREADSHEET_URL" ส่วนค่าคือ URL เอง หากต้องการจัดเก็บค่าดังกล่าว คุณจะต้องแก้ไข submitForm ใน Cards.gs เพื่อให้ระบบจัดเก็บ URL ของสเปรดชีตเป็นพร็อพเพอร์ตี้เมื่อต่อท้ายแถวใหม่ในชีต

โปรดทราบว่าพร็อพเพอร์ตี้มีขอบเขต 3 อย่าง ได้แก่ สคริปต์ ผู้ใช้ หรือเอกสาร ขอบเขตเอกสารใช้ไม่ได้กับส่วนเสริม Gmail แม้ว่าจะเกี่ยวข้องกับส่วนเสริมอีกประเภทหนึ่งเมื่อจัดเก็บข้อมูลที่เฉพาะเจาะจงสำหรับ Google เอกสารหรือชีตใดชีตหนึ่ง สำหรับส่วนเสริมของเรา พฤติกรรมที่ต้องการคือให้แต่ละบุคคลเห็นสเปรดชีตล่าสุดของตนเอง (ไม่ใช่ของคนอื่น) เป็นตัวเลือกเริ่มต้นในแบบฟอร์ม ดังนั้นเราจึงเลือกขอบเขตผู้ใช้แทนขอบเขตสคริปต์

ใช้ PropertiesService.getUserProperties().setProperty() เพื่อจัดเก็บ URL ของสเปรดชีต เพิ่มรายการต่อไปนี้ไปยัง submitForm ใน Cards.gs

PropertiesService.getUserProperties().setProperty('SPREADSHEET_URL', 
    res['Spreadsheet URL']);

จากนั้นแก้ไขgetSheetUrlฟังก์ชันใน Helpers.gs เพื่อแสดงพร็อพเพอร์ตี้ที่จัดเก็บไว้เพื่อให้ผู้ใช้เห็น URL ล่าสุดทุกครั้งที่ใช้ส่วนเสริม ใช้ PropertiesService.getUserProperties().getProperty() เพื่อรับค่าของพร็อพเพอร์ตี้

/**
 * Determines most recent spreadsheet URL.
 * Returns null if no URL was previously submitted.
 *
 * @returns {String}
 */
function getSheetUrl() {
  return PropertiesService.getUserProperties().getProperty('SPREADSHEET_URL');
}

สุดท้ายนี้ สคริปต์จะต้องได้รับอนุญาตด้วยจึงจะเข้าถึงบริการพร็อพเพอร์ตี้ได้ เพิ่มขอบเขต https://www.googleapis.com/auth/script.storage ลงในไฟล์ Manifest เหมือนเดิมเพื่อให้ส่วนเสริมอ่านและเขียนข้อมูลพร็อพเพอร์ตี้ได้

7. แยกวิเคราะห์ข้อความ Gmail

เราจะกรอกข้อมูลที่เกี่ยวข้องเกี่ยวกับค่าใช้จ่ายจากอีเมลลงในแบบฟอร์มล่วงหน้าเพื่อช่วยประหยัดเวลาของผู้ใช้ เราได้สร้างฟังก์ชันใน Helpers.gs ที่มีบทบาทนี้แล้ว แต่จนถึงตอนนี้เราได้แสดงผล "TODO" สำหรับวันที่ จำนวนเงิน และคำอธิบายของค่าใช้จ่ายเท่านั้น

เช่น เราสามารถรับวันที่ที่ได้รับอีเมลและใช้เป็นค่าเริ่มต้นสำหรับวันที่ของค่าใช้จ่ายได้

/**
 * Determines date the email was received.
 *
 * @param {Message} message - The message currently open.
 * @returns {String}
 */
function getReceivedDate(message) {
  return message.getDate().toLocaleDateString();
}

ติดตั้งใช้งานฟังก์ชันที่เหลืออีก 2 ฟังก์ชัน

  1. getExpenseDescription อาจต้องรวมทั้งชื่อผู้ส่งและหัวเรื่องของข้อความเข้าด้วยกัน แม้ว่าจะมีวิธีที่ซับซ้อนกว่าในการแยกวิเคราะห์เนื้อหาของข้อความและแสดงคำอธิบายที่แม่นยำยิ่งขึ้น
  2. สำหรับ getLargestAmount ให้ลองมองหาสัญลักษณ์ที่เกี่ยวข้องกับเงินโดยเฉพาะ ใบเสร็จมักจะมีค่าหลายรายการ เช่น ภาษีและค่าธรรมเนียมอื่นๆ ลองคิดดูว่าคุณจะระบุจำนวนเงินที่ถูกต้องได้อย่างไร นิพจน์ทั่วไปอาจมีประโยชน์เช่นกัน

หากต้องการแรงบันดาลใจเพิ่มเติม โปรดดูเอกสารอ้างอิงสำหรับ GmailMessage หรือดูโค้ดโซลูชันที่คุณดาวน์โหลดไว้ตอนต้นของ Codelab เมื่อคิดการติดตั้งใช้งานฟังก์ชันทั้งหมดใน Helpers.gs ได้แล้ว ก็ลองใช้ส่วนเสริมของคุณได้เลย เปิดใบเสร็จและเริ่มบันทึกในสเปรดชีต

8. ล้างแบบฟอร์มด้วยการดำเนินการกับการ์ด

จะเกิดอะไรขึ้นหาก Expense It! ระบุค่าใช้จ่ายในอีเมลที่เปิดอยู่ผิดและกรอกข้อมูลในแบบฟอร์มล่วงหน้าด้วยข้อมูลที่ไม่ถูกต้อง ผู้ใช้ล้างแบบฟอร์ม คลาส CardAction ช่วยให้เราสามารถระบุฟังก์ชันที่จะเรียกใช้เมื่อมีการคลิกการดำเนินการ มาใช้ฟีเจอร์นี้เพื่อให้ผู้ใช้ล้างแบบฟอร์มได้อย่างรวดเร็วกัน

แก้ไข createExpensesCard เพื่อให้การ์ดที่แสดงผลมีการดำเนินการของการ์ดที่มีป้ายกำกับว่า "ล้างแบบฟอร์ม" และเมื่อคลิกแล้วจะเรียกใช้ฟังก์ชัน clearForm ต่อไปนี้ ซึ่งคุณสามารถวางลงใน Cards.gs ได้ คุณจะต้องส่ง opt_status เป็นพารามิเตอร์ชื่อ "สถานะ" ไปยังการดำเนินการเพื่อให้มั่นใจว่าเมื่อล้างแบบฟอร์มแล้ว ข้อความสถานะจะยังคงอยู่ โปรดทราบว่าพารามิเตอร์ที่ไม่บังคับสำหรับการดำเนินการต้องเป็นประเภท Object.<string, string> ดังนั้นหาก opt_status ไม่พร้อมใช้งาน คุณควรส่ง {'Status' : ''}

/**
 * Recreates the main card without prefilled data.
 *
 * @param {Event} e An event object containing form inputs and parameters.
 * @returns {Card}
 */
function clearForm(e) {
  return createExpensesCard(null, e['parameters']['Status']).build();
}

9. สร้างสเปรดชีต

นอกเหนือจากการใช้ Google Apps Script เพื่อแก้ไขสเปรดชีตที่มีอยู่แล้ว คุณยังสร้างสเปรดชีตใหม่ทั้งหมดแบบเป็นโปรแกรมได้ด้วย สำหรับส่วนเสริมของเรา เราจะอนุญาตให้ผู้ใช้สร้างสเปรดชีตสำหรับค่าใช้จ่าย หากต้องการเริ่มต้น ให้เพิ่มส่วนการ์ดต่อไปนี้ลงในการ์ดที่ createExpensesCard แสดง

var newSheetSection = CardService.newCardSection();
var sheetName = CardService.newTextInput()
  .setFieldName('Sheet Name')
  .setTitle('Sheet Name');
var createExpensesSheet = CardService.newAction()
  .setFunctionName('createExpensesSheet');
var newSheetButton = CardService.newTextButton()
  .setText('New Sheet')
  .setOnClickAction(createExpensesSheet);
newSheetSection.addWidget(sheetName);
newSheetSection.addWidget(CardService.newButtonSet().addButton(newSheetButton));
card.addSection(newSheetSection);

ตอนนี้เมื่อผู้ใช้คลิกปุ่ม "ชีตใหม่" ส่วนเสริมจะสร้างสเปรดชีตใหม่ที่จัดรูปแบบด้วยแถวส่วนหัวที่ตรึงไว้เพื่อให้มองเห็นได้เสมอ ผู้ใช้จะระบุชื่อสเปรดชีตใหม่ในแบบฟอร์ม แม้ว่าการใส่ค่าเริ่มต้นในกรณีที่แบบฟอร์มว่างเปล่าอาจเป็นตัวเลือกที่ดีก็ตาม ในการติดตั้งใช้งาน createExpensesSheet ให้แสดงการ์ดที่เกือบจะเหมือนกับการ์ดที่มีอยู่ โดยเพิ่มข้อความสถานะที่เหมาะสม รวมถึงป้อนข้อมูลล่วงหน้าในช่อง URL ด้วย URL ของสเปรดชีตใหม่

10. ยินดีด้วย

คุณออกแบบและติดตั้งใช้งานส่วนเสริม Gmail ที่ค้นหาค่าใช้จ่ายในอีเมลและช่วยให้ผู้ใช้บันทึกค่าใช้จ่ายลงในสเปรดชีตได้สำเร็จภายในไม่กี่วินาที คุณใช้ Google Apps Script เพื่อเชื่อมต่อกับ Google API หลายรายการและจัดเก็บข้อมูลระหว่างการเรียกใช้ส่วนเสริมหลายครั้ง

การปรับปรุงที่เป็นไปได้

ปล่อยให้จินตนาการนำทางคุณขณะปรับปรุง Expense It! แต่เรามีไอเดียบางอย่างที่จะช่วยให้ผลิตภัณฑ์มีประโยชน์มากยิ่งขึ้น

  • ลิงก์ไปยังสเปรดชีตเมื่อผู้ใช้บันทึกค่าใช้จ่ายแล้ว
  • เพิ่มความสามารถในการแก้ไข/ยกเลิกการบันทึกค่าใช้จ่าย
  • ผสานรวม API ภายนอกเพื่อให้ผู้ใช้ชำระเงินและขอรับเงินได้

ดูข้อมูลเพิ่มเติม