用 Google Apps Script 發送電郵

上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。

準備內容

上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。

第二及第三欄是我們會在電郵內文用到;Sent By 和 Sent At 兩欄是用來判別這封電郵是否發送過。如果已經發送過就會標明是由誰人執行和執行時間。

準備內文範本

和之前一樣,都是用 Google Apps Script 的 HTML template 功能。

email.html
1
2
3
<p>Dear <?= name ?>,<p>

<p>Congratulation! You won our smartphone giveaway. Your <?= prize ?> will be shipped to you within one month.</p>

簽名檔 / 加入圖片(非必須)

使用 GmailApp 發送電郵是不會把你 Gmail 所設的簽名檔加到電郵內。如果你想在電郵內出現簽名檔的話,需要在 HTML template 內加入簽名檔的 HTML code。留意電郵軟件未必支援新式的 HTML/CSS,所以電郵排版都是用 HTML <table> 和用 inline CSS。

如果想加入圖片的話,有兩個方法:

  1. 直接連結寄存在其他地方的圖片
  2. 將圖片變成電郵附件,再顯示在電郵的內文

第一個方法和平時做網頁差不多,第二個方法比較複雜。如果需要用第二個方法,可以參考下面的做法。

首先,將圖片上載到 Google Drive。接着就用 script 把這些圖片檔變成 Blob。以下的 function 示範了如何抽取在 EMAIL_SIGNATURE_IMAGE_FOLDER_ID 入面的檔案的 Blob。Folder ID 其實就是用網頁版 Google Drive 開啟 folder 後在網址見到的一串 ID。

1
2
3
4
5
6
7
8
9
function extractEmailSignatureBlobs() {
var result = {}
var files = DriveApp.getFolderById(EMAIL_SIGNATURE_IMAGE_FOLDER_ID).getFiles()
while (files.hasNext()) {
var file = files.next()
result[file.getName()] = file.getBlob()
}
return result
}

在 HTML template 內,可以在 <img>src 加上 cid: 前綴來顯示 inline 圖片:

1
<img src="cid:imageKey" />

imageKey 就是剛才 extractEmailSignatureBlobs return 的 object 的 key。例如在 Google Drive 的 folder 有一個 foo.png,那 src 就會是 cid:foo.png

發送郵件或建立草稿

createDraft 會在你 Gmail 草稿箱內新增一封電郵,不會把它發出。

1
2
3
4
GmailApp.createDraft(rowValues[0], subject, html, {
htmlBody: html,
inlineImages: inlineImages
});

inlineImages 就是 extractEmailSignatureBlobs 的 return value。如果沒有加入圖片就不用加。

如果想馬上發送郵件,那就把 createDraft 換成 sendEmail

下面是發送郵件的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function sendEmail() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Mail merge');
var range = sheet.getRange(2, 1, sheet.getMaxRows() - 1, 5);
var values = range.getValues();
var template = HtmlService.createTemplateFromFile('email');

for (var row in values) {
var rowValues = values[row];
var rowIndex = parseInt(row) + 2;

// Early termination when no email address found in a row
if (isCellEmpty(rowValues[0])) {
return;
}

// Skip sent row
if (!isCellEmpty(rowValues[4])) {
continue;
}

template.name = rowValues[1];
template.prize = rowValues[2];

var html = template.evaluate().getContent();
var subject = rowValues[1] + ', you are the winner!'
GmailApp.createDraft(rowValues[0], subject, html, {
htmlBody: html
});

// Write the user's email address and timestamp to that row
sheet.getRange(rowIndex, 4).setValue(Session.getActiveUser().getEmail());
sheet.getRange(rowIndex, 5).setValue(new Date());
}
}

基本上就是一個 loop 逐行檢查,如果需要發電郵就用 GmailApp 製造郵件放到自己 Gmail 的草稿箱。

執行完成後會看到 Sent By 和 Sent At 兩欄經已被填妥。下次再執行時程式會檢查 Sent At 是否為空,那就不會重覆發送郵件。

完整程式碼

Google Apps Script 程式碼、HTML 和試算表內容已經放到 Gist 上。

下一步

之後可以考慮加入自訂選單。有了自訂選單就不用每次都要進入 Script editor 才能執行程式。

結語

上一篇和這一篇分別介紹了 Google Apps Script 操作 Spreadsheet、Gmail、Drive 和 HTML template 功能。你可以結合一起使用,提昇工作效率。例如 Spreadsheet 內容由 Form 形式輸入、使用 Maps Service 做 Geocoding 等等。此外,Google Apps Script 的 function 也可以當 spreadsheet 的 function 使用。如果你常常使用 Google Drive 的辦公室軟件功能的話 Google Apps Script 實在不容錯過。