问题描述
我正在使用经过稍微调整的 this apps script 副本,使用包含各种数据的 Google 表格在 Gmail 中进行邮件合并。我在工作表中有一个列,供我也希望将电子邮件发送到的抄送收件人。但我不确定如何在 Apps 脚本中使用它。
我需要调整的代码如下,但是如何在表格中的抄送栏中添加抄送邮件?
// copyright Martin Hawksey 2020
//
// 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.
/**
* @OnlyCurrentDoc
*/
/**
* Change these to match the column names you are using for email
* recipient addresses and email sent column.
*/
const RECIPIENT_COL = "Recipient";
const EMAIL_SENT_COL = "Email Sent";
/**
* Creates the menu item "Mail Merge" for user to run scripts on drop-down.
*/
function onopen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('Mail Merge')
.addItem('Send Emails','sendEmails')
.addToUi();
}
/**
* Send emails from sheet data.
* @param {string} subjectLine (optional) for the email draft message
* @param {Sheet} sheet to read data from
*/
function sendEmails(subjectLine,sheet=SpreadsheetApp.getActiveSheet()) {
// option to skip browser prompt if you want to use this code in other projects
if (!subjectLine){
subjectLine = browser.inputBox("Mail Merge","Type or copy/paste the subject line of the Gmail " +
"draft message you would like to mail merge with:",browser.Buttons.OK_CANCEL);
if (subjectLine === "cancel" || subjectLine == ""){
// if no subject line finish up
return;
}
}
// get the draft Gmail message to use as a template
const emailTemplate = getGmailTemplateFromDrafts_(subjectLine);
// get the data from the passed sheet
const datarange = sheet.getDatarange();
// Fetch displayed values for each row in the Range HT Andrew Roberts
// https://mashe.hawksey.info/2020/04/a-bulk-email-mail-merge-with-gmail-and-google-sheets-solution-evolution-using-v8/#comment-187490
// @see https://developers.google.com/apps-script/reference/spreadsheet/range#getdisplayvalues
const data = datarange.getdisplayValues();
// assuming row 1 contains our column headings
const heads = data.shift();
// get the index of column named 'Email Status' (Assume header names are unique)
// @see http://ramblings.mcpher.com/Home/excelquirks/gooscript/arrayfunctions
const emailSentColIdx = heads.indexOf(EMAIL_SENT_COL);
// convert 2d array into object array
// @see https://stackoverflow.com/a/22917499/1027723
// for pretty version see https://mashe.hawksey.info/?p=17869/#comment-184945
const obj = data.map(r => (heads.reduce((o,k,i) => (o[k] = r[i] || '',o),{})));
// used to record sent emails
const out = [];
// loop through all the rows of data
obj.forEach(function(row,rowIdx){
// only send emails is email_sent cell is blank and not hidden by filter
if (row[EMAIL_SENT_COL] == ''){
try {
const msgObj = fillInTemplateFromObject_(emailTemplate.message,row);
// @see https://developers.google.com/apps-script/reference/gmail/gmail-app#sendEmail(String,String,Object)
// if you need to send emails with unicode/emoji characters change GmailApp for MailApp
// Uncomment advanced parameters as needed (see docs for limitations)
GmailApp.sendEmail(row[RECIPIENT_COL],msgObj.subject,msgObj.text,{
htmlBody: msgObj.html,// bcc: 'a.bbc@email.com',// cc: 'a.cc@email.com',from: 'myemail@email.co.uk',name: 'Custom From Name',// replyTo: 'a.reply@email.com',// noreply: true,// if the email should be sent from a generic no-reply email address (not available to gmail.com users)
attachments: emailTemplate.attachments,inlineImages: emailTemplate.inlineImages
});
// modify cell to record email sent date
out.push([new Date()]);
} catch(e) {
// modify cell to record error
out.push([e.message]);
}
} else {
out.push([row[EMAIL_SENT_COL]]);
}
});
function getccs() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
return sh.getRange(2,4,sh.getLastRow()-1,1).getValues().flat().join(',');
}
// updating the sheet with new data
sheet.getRange(2,emailSentColIdx+1,out.length).setValues(out);
/**
* Get a Gmail draft message by matching the subject line.
* @param {string} subject_line to search for draft message
* @return {object} containing the subject,plain and html message body and attachments
*/
function getGmailTemplateFromDrafts_(subject_line){
try {
// get drafts
const drafts = GmailApp.getDrafts();
// filter the drafts that match subject line
const draft = drafts.filter(subjectFilter_(subject_line))[0];
// get the message object
const msg = draft.getMessage();
// Handling inline images and attachments so they can be included in the merge
// Based on https://stackoverflow.com/a/65813881/1027723
// Get all attachments and inline image attachments
const allInlineImages = draft.getMessage().getAttachments({includeInlineImages: true,includeAttachments:false});
const attachments = draft.getMessage().getAttachments({includeInlineImages: false});
const htmlBody = msg.getBody();
// Create an inline image object with the image name as key
// (can't rely on image index as array based on insert order)
const img_obj = allInlineImages.reduce((obj,i) => (obj[i.getName()] = i,obj),{});
//Regexp to search for all img string positions with cid
const imgexp = RegExp('<img.*?src="cid:(.*?)".*?alt="(.*?)"[^\>]+>','g');
const matches = [...htmlBody.matchAll(imgexp)];
//Initiate the allInlineImages object
const inlineImagesObj = {};
// built an inlineImagesObj from inline image matches
matches.forEach(match => inlineImagesObj[match[1]] = img_obj[match[2]]);
return {message: {subject: subject_line,text: msg.getPlainBody(),html:htmlBody},attachments: attachments,inlineImages: inlineImagesObj };
} catch(e) {
throw new Error("Oops - can't find Gmail draft");
}
/**
* Filter draft objects with the matching subject linemessage by matching the subject line.
* @param {string} subject_line to search for draft message
* @return {object} GmailDraft object
*/
function subjectFilter_(subject_line){
return function(element) {
if (element.getMessage().getSubject() === subject_line) {
return element;
}
}
}
}
/**
* Fill template string with data object
* @see https://stackoverflow.com/a/378000/1027723
* @param {string} template string containing {{}} markers which are replaced with data
* @param {object} data object used to replace {{}} markers
* @return {object} message replaced with data
*/
function fillInTemplateFromObject_(template,data) {
// we have two templates one for plain text and the html body
// stringifing the object means we can do a global replace
let template_string = JSON.stringify(template);
// token replacement
template_string = template_string.replace(/{{[^{}]+}}/g,key => {
return escapeData_(data[key.replace(/[{}]+/g,"")] || "");
});
return JSON.parse(template_string);
}
/**
* Escape cell data to make JSON safe
* @see https://stackoverflow.com/a/9204218/1027723
* @param {string} str to escape JSON special characters from
* @return {string} escaped string
*/
function escapeData_(str) {
return str
.replace(/[\\]/g,'\\\\')
.replace(/[\"]/g,'\\\"')
.replace(/[\/]/g,'\\/')
.replace(/[\b]/g,'\\b')
.replace(/[\f]/g,'\\f')
.replace(/[\n]/g,'\\n')
.replace(/[\r]/g,'\\r')
.replace(/[\t]/g,'\\t');
};
}
感谢您的建议。
解决方法
获取抄送:
function getccs() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('sheetname');
return sh.getRange(2,4,sh.getLastRow()-1,1).getValues().flat().join(',');
}
在这里试试:
GmailApp.sendEmail(row[RECIPIENT_COL],msgObj.subject,msgObj.text,{
htmlBody: msgObj.html,// bcc: 'a.bbc@email.com',cc: getccs(),from: 'myemail@email.co.uk',name: 'Custom From Name',// replyTo: 'a.reply@email.com',// noReply: true,// if the email should be sent from a generic no-reply email address (not available to gmail.com users)
attachments: emailTemplate.attachments,inlineImages: emailTemplate.inlineImages
});