Slow MailApp.send in Google Sheets - email

I have been using this mailapp coding for a while, and only sends to a list of people based on the EMAIL_SENT in column K, confirming it was sent in column J. The list is long, but only sends to maybe 20 people per run based on the rule for column K (a formula sending to people actually working today). Has worked great in the past.
Now though after recently reformatting the Google Sheet for my group, it takes 20-30 seconds to send one email off, and doesn't even finish the list of 20 emails in the 6 minute time limit. My recent reformatting does include a lot more scripting on the Google Sheet overall with more Conditional Formatting, but not on the sheet that feeds this Script.
Any ideas what is slowing it down so much?
Thanks
function MorningEmail() {
var sheet = SpreadsheetApp.getActive().getSheetByName('EarlyEmail');
var startRow = 4;
var numRows = 180;
var dataRange = sheet.getRange(startRow, 1, numRows, 50)
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[0]; // First column
var fromName = row[16];
var replyToaddress = row[15];
var message = row[20]+row[21]+row[22]+row[23]+row[24]+row[25]+row[26]+row[27]+row[28]+row[29]+row[30]+row[31]+row[32]+row[33]+row[34]+row[35]+row[36]+row[37]+row[38]+row[39]+row[40]+row[41]+row[42]+row[43]+row[44]+row[45]+row[46]+row[47]+row[48]+row[49];
var emailSent = row[10];
if (emailSent != EMAIL_SENT) {
var subject = row[19];
MailApp.sendEmail(emailAddress, subject, message, {
name: fromName,
replyTo: replyToaddress,
htmlBody: message
});
sheet.getRange(startRow + i, 10).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}

Related

Unable to add inline image to email in google apps script

I'm new to Google Apps script and am trying to add an image inline to an automated response email.
The auto reply works perfectly, the main text of the email formats well in plain text and html.
the problem i'm facing is that the image does not appear.
my code:
// This constant is written in column Y for rows for which an email
// has been sent successfully.
var EMAIL_SENT = 'EMAIL_SENT';
/**
* Sends non-duplicate emails with data from the current spreadsheet.
*/
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(sheet.getSheetByName('Data'))
var startRow = 2; // First row of data to process
// Fetch the range
var dataRange = sheet.getRange("L2:L1000")
var dataRange2 = sheet.getRange("K2:K1000")
var dataRange3 = sheet.getRange("O2:O1000")
var dataRange4 = sheet.getRange("Y2:Y1000")
var dataRange5 = sheet.getRange("B2:B1000")
// Fetch values for each row in the Range.
var data = dataRange.getValues();
var data2 = dataRange2.getValues();
var data3 = dataRange3.getValues();
var data4 = dataRange4.getValues();
var data5 = dataRange5.getValues();
for (var i = 0; i < data.length; ++i) {
var yesno = data2[i]
if(yesno == "Yes"){
var TFlogoUrl = "https://drive.google.com/openid=1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287";
var TFlogoBlob = UrlFetchApp
.fetch(TFlogoUrl)
.getBlob()
.setName("TFlogoBlob");
var emailAddress = data[i];
var ShipID = data3[i];
var cmdrID = data5[i];
var TFmsg = "Hi " + cmdrID + ",/n /nThank you for signing up to The Fatherhoods Lost Souls Expedition./n /nYour unique Ship ID is: " + ShipID + "/n /nWe look forward to seeing you on the expedition CMDR!/n /nFly Safe,/nThe Lost Souls Expedition team.";
var htmlTFmsg = "Hi " + cmdrID + ",<br> <br>Thank you for signing up to The Fatherhoods Lost Souls Expedition.<br> <br>Your unique Ship ID is: " + ShipID + "<br> <br>We look forward to seeing you on the expedition CMDR!<br> <br>Fly Safe,<br>The Lost Souls Expedition team.<br><img src='cid:TFlogo'>";
emailSent = data4[i]; // email sent (column Y)
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "Lost Souls Expedition Sign up confirmation";
MailApp.sendEmail(emailAddress,subject,TFmsg,{
htmlBody: htmlTFmsg,
inlineImage:
{
TFlogo:TFlogoBlob
}
});
sheet.getRange("Y" + (startRow + i)).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
}
How about this modification?
Modification points:
You cannot retrieve the file blob from this URL var TFlogoUrl = "https://drive.google.com/openid=1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287";. If you want to retrieve the file blob from URL, please use var TFlogoUrl = "http://drive.google.com/uc?export=view&id=1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287";. 1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287 is the file ID.
As an another method, from the file ID, it is found that the values of getSharingAccess() and getSharingPermission() are ANYONE_WITH_LINK and VIEW, respectively. So you can also retrieve the blob using var TFlogoBlob = DriveApp.getFileById("1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287").getBlob().setName("TFlogoBlob");. I recommend this.
When you want to use the inline image to email, please modify from inlineImage to inlineImages.
The script which reflected above points is as follows.
Modified script:
Please modify your script as follows.
From:
var TFlogoUrl = "https://drive.google.com/openid=1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287";
var TFlogoBlob = UrlFetchApp.fetch(TFlogoUrl).getBlob().setName("TFlogoBlob");
To:
var id = "1nzmvP_zzOms1HiBoFCsVLFjDM6ZzM287";
var TFlogoBlob = DriveApp.getFileById(id).getBlob().setName("TFlogoBlob");
And
From:
inlineImage: {TFlogo:TFlogoBlob}
To:
inlineImages: {TFlogo:TFlogoBlob}
References:
sendEmail(recipient, subject, body, options)
If I misunderstand your question, please tell me. I would like to modify it.

Google Apps Script using wrong date format in excel

So I am working on a Google Apps script that pulls an email address, subject, and body from a Google sheet file. This info is used to send an email out. Right now in my Subject column for the Google Sheets file I have =TODAY() so that the date is pulled. My script updates this column everyday so the date is always current.
The issue is that when the email comes the subject line shows
"Sat Aug 11 2018 00:00:00 GMT-0700 (PDT) "
Instead of...
08/11/18 like its setup for in Google Sheets
Not sure why this could be, My code is below.
/**
* Creates a two time-driven triggers.
*/
function createTimeDrivenTriggers() {
// Trigger every 6 hours.
ScriptApp.newTrigger('adddate')
.timeBased()
.atHour(21)
.everyDays(1)
.inTimezone("America/Los_Angeles")
.create()
ScriptApp.newTrigger('sendEmails2')
.timeBased()
.atHour(22)
.everyDays(1)
.inTimezone("America/Los_Angeles")
.create()
}
/**
* This is my hacky way to make sure sheets has today's date.
*/
function adddate() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var cell = sheet.getRange(2,2);
cell.setValue('=TODAY()');
}
// This constant is written in column C for rows for which an email
// has been sent successfully.
var EMAIL_SENT = 'SUCCESSFULLY SENT';
/**
* Sends non-duplicate emails with data from the current spreadsheet.
*/
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 2; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows, 3);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[0]; // First column
var subject = row[1]; // Second column
var message = row[2]; // Third column
var emailSent = row[3]; // Fourth column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
The problem you are encountering is that your cell is formatted as a date and you are passing it as a parameter that expects a string. At that point you are no longer controlling the conversion, and you are getting way more than you wanted. Two ways to address it.
1) Make it a string in the first place with
cell.setValue('=text(TODAY(),"mm/dd/yy"');
which does run the risk of ruining any date processing you do on it at first (though I see none, so probably fine).
So probably better is (not 1, just this)
2) is to get the displayed string while leaving the underlying date like you had it with var subject = row[1].getDisplayValue();
2) has the added benefit of going with the date format for which sheets is set up.

How to send e-mails automatically from a sheet as it updated?

I posted this question on Google Sheets forum and was directed here for help.
*Please note that I am not a coder, I've just started working with things like this.
I've created a workbook with 3 pages. It's connected to a form, so as people submit their reviews the answers are collected on the first sheet, calculated on the second sheet, and any submission with an overall score higher than 3.9 has the email address moved to the third sheet where it will be sent a follow-up email.
Here is a link to a copy of the sheet:
https://docs.google.com/spreadsheets/d/15p7_M4guRWCVHG-acRAheAROmh9t4wk3GWD_x0_rl90/edit?usp=sharing
It all works except the last page: I cannot figure out how to get the emails to send automatically as the sheet is updated. I've set the trigger to have it run when the sheet is updated, but to no avail - I have to manually go in and click the "Run" button to have the emails sent. (When I do, it pops up 'Failed to sent email: no recipient (line 21, file "Code")' but the emails are successfully delivered anyway.)
The formula I am using is this: (I have also tried swapping "sendEmails2" with "autoResponder" but that didn't change anything either.) Can anyone tell me what is wrong with it? Or is there a better way to automatically send the e-mails?
// This constant is written in column C for rows for which an email
// has been sent successfully.
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 20000; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows, 3)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[0]; // First column
var message = row[1]; // Second column
var emailSent = row[2]; // Third column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "Sending emails from a Spreadsheet";
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is
interrupted
SpreadsheetApp.flush();
}
}
}
This should do it:
function sendEmails2(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Automatic Emails');
var rg=sh.getRange(2,1,sh.getLastRow(), 3);
var vA=rg.getValues();
for (var i=0;i<vA.length;i++){
var emailAddress = vA[i][0];
var message = vA[i][1];
var emailSent = vA[i][2];
var subject = "Sending emails from a Spreadsheet";
if(emailSent != 'EMAIL_SENT'){
MailApp.sendEmail(emailAddress, subject, message);
sh.getRange(2 + i,3).setValue('EMAIL_SENT');
}
}
}
Don't forget the onFormSubmit:

Google Script Email loop

I am relatively new to programming, and have recently been working on a script to send emails from a google spreadsheet when a cell in a certain column is changed. The recipient is assigned based off of an email address in another column in the same row as the change. I am having difficulty getting my code to stop running after the first email. As it is, the script runs indefinitely (at least until I run out of emails for the day).
Here is the code:
function sendNotification() {
var sheet = SpreadsheetApp.getActiveSheet();
//Get Active cell
var mycell = sheet.getActiveSelection();
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
var address = sheet.getRange("C" + cellrow).getValue();
var streetAddress = sheet.getRange("F" + cellrow).getValue();
var startRow = 2;
var numRows = 2000;
// Fetch the range of cells A2:O2000
var dataRange = sheet.getRange(startRow, 1, numRows, 15)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = address; // First column
var message = streetAddress +" Has been Submitted for permitting!"; // Second column
var subject = "The above Address has been Submitted For Permitting! We will Follow up with you when it has been approved.";
//Check to see if column is H to trigger
if (cellcol == 8 && sheet.getName() == "Sheet1" && mycell !== "")
{
//Send the Email
MailApp.sendEmail(emailAddress, message, subject);
}
//End sendNotification
}
}
What can I do to resolve this? Would a loop be the best option? How would I implement this?
How about this approach?
var EMAIL_SENT = "EMAIL_SENT";
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var mycell = e.range;
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
var emailAddress = sheet.getRange("C" + cellrow).getValue();
var streetAddress = sheet.getRange("F" + cellrow).getValue();
var subject = "The above Address has been Submitted For Permitting! We will follow up with you when it has been approved."
// Fetch values for each row in the Range
var message = streetAddress +" Has been Submitted for permitting!";
var emailSent = sheet.getRange("O" + cellrow).getValue();
if ( cellcol == 8 && sheet.getName() == "Sheet1" && emailSent != EMAIL_SENT) { // Prevents sending duplicates
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(cellrow, 15).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
If you use the onEdit() the function will be triggered every time you edit the cell. Is this what you want?
What I assume you're looking for is a script that:
Reads every row of the active sheet
If the 8th column is not empty, sends an e-mail to the address in the first column
The whole script is trigger upon the user requests (not when the user edits a cell)
In this case the first approach is better, the sendNotification(). Also a loop is necessary to read all the rows. And the IF statement should be something like if (row[8] != "") then send the e-mail.
In this case row which was defined as row = data[i] in your first script will have the values of all the cells in the row being read in the loop. So row[8] will have the value of the 8th (column H), which you want to check for emptiness, thus if(row[8] != "").
Also, if I understand correctly the e-mail adress should be emailAddress = row[1] inside the loop, because the email address is different every row.
Your second approach var EMAIL_SENT = "EMAIL_SENT"; is almost similar to the simple extension of the code that sets the cells in a column to 'EMAIL_SENT' for each row after sendEmail is called given in Section 2: Improvements. Within tutorial, each cell was marked in each row every time an email is sent. With that, you should mark edited cells as unsent then you will be able to re-run the script later on, avoid sending email duplicates and will only send the edited cells.
To make your code more efficient and help you improve the performance of your scripts, there are also list of best practices given.

Google Apps Script: read text from a .txt file

After taking a look at the provided tutorial for sending emails from a spreadsheet with Google Apps Script, I modified the given code with aims to be able to send the set of emails out with attachments as well.
It works well enough, even with a couple quirks from the limitations of Google Apps Script (the files have to be in the Google Drive, and all files in the Google Drive with whichever name is appropriate are taken from all folders in the drive).
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 1; // Number of rows to process
// Fetch the range of cells A2:C3
var dataRange = sheet.getRange(startRow, 1, numRows, 3)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[0]; // First column
var message = row[1]; // Second column
var attachment = row[2]; // File name provided.
var subject = "Mass Email";
var files = DriveApp.getFilesByName(attachment); // Get all files with name.
var blobs = []; // Array for attachment.
// Move files into blobs
while (files.hasNext()) {
var file = files.next();
blobs.push(file.getAs(MimeType.PLAIN_TEXT));
}
MailApp.sendEmail(emailAddress, subject, message, {
attachments: blobs, // add attachments
cc: "extra#email.com" // CC to employer
});
}
}
After I first used it, however, I learned that I need to send the files not as attachments, but as the message body of the emails, among some other changes (this is for work). That is, I will only ever email one 'attachment' at a time to each email, and instead of that file being an attachment, its content should be copied over to the message of the email. The attachments are currently text files, and I'd like them to stay that way, but it isn't the most vital thing.
I cannot determine a way to do this with Google Apps Script. Is this possible, or will I have to have a different way of emailing these files? (Hopefully not by hand.)
Try this. It's more faster and simpler.
var docContent = file.getBlob().getDataAsString();
Logger.log(docContent);
as mentioned in my last comment, converting your .txt files to Google documents makes it easy to achieve.
see below (suggestion)
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 1; // Number of rows to process
// Fetch the range of cells A2:C3
var dataRange = sheet.getRange(startRow, 1, numRows, 3)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
Logger.log(row)
var emailAddress = row[0]; // First column
var message = row[1]; // Second column
var attachment = row[2]; // File name provided.
var subject = "Mass Email";
var files = DriveApp.getFilesByName(attachment); // Get all files with name.
while (files.hasNext()) {
var file = files.next();
var Id = file.getId();
var content = DocumentApp.openById(Id).getBody().getText();
Logger.log(content)
MailApp.sendEmail(emailAddress, subject, message+'\n'+content)
}
}
}