Checking If GmailApp.sendEmail() Is Running - google-apps-script

I'm trying to run a script to send automated emails to a list of people (I needed to edit out a bit of information).
function sendEmail(){
for (var key in emailBank){
var email = getEmail(key);
var bodyText = emailBank[key]['body'];
var commitmentType = emailBank[key]['type'];
GmailApp.sendEmail(key,"Subject Line", bodyText {htmlBody:bodyText});
}
}
I'm using an array (emailBank) to store information from a spreadsheet before sending it out. The function should loop through and send each person an email.
On occasion, I'm getting the following error: "We're sorry, a server error occurred. Please wait a bit and try again." for the line including GmailApp.
Can you think of any to verify if an email is sent or not? My concern is that half of the emails will be sent and half will not, so I wouldn't know who actually received the email and at which point the loop stopped.
Any ideas are appreciated here!

Ok, so I think I found a solution to this after trying out a few methods:
// Variables for error checking later
var emailRecipients = "";
var trueRecipients = "";
//The sendEmail function to send the email as expected
//It will also increment the emailRecipients variable
//only after the email has been sent
function sendEmail(){
for (var key in emailBank){
var email = getEmail(key);
var bodyText = emailBank[key]['body'];
var commitmentType = emailBank[key]['type'];
GmailApp.sendEmail(key,"Subject Line", bodyText {htmlBody:bodyText});
emailRecipients += key + "<br>";
}
}
//This function will only run if there was an error
//It will check increment all of the email addresses
//that should have been sent a message and then send me an
function errorEmail(){
for (var key in emailBank){
trueRecipients += key + "<br>";
}
var errorBodyText = "Emails sent to:<br><br>" + emailRecipients + "<br><br>Number of emails that should have sent:<br><br>" + trueRecipients;
GmailApp.sendEmail("example#gmail.com","Email Errors", errorBodyText,{htmlBody:errorBodyText});
}
function reminderEmail(){
try{
sendEmail();
}
catch(e){
errorEmail();
}
}

As a temporary solution, you could check the last sent email after calling sendEmail() method.
/* the search() method returns an array of GmailThread objects.
Pop() returns the GmailThread */
var threads = GmailApp.search("label: sent", 0, 1).pop(); // returns the most recent thread in 'Sent Mail'
var msg = threads.getMessages().pop(); //returns the most recent message
Logger.log(msg.getId()); //returns unique id for the message
Logger.log(msg.getTo()); //returns comma-separated list of recipients
One option would be to store the message id and check if it changes on each run of the loop. You may also directly compare email properties, e.g. the recipient's email, against the properties of the message you sent.

Related

How to overcome "Cannot find method addLabel(string)" in Gmail App Script?

The below App Script fetches the first message of the first Gmail Inbox thread and checks the "From:" header against a regular expression.
Based on the outcome, we want to use addLabel() to set a Gmail label.
It performs the fetch and test fine, but fails when attempting to set the label - Cannot find method addLabel(string). (line 15, file "Code")
function myFunction() {
// Get first thread in Inbox.
var thread = GmailApp.getInboxThreads(0,1)[0];
// Get the first message in the thread.
var message = thread.getMessages()[0];
// Get a message header
var headerstring = message.getHeader("From");
// Check header for "rob" test string, apply label
if ( headerstring.match(/rob/g) ) {
thread.addLabel("MyLabel");
Logger.log("Matched rob");
} else {
thread.addLabel("AnotherLabel"); // failing, not class scope?
Logger.log("No match");
}
}
It feels like the existence of addLabel within the if clause has robbed it of the application of GmailApp, since I have had addLabel functioning outside of one - am I right? This is my first script.
How can I overcome this?
Explanation:
The issue is that addLabel(label) does not accept a string but an object of type GmailLabel.
If the labels are already created by you, you need to use getUserLabelByName(name) where you can pass the label name as string, get a GmailLabel object back and finally pass it to addLabel(label).
Solution:
function myFunction() {
// Get first thread in Inbox.
var thread = GmailApp.getInboxThreads(0,1)[0];
// Get the first message in the thread.
var message = thread.getMessages()[0];
// Get a message header
var headerstring = message.getHeader("From");
// Check header for "rob" test string, apply label
if ( headerstring.match(/rob/g) ) {
var label = GmailApp.getUserLabelByName("MyLabel");
thread.addLabel(label);
Logger.log("Matched rob");
} else {
var label = GmailApp.getUserLabelByName("AnotherLabel");
thread.addLabel(label);
Logger.log("No match");
}
}

Google Apps Script Compare Active User to Array

Not sure why this isn't working. I have a simple script that grabs the active user and checks to see if they are in an array of allowed senders. If they are, then it runs another function sendEmail(). If they are not in the array, then it throws an error.
function send(){
var ValidEmailsArray = ['email1#gmail.com', 'email2#gmail.com', 'email3#gmail.com']; //can be any number of emails.
var ActiveUser = Session.getActiveUser().getEmail();
for (var i = 0; i < ValidEmailsArray.length; i++){
if (ActiveUser.indexOf(ValidEmailsArray[i]) == -1) {
throw new Error('Woah! You must send the email from the ' + ValidEmailsArray + ' account.');
}
else {
sendEmail();
return;
}
}
As it is now, it throws the error even if the ActiveUser is in the ValidEmailsArray.
Any help would be much appreciated. Thanks!
Issue:
ActiveUser is of type string.
if (ActiveUser.indexOf(ValidEmailsArray[i]) == -1) {throw new Error
says that if ActiveUser doesn't contain the first email address in ValidEmailsArray, throw a error.
Solution:
Use Array#includes instead of a loop.
Snippet:
function send(){
const validEmailsArray = ['email1#gmail.com', 'email2#gmail.com', 'email3#gmail.com']; //can be any number of emails.
const activeUser = Session.getActiveUser().getEmail();
if(validEmailsArray.includes(activeUser)) sendEmail()
else throw new Error("?!")
}

How to send email based on cell contents in Google Sheets using custom function

I have a spreadsheet where data will be continuously inputted (through Google Forms and user input), and I would like to have code that will send out a single email for a single row, which I can call multiple times as rows get completed. Currently, I created a custom function that is supposed to do this but when I run it I get an error which reads "You do not have permission to call MailApp.sendEmail. Required permissions: https://www.googleapis.com/auth/script.send_mail (line 6)."
function sendDirectiveResponse(name, message, response, emailAddress) {
var sheet = SpreadsheetApp.getActiveSheet();
var emailSent = "Email Sent";
var message = "Received: " + message + "\n\n Response: " + response;
var subject = "Message Response";
MailApp.sendEmail(emailAddress, subject, message);
return emailSent
}
I expect an email to be sent out and the cell to show "Email Sent" but, instead, it says "#ERROR" and no email is sent.
Sending emails based upon cell contents
function sendMyEmails() {
var ss=SpreadsheetApp.getActive();
//the next few commands create a newsheet and load it with sample data so that you do not have to. You will want to remove this and use a spreadsheet of your own choosing
var sh=ss.insertSheet();//setup
var init=[['Email','Subject','Message','Status'],['sample1#gmail.com','Email Testing','Have a great day.',''],['sample2#gmail.com','Email Testing','Have a great day.',''],['sample3#gmail.com','Email Testing','Have a great day.',''],['sample4#gmail.com','Email Testing','Have a great day.','']];//setup
sh.getRange(1,1,init.length,init[0].length).setValues(init);//setting up data
var rg=sh.getDataRange();//get data
var vA=rg.getValues();
var hObj=[];
var html='';
for(var i=0;i<vA.length;i++) {
for(var j=0;j<vA[i].length;j++) {
hObj[vA[0][j]]=vA[i][j];//this loads the object with all of the data for this row. And you can now refer to it with hObj.headertitle
}
if(!hObj.Status) {//When you supply your own data this will prevent function from sending emails more than once
html+=Utilities.formatString('<br />Email: <strong>%s</strong> Subject: <strong>%s</strong> Message: <strong>%s</strong>', hObj.Email,hObj.Subject,hObj.Message)
sh.getRange(i+1,vA[0].indexOf('Status') + 1).setValue('Done')
//MailApp.sendEmail(hObj.Email, hObj.Subject, hObj.Message);//removed for testing you will have to uncomment this line to actually send email
}
}
var ui=HtmlService.createHtmlOutput(html).setWidth(1000);
SpreadsheetApp.getUi().showModelessDialog(ui, 'Email Testing');//this provides a dialog to show you what would have been sent if everything were enabled.
}

getMessageById() slows down

I am working on a script that works with e-mails and it needs to fetch the timestamp, sender, receiver and subject for an e-mail. The Google script project has several functions in separate script files so I won't be listing everything here, but essentially the main function performs a query and passes it on to a function that fetches data:
queriedMessages = Gmail.Users.Messages.list(authUsr.mail, {'q':query, 'pageToken':pageToken});
dataOutput_double(sSheet, queriedMessages.messages, queriedMessages.messages.length);
So this will send an object to the function dataOutput_double and the size of the array (if I try to get the size of the array inside the function that outputs data I get an error so that is why this is passed here). The function that outputs the data looks like this:
function dataOutput_double(sSheet, messageInfo, aLenght) {
var sheet = sSheet.getSheets()[0],
message,
dataArray = new Array(),
row = 2;
var i, dateCheck = new Date;
dateCheck.setDate(dateCheck.getDate()-1);
for (i=aLenght-1; i>=0; i--) {
message = GmailApp.getMessageById(messageInfo[i].id);
if (message.getDate().getDate() == dateCheck.getDate()) {
sheet.insertRowBefore(2);
sheet.getRange(row, 1).setValue(message.getDate());
sheet.getRange(row, 2).setValue(message.getFrom());
sheet.getRange(row, 3).setValue(message.getTo());
sheet.getRange(row, 4).setValue(message.getSubject());
}
}
return;
};
Some of this code will get removed as there are leftovers from other types of handling this.
The problem as I noticed is that some messages take a long time to get with the getMessageById() method (~ 4 seconds to be exact) and when the script is intended to work with ~1500 mails every day this makes it drag on for quite a while forcing google to stop the script as it takes too long.
Any ideas of how to go around this issue or is this just something that I have to live with?
Here is something I whipped up:
function processEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var messages = Gmail.Users.Messages.list('me', {maxResults:200, q:"newer_than:1d AND label:INBOX NOT label:PROCESSED"}).messages,
headers,
headersFields = ["Date","From","To","Subject"],
outputValue=[],thisRowValue = [],
message
if(messages.length > 0){
for(var i in messages){
message = Gmail.Users.Messages.get('me', messages[i].id);
Gmail.Users.Messages.modify( {addLabelIds:["Label_4"]},'me',messages[i].id);
headers = message.payload.headers
for(var ii in headers){
if(headersFields.indexOf(headers[ii].name) != -1){
thisRowValue.push(headers[ii].value);
}
}
outputValue.push(thisRowValue)
thisRowValue = [];
}
var range = ss.getRange(ss.getLastRow()+1, ss.getLastColumn()+1, outputValue.length, outputValue[0].length);
range.setValues(outputValue);
}
}
NOTE: This is intended to run as a trigger. This will batch the trigger call in 200 messages. You will need to add the label PROCESSED to gmail. Also on the line:
Gmail.Users.Messages.modify( {addLabelIds:["Label_4"]},'me',messages[i].id);
it shows Label_4. In my gmail account "PROCESSED" is my 4th custom label.

Google Script not Finishing Successfully

I recently adapted a short piece of script to automatically send an e-mail to me when a specific response appears (the word FAIL) in a 'Google docs' spreadsheet, that collects responses from volunteers using a 'Google Form' to record their weekly checks on life-saving equipment.
The email on FAIL is working well, having tested it with a few FAIL responses. However, as the form 'owner' I am receiving notifications from Google, for each normal PASS response submitted, telling me that the script failed to finish successfully.
8/4/15 10:57 AM
SendGoogleForm
ReferenceError: "found" is not defined. (line 35, file "Code")
formSubmit
It finishes successfully if a FAIL response is submitted, so I suspect that, due to my inexperience with scripts, I have not defined what should happen when found is 'not true'.
It's probably glaringly obvious to a more experienced script writer but none of the things I have tried with 'Else' seem to work.
Any suggestions would be greatly appreciated.
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers)
ScriptApp.deleteTrigger(triggers[i]);
ScriptApp.newTrigger("SendGoogleForm")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit().create();
}
function SendGoogleForm(e) {
if (MailApp.getRemainingDailyQuota() < 1) return;
// Recipients email address
var email = "12345#abcde.co.uk";
// Subject for Google Form email notification
var subject = "Weekly Checks - FAIL Submitted";
var s = SpreadsheetApp.getActiveSheet();
var columns = s.getRange(1, 1, 1, s.getLastColumn()).getValues()[0];
var message = "A check form has been submitted containing a ** FAIL ** response: ";
// Look for a 'FAIL' response
var key = columns[keys];
for ( var keys in columns ) {
if ( e.namedValues[columns[keys]] == 'FAIL') {
found = true;
}
}
// Only include form fields that are not blank
if (found)
for (var keys in columns) {
var key = columns[keys];
if (e.namedValues[key] && (e.namedValues[key] !== "")) {
message += key + ' : ' + e.namedValues[key] + "\n\n";
}
}
MailApp.sendEmail(email, subject, message);
}
Your found variable is null whenever a non-"FAIL" occurs in your script and I think your if(found) is creating the error message.
You could Initialise your variable:
var found = false;
before you start testing it. That way, your if(found) will be false and it will skip down to your send email code.
Perhaps u can use
if ( e.namedValues["Pass or Fail"] == "Fail" ) { ... }
where "Pass or Fail" is the form question title. And also use e.range.getRow() to capture the sheet row that the responses were written.