MailApp.sendEmail function runs without errors but no emails are sent - email

The below script runs without any visible error, but no emails are sent. Any ideas as to why?
function sendEmailLoop() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
sheets.forEach(function(sheet) {
var range = sheet.getDataRange();
if (sheet.getName() == "Summary") //Disregard tab named 'Summary'
{
}
else {
var range = sheet.getDataRange(); //to set the range as array
var values = range.getDisplayValues(); //to get the value in the array
var lastRow = range.getLastRow();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //declare the spreadsheet
var sheet = ss.getSheetByName("Sheet1");
var Title = values[0][0]; //[Title] cell A1
var URL = values[0][1]; //[URL] cell B1
var i;
var logContent = '';
for (i = 3; i < lastRow; i++) {
var Name = values[i][0]; //[Name] cell A++
var Email = values[i][1]; // [Email] cell B++
Logger.log('to: ' + Email);
Logger.log('subject: ' + Name + Title + 'Test');
Logger.log('message: ' + 'This is a test message for the training that can be found at ' + URL);
/*
MailApp.sendEmail({
to: Email,
subject: Name + Title + 'Test',
message: 'This is a test message for the training that can be found at ' + URL});
*/
}; //end for loop - email tab data
}; // end 'else'
}); // end function(sheet)
} // end SendEmailLoop()
Here is the Stackdriver log of a successful execution (success meaning no errors, but still no emails are sent):
The structure of the spreadsheet associated with the script:
Note - an earlier version of this script didn't include the sheets.forEach() method call (ie, to loop through each tab of the spreadsheet) and the emails were sent fine.
Could the lack of emails being sent or received be related to the fact that I have many sheets and this function is looping through them?

function sendEmailLoop() {
var exclA=['Summary'];
var ss=SpreadsheetApp.getActive();
var sheets=ss.getSheets();
sheets.forEach(function(sheet) {
if (exclA.indexOf(sheet.getName())==-1) {
var range=sheet.getDataRange();
var values=range.getDisplayValues();
var lastRow=range.getLastRow();
var Title=values[0][0];
var URL=values[0][1];
var logContent='';
for (var i=3; i <values.length; i++) {
var Name=values[i][0];
var Email=values[i][1];
Logger.log('to: %s\nsubject: %s %s Test\nmessage: %s This is a test message for the training that can be found at %s',Email,Name,Title,URL);
/*
MailApp.sendEmail({
to: Email,
subject: Name + Title + 'Test',
message: 'This is a test message for the training that can be found at ' + URL});
*/
}
}
});
}

Answer:
You need to uncomment your MailApp code.
Code changes:
I tested your code and it seems to run without issue for me, including the receipt of the emails, only that the code comments around your MailApp call need to be removed (the /* and the */).
I would also suggest adding a conditional line before you send the email in the event .getDataRange() obtains seemingly empty rows:
if (Email == "") {
continue;
};
MailApp.sendEmail({
to: Email,
subject: Name + Title + 'Test',
message: 'This is a test message for the training that can be found at ' + URL});
References:
JavaScript Comments

Related

Cannot read property 'getRange' of null error. I am positive the sheet exists and is named correctly in the script

My fellow teachers and I like to send students little google drawings to let them know they've been doing well lately. We have a spreadsheet that we use and I am trying to automate the process of sending them an email when their drawing is ready to be viewed. I keep getting the 'Cannot read property 'getRange' of null error' even though I am 100% sure that a sheet exists with the name PR and that it is spelled right. I am new to Google Script so I lack the skills to troubleshoot any more than the googling I've done already, which basically just says to make sure you've named the sheet correctly. Any help would be very appreciated!
var studentFirstNameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("A2:A");
var studentFirstname = studentFirstNameRange.getValues();
var studentEmailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("D2:D");
var studentEmail = studentEmailRange.getValues();
var emailSendRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("L2:L");
var emailSend = emailSendRange.getValues();
if (emailSend){
// Send Alert Email.
var message = 'Hi ' + studentFirstname + '! Your teachers noticed you have been doing a great job this year, so we made this for you! Keep up the great work!' ; // Second column
var subject = 'Positive Recognition';
MailApp.sendEmail(studentEmail, subject, message);
}
}```
According to the title and the error you related, I did'nt get any error.
However, I don't understand your condiiton if (emailSend) neither the way you send emails. If you send emails to all the population at once, you can try
function myFunction() {
var lastRow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getLastRow()
var studentFirstNameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("A2:A"+lastRow);
var studentFirstname = studentFirstNameRange.getValues();
var studentEmailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("D2:D"+lastRow);
var studentEmail = studentEmailRange.getValues();
var emailSendRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("L2:L"+lastRow);
var emailSend = emailSendRange.getValues();
if (emailSend){
// Send Alert Email.
var message = 'Hi ' + studentFirstname + '! Your teachers noticed you have been doing a great job this year, so we made this for you! Keep up the great work!' ; // Second column
var subject = 'Positive Recognition';
Logger.log(studentEmail.join())
MailApp.sendEmail(studentEmail.join(), subject, message);
}
console.log('there is no errors!')
}
if you want to send individually
function myFunction() {
var lastRow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getLastRow()
var studentFirstNameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("A2:A" + lastRow);
var studentFirstname = studentFirstNameRange.getValues();
var studentEmailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("D2:D" + lastRow);
var studentEmail = studentEmailRange.getValues();
var emailSendRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PR').getRange("L2:L" + lastRow);
var emailSend = emailSendRange.getValues();
for (var i = 0; i < studentFirstname.length; i++) {
if (emailSend[i][0]) {
// Send Alert Email.
var message = 'Hi ' + studentFirstname[i][0] + '! Your teachers noticed you have been doing a great job this year, so we made this for you! Keep up the great work!'; // Second column
var subject = 'Positive Recognition';
MailApp.sendEmail(studentEmail[i][0], subject, message);
}
}
}

Apps Script: Send email - Script sends 1 email per row instead of one email with all the info that meets the criteria

Newbie looking for some help... I can't understand what is going wrong in this script!
I am using a sheet similar to this one. My objective is to send and email to a specific address whenever certain conditions are met.
The conditions are Col R (Yesterday = 1) & Col D (Status) is different than "Resgate".
This is the output I am getting from my script. If 2 rows meet these conditions, then I get an email with the first row that meets conditions, and then another email with the first and second row that meets conditions. If there where 5 rows that meet conditions, I would receive 5 emails, and in the last one, I would have all the rows that meet conditions.
What I want to achieve is to receive only the final email with all the rows that meet conditions and avoid receiving the other ones.
Email 1
Email 2
This Is the code I am using
function SENDMAIL() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// set active sheet to Sheet by name
spreadsheet.setActiveSheet(spreadsheet.getSheetByName("Sheet 1"));
var sheet = spreadsheet.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
var startRow = 2;
// grab all data from user to days left
var range = sheet.getRange(startRow, 1, lastRow - startRow + 1, 19);
var values = range.getValues();
var users1 = [];
var users2 = [];
var users3 = [];
var users4 = [];
var users5 = [];
var users6 = [];
// loop all data per row
values.forEach(function(row) {
if (row[17] == 1 && row[3] !== "Resgate") {
// add user if 4 or more days left
users1.push(row[3]);
users2.push(row[4]);
users3.push(row[5]);
users4.push(row[16]);
users5.push(row[15]);
users6.push(row[14]);
// if users has elements
var message = "<html><body></h1><p><b>New Requests</font2></h1>";
var names = "<ul>";
users4.forEach(function(user, i) {
names += `<p><b><li>${user} -> ${users1[i]} - ${users3[i]} - ${users2[i]} - ${users5[i]} - ${users6[i]}€ `;
});
names += "</ol>"
message = message + names + "</body></html>";
var flexmails = "Example#gmail.com";
MailApp.sendEmail(flexmails, "NEW PPR's", "", {
htmlBody: message,
noReply: true
});
} else {}
})
}
Simply move sendEmail out from the values.forEach loop and put it at the end of the script
MailApp.sendEmail(flexmails, "NEW PPR's","", { htmlBody: message, noReply: true });
values.forEach(function(row) {
if (row[17] == 1 && row[3] !== "Resgate") {
// add user if 4 or more days left
users1.push(row[3]);
users2.push(row[4]);
users3.push(row[5]);
users4.push(row[16]);
users5.push(row[15]);
users6.push(row[14]);
}
});
// if users has elements
var message = "<html><body></h1><p><b>New Requests</font2></h1>";
var names = "<ul>";
users4.forEach(function(user, i) {
names += `<p><b><li>${user} -> ${users1[i]} - ${users3[i]} - ${users2[i]} - ${users5[i]} - ${users6[i]}€ `;
});
names += "</ol>"
message = message + names + "</body></html>";
var flexmails = "Example#gmail.com";
MailApp.sendEmail(flexmails, "NEW PPR's", "", {
htmlBody: message,
noReply: true
});

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: How to pull values from column A based on values in column E and send all values in one email?

I'm trying to create a script for a student attendance spreadsheet that will look in Column E for the string "X". For each instance of "X", the string from column A (the student name) will be added to the body of an email. I'm pretty new to JavaScript, although I have been studying the basics. I've done a lot of research and found some scripts I was able to modify to send an individual email for each instance of X in E. However, I have not been able to figure out how to combine that information into a single email.
Here's what I have so far:
function Email_ReminderNS() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("July_August"),
EMAIL_SENT = "EMAIL_SENT",
statusArray = sheet.getDataRange().getValues();
var class = statusArray[0][8],
status = "X",
email = "XXXX"
for (i=7;i < statusArray.length;i++){
var emailSent = statusArray[i][84];
if (status == statusArray[i][4] & emailSent != EMAIL_SENT) {
var student = statusArray[i][0];
var body = "This is a No-Show Report for " +student+ " from " + class;
var subject = "No-Show Report for " + student+ " from " + class;
MailApp.sendEmail(email,subject,body,{NoReply : true});
sheet.getRange(i+1, 85).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
I realize I'll probably need to move the sendEmail function to be outside the IF statement. I tried to create an array with the names and join those into a string and add it to the body of the email, but I've had no luck. It just ended up sending the last name instead of all of them.
If anyone has any suggestions for me I would be deeply grateful.
First set up variables to keep track of which student did not show up:
var students = [];
var student_rows = [];
Then, add student to these arrays when X is found:
if (status == statusArray[i][4] & emailSent != EMAIL_SENT) {
var student = statusArray[i][0];
students.push(student);
student_rows.push(i+1);
}
Then send the email with all student names combined (outside of the for loop like you said)
var body = "This is a No-Show Report for " + students.join(', ') + " from " + class;
var subject = "No-Show Report for " + students.join(', ') + " from " + class;
MailApp.sendEmail(email,subject,body,{NoReply : true});
Finally update the spreadsheet indicating which names were in that email:
for (var i=0; i<student_rows.length; i++) {
sheet.getRange(student_rows[i], 85).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
Here's the complete script:
function Email_ReminderNS() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("July_August"),
EMAIL_SENT = "EMAIL_SENT",
statusArray = sheet.getDataRange().getValues();
var class = statusArray[0][8],
status = "X",
email = "francis#bposolutions.com";
var students = [];
var student_rows = [];
for (i=7;i < statusArray.length;i++){
var emailSent = statusArray[i][84];
if (status == statusArray[i][4] & emailSent != EMAIL_SENT) {
var student = statusArray[i][0];
students.push(student);
student_rows.push(i+1);
}
}
var body = "This is a No-Show Report for " + students.join(', ') + " from " + class;
var subject = "No-Show Report for " + students.join(', ') + " from " + class;
MailApp.sendEmail(email,subject,body,{NoReply : true});
for (var i=0; i<student_rows.length; i++) {
sheet.getRange(student_rows[i], 85).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
There are probably many ways to implement a new version of your code, the other answer probably works but I think it can be improved (a bit).
First of all, you can get rid of the flush method that does nothing else than slowing down the function (it was originally used in the Google example to check the sent status row by row, it is useless when we send only one mail with all the data in it)
Secondly, it might be a good idea to use html format to get a better looking result.
And lastly, it is good practice to write back to the sheet using one setValues instead of multiple setValue() in a loop.
Here is a possible replacement code, you'll have to "tune" it to your needs to eventually improve the message format but the main structure is there and working.
function Email_ReminderNS() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("July_August"),
statusArray = sheet.getDataRange().getValues();
var email = Session.getActiveUser().getEmail(); //replace with the email you want, this value will send mails to you I used it for test.
var class = statusArray[0][8],
status = "X",
students = [];
for (var i=7;i < statusArray.length; i++){
var emailSent = statusArray[i][84];
if (status == statusArray[i][4] & emailSent != "EMAIL_SENT") {
students.push(statusArray[i][0]);
statusArray[i][84]="EMAIL_SENT";
}
}
var subject = "No-Show Report for " + students.length + " from " + class;
var textBody = "This is a No-Show Report for " +students.length+ " from " + class+"\n";
var HTMLBody = "<b>This is a No-Show Report for " +students.length+ " from " + class+"</b><br><br>"
+'<table style="background-color:lightblue;border-collapse:collapse;" border = 1 cellpadding = 5><th>Sent Mails</th><tr>';
for(var n in students){
HTMLBody += '<tr><td>'+n+'</td><td>'+statusArray[n][0]+'</td></tr>';
textBody += '\n'+n+' - '+statusArray[n][0];
}
HTMLBody+='</table><BR> kind regards.' ;
textBody+='\n\nKind regards';
Logger.log(HTMLBody);
Logger.log(textBody);
MailApp.sendEmail(email,subject,textBody,{'NoReply' : true, 'htmlBody' : HTMLBody});
sheet.getRange(1,1,statusArray.length,statusArray[0].length).setValues(statusArray);
}

Google Form. Confirmation Email Script. With edit url

I have two working trigger functions in Google Script that fire when a form response spreadsheet gets a new submission. One inserts the "edit your submission" url into the spreadsheet. The other looks up the response's email and sends them a confirmation.
What I'm having a hard time understanding is how to populate the url first, and then send an email containing that url.
(Google Script is different to debug than js in the browser :\ )
Setup triggers
function Initialize() {
var triggers = ScriptApp.getScriptTriggers();
for (var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("SendConfirmationMail")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit()
.create();
assignEditUrls();
}
On form submit, searches for column titled "Clients Email", and sends a formatted email to them.
function SendConfirmationMail(e) {
try {
var ss, cc, sendername, subject, columns;
var message, value, textbody, sender;
var url;
// This is your email address and you will be in the CC
cc = Session.getActiveUser().getEmail();
// This will show up as the sender's name
sendername = "XXXX";
// Optional but change the following variable
// to have a custom subject for Google Docs emails
subject = "Form Complete: Mobile App - Client Questionnaire";
// This is the body of the auto-reply
message = "Confirmation text here.<br><br>Thanks!<br><br>";
ss = SpreadsheetApp.getActiveSheet();
columns = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
// This is the submitter's email address
sender = e.namedValues["Clients Email"].toString();
// Only include form values that are not blank
for ( var keys in columns ) {
var key = columns[keys];
if ( e.namedValues[key] ) {
message += key + ' :: '+ e.namedValues[key] + "<br />";
}
}
textbody = message.replace("<br>", "\n\n");
GmailApp.sendEmail(sender, subject, textbody,
{cc: cc, name: sendername, htmlBody: message});
} catch (e) {
Logger.log(e.toString());
}
}
Separate function. Looks up form and applies the edit URL to the 26th column.
function assignEditUrls() {
var form = FormApp.openById('10BVYipGhDa_AthabHE-xxxxxx-hg');
//enter form ID here
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses');
//Change the sheet name as appropriate
var data = sheet.getDataRange().getValues();
var urlCol = 26; // column number where URL's should be populated; A = 1, B = 2 etc
var responses = form.getResponses();
var timestamps = [], urls = [], resultUrls = [];
for (var i = 0; i < responses.length; i++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(responses[i].getEditResponseUrl());
}
for (var j = 1; j < data.length; j++) {
resultUrls.push([data[j][0]?urls[timestamps.indexOf(data[j][0].setMilliseconds(0))]:'']);
}
sheet.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
}
Programmers are provided no direct control over the order that trigger functions will be invoked, when they are dependent on the same event.
In your situation though, there are a couple of options available.
Use only one trigger function for the event, and have it invoke the current functions in the order you require. (Alternatively, set assignEditUrls() up as the only trigger function, and have it call SendConfirmationMail() after completing its task.
Have SendConfirmationMail() check for the availability of the Edit URL, and call Utilities.sleep() if the URL isn't ready yet. (In a loop, until ready.)