Retuning a GMail script to prevent "Exceeded maximum execution time" - google-apps-script

I use a gmail account to receive status updates from devices which I have listed in a Google spreadsheet by its user and serial number. The status update email will have the serial number of the device in it's email subject and will usually be sent about once a week per device. Due to the number of devices (600+), I get a pretty constant influx of status updates from various devices every day. What I'm trying to do is setup a script that will search my Inbox and create a list of devices that I haven't received a status update email from which would prompt me to figure out why that device isn't reporting in.
I've modified a script I found online at: http://forums.mozillazine.org/viewtopic.php?f=46&t=2740775. It works by obtaining a device serial number listed in a spreadsheet and searching the Inbox for an email subject with that serial number sent within the past 15 days. The script works fine when the spreadsheet has about 200 items, but beyond that I start getting an "Exceeded maximum execution time" error and I need it to do 600+ items now and more as time goes on. I also had to add the Utilities.sleep(1000) line to avoid a "Service invoked too many times in a short time: gmail rateMax" error. Is there a better way to write this script to avoid these errors?
function SearchEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var waittime = 15;
var emailTo = "temp#email.com";
var emailSubject = "NO EMAIL IN " + waittime + " DAYS FROM FOLLOWING DEVICES";
var emailText = "";
for (var i = 1; i < data.length; i++) {
Utilities.sleep(1000);
var j = i + 1;
var gsearch = "in:inbox subject:(" + data[i][1] + ") newer_than:" + waittime + "d";
var threads = GmailApp.search(gsearch, 0, 1);
if (threads.length == 0) {
var emailText = emailText + j + " Device: " + data[i][1] + " User: " + data[i][0] + "\n";
}
}
MailApp.sendEmail(emailTo, emailSubject, emailText);
}

I'd suggest you see the problem from another point of view.
Could you find a common string to all the emails that your report is sending ? Something like "device status" in the subject or in the body...
This way you could search your inbox for that string, put a label on it so that you avoid rechecking them and use a simple function to check in your spreadsheet if that number is in the list.
function checkList(device){
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for(var n=0;n<data.length;n++){
if(data[n][0]==device){ return true};// assuming number are in column A
}
return false;
}
then send a mail only if(checkList(device)!=true){
This would be far more efficient I think.

Related

Google Apps Script to send email notification, if the email from an expected sender is not received every 1 hour

I have managed to get email notification alert if I have not received an expected email in past XX day.
By following the guidelines given in - http://baumbach.com/google-script/
It is working as expected.
Here is the javascript code-
function SearchEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
// "in:all " searches trash and spam email folders. Remove "in:all " + to only search all mail and inbox
var gsearch = "in:all " + "from:(" + data[i][1] + ") to:(" + data[i][2] + ") subject:(" + data[i][3] + ") newer_than:" + data[i][4] + "d";
var threads = GmailApp.search(gsearch, 0, 1);
if (threads.length == 0) {
var emailSubject = "No email in " + data[i][4] + " days: - " + data[i][5];
var emailText = "Note: " + data[i][5] + "\r\n\r\nSearch was: " + gsearch;
var emailTo = data[i][0];
MailApp.sendEmail(emailTo, emailSubject, emailText);
}
}
}
But, What I want is, to get email notification alert if I have not received an expected email in past 1 Hour and not days.
Can anyone tell me how to do this and where should I change the code??
Thanks a lot in advance.
EDIT
In the guide from the link you provided and in your code, the date used to query the messages from Gmail comes from the linked spreadsheet you have, so this part would be set by you.
To query the message the code uses the gsearch variable which is formatted by the q parameter guidelines using the Gmail search operators and inserting data in the query from the data variable which is obtained from using the getDataRange and getValues methods to your sheets. For what I see in the search operators, there's no option to query the messages by time, the minimum is 1 day.
Also, they explain they set up a time driven trigger to execute the SearchEmail function once a day. For this part you can use the everyHours method to create a trigger to execute the function once every hour. Run the below function once in order to create the trigger:
function createTimeTriger1hour() {
ScriptApp.newTrigger("SearchEmail")
.timeBased()
.everyHours(1)
.create();
}

GScript: Track number of emails sent using mass email Gscript code

I am trying to modify this GScript to count the number of emails sent. I would like to track the total number of emails sent using this function. Would it be easier to do it within this code or to use a code to search the sent box of the user? I believe I know how to make a count value get stored temporarily, but I am unsure how to store it for a greater period of time.
function WhiteTicket() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Email Outline');
var lastRow = sheet.getRange("J1").getValue();
var range = sheet.getRange("A3:"+ lastRow);
var UserData = range.getValues();
for (i in UserData) {
var row = UserData[i];
var firstname = row[1];
var lastname = row[2];
var email = row[3];
var whiteticket = row[6];
if (whiteticket != '0') {
var esubject = "New Tickets";
MailApp.sendEmail({
to: row[3],
subject: esubject,
htmlBody:"Hello " + firstname + "," + '<br />'+'<br />' +
"Please note that you have " + whiteticket + " ticket(s)."});
}
}
if (ok) {
EmailComplete();;
}
}
Requirement:
Record amount of emails sent by this script.
Solution:
Use GmailApp.search() and pass to length to count the amount of emails sent.
Example:
var total = GmailApp.search('in:sent subject:"'+esubject+'"').length;
Logger.log(total);
This is pretty self explanatory, it'll search all of the emails in your sent folder with subject matching the subject string you're assigning with var esubject. The script above just writes it to the log using Logger.log().

Sending notification from google spreadsheet using time-driven events with condition

I would like to receive notifications when domain will expire. So I created a spreadsheet with a list of website and date of expiration. It has also a condition in Column A that when a domain is about to expire in 10 days it will appear Send notification as cell value. You can view my spreadsheet here.
With the values in column A, I would like to receive emails telling that the www.sample.com will expire on --some date here--.
For example, when Column A have new values equal to Send notification, then send email.
What I have tried and encountered:
var cell = sheet.getActiveCell().getA1Notation();
var row = sheet.getActiveRange().getRow();
var cellvalue = sheet.getActiveCell().getValue().toString();
var recipients = "youremail#gmail.com";
var domain = '';
var expirydate= '';
if(cell.indexOf('A')!=-1 && cell.indexOf('A') == 'Send notification'){
domain = sheet.getRange('B'+ sheet.getActiveCell().getRowIndex()).getValue();
expirydate = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'Expiry Notification : '+sheet.getName();
var body = 'Website will expire! ' + domain + ' is about to expire on ' + expirydate;
MailApp.sendEmail(recipients, subject, body);
Logger.log(body);
Trying this script only sends me notification without the value of the cell.
I would like this works even when the spreadsheet is not open / I am offline. So I guess I will be using the time driven event (every week).
Any help is much appreciated!
Prepare yourself, my answers tend to be long and explanatory.
Take this block:
if(cell.indexOf('A')!=-1 && cell.indexOf('A') == 'Send notification'){
domain = sheet.getRange('B'+ sheet.getActiveCell().getRowIndex()).getValue();
expirydate = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()
}
In this block we can see that the if will always return FALSE. That is because cell.indexOf('A') cannot be a string, it will be an integer of the index. You want to be checking the value. I assume that is why you have the cellvalue variable.
Furthermore, your use of getRange() is also off. Why bother with A1 notation if you are getting indexes anyway. Instead I will go over the code and offer a different way of coding this.
Ok, let's start from the top of the code. You mentioned that you want this to run offline. We immediately get a problem here:
var cell = sheet.getActiveCell().getA1Notation();
this will be meaningless once you fire script based on a timer. I would recommend batching your data collection. First to be sure that you are not using getActiveSheet():
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Send notification')
OR
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]
to get the first sheet or the sheet by it's name. Then we get the entire list into a 2D array.
var vals = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues()
So we wish to check all domains. So now that we have all the values we wish to loop through them (we assume we left var domain and var expirydate as is)
var i
for (i = 0; i < vals.length; i++) {
if (vals[i][0] == 'Send notification!') {
domain = vals[i][1] //we get the domain name.
expirydate = vals[i][3] //we get the expire date
sendNotification(domain, expirydate); //use a seperate fu
}
}
where in the above block code I would seperate the function
function sendNotification(domain, expirydate) {
var subject = 'Expiry Notification : '+ domain;
var body = 'Website will expire! ' + domain + ' is about to expire on ' + expirydate;
MailApp.sendEmail(recipients, subject, body);
Logger.log(body);
}
of course you can leave the code inside of the for loop, but this will look cleaner. Also, I am not sure you really wanted var subject = 'Expiry Notification : '+sheet.getName(); because that will send all emails with the title Expiry Notification : Send notification because that is the sheet name (the tab at the bottom of the spreadsheet)

Excluding a label from a Google Apps Script

This is my first post :)
I am using a Google Apps Script that tracks e-mail from the last 7 days that have not had a response (basically tracks emails where my message is the last one).
This is the code here:
// This script searches Gmail for conversations where I never received a response
// and puts them in a NoResponse label
var DAYS_TO_SEARCH = 7; // look only in sent messages from last 7 days, otherwise script takes a while
var SINGLE_MESSAGE_ONLY = false; // exclude multi-message conversations where I sent the last message?
function label_messages_without_response() {
var emailAddress = Session.getEffectiveUser().getEmail();
Logger.log(emailAddress);
var EMAIL_REGEX = /[a-zA-Z0-9\._\-]+#[a-zA-Z0-9\.\-]+\.[a-z\.A-Z]+/g;
var label = GmailApp.createLabel("AwaitingResponse");
var d = new Date();
d.setDate(d.getDate() - DAYS_TO_SEARCH);
var dateString = d.getFullYear() + "/" + (d.getMonth() + 1) + "/" + d.getDate();
threads = GmailApp.search("in:Sent after:" + dateString);
for (var i = 0; i < threads.length; i++)
{
var thread = threads[i];
if (!SINGLE_MESSAGE_ONLY || thread.getMessageCount() == 1)
{
var lastMessage = thread.getMessages()[thread.getMessageCount()-1];
lastMessageSender = lastMessage.getFrom().match(EMAIL_REGEX)[0];
if (lastMessageSender == emailAddress)
{
thread.addLabel(label);
Logger.log(lastMessageSender);
}
}
}
}
The problem is at the moment, when the script runs the un-replied messages go into the "NoResponse" label which is great. However, when I delete the label from the emails that I don't need to follow up on, they come back up again when the script runs again.
My question is:
Would there would be a way to apply a label to messages that don't need to be followed up on, and then work that into the script, so that the script knows to exclude that label?
Any help would be fantastic :)
Thanks
Aidan
May be the script can apply two labels - NoResponse and Processed. You can remove the NoResponse label manually and yet the Processed label would stay.
The filter can be modified like:
threads = GmailApp.search("in:Sent -in:Processed after:" + dateString);

Google app script - how to count number of google form response

It is my first time writing google app script and I am desperately need help.
The purpose is to set up a workshop sign up form. Based on how many people already signed up, an email is sent out to inform if sign up was successful, or was put in the wait list.
I copied code from a tutorial. But need help to get the count of form responses. Here is how it looks like now:
function onFormSubmit(e) {
var timestamp = e.values[0];
var yourName = e.values[1];
var toAddress = e.values[2];
var subject = "Workshop Confirmation";
var emailBody = "Thank you for your submitted on " + timestamp
var num_response = COUNT_NUMBER_OF_RESPONSE // <-- need help here
var LIMIT = 15
if (num_response <= LIMIT) {
emailBody = emailBody + "\n\nYou are enrolled in the workshop";
}
else {
var wait = num_response - LIMIT
emailBody = emailBody + "\n\nThe workshop is full. You are #" + wait + " in the waiting list"
}
emailBody = emailBody + "\n\nThe details you entered were as follows: " +
"\nYour Name: " + yourName +
"\nYour Email: " + toAddress ;
MailApp.sendEmail(toAddress, subject,
emailBody, optAdvancedArgs);
}
I have no clue how to find right answer in the google app document. Any help would be greatly appreciated!
How about the composite function
FormApp.getActiveForm().getResponses().length
no need to go around looking for the spreadsheet (since in my experience, the spreadsheet is not always up to date, whereas the form is)
From what I see in the tutorial, this script is embedded in a spreadsheet so the easiest would be to count the number of rows and substract 1 because of the headers...
There is a method for that : getLastRow(), the doc refered in this link should give you enough information to write the few lines of code you need...
test :
function xx(){
var lr = SpreadsheetApp.getActiveSheet().getLastRow()-1;
Logger.log(lr);
}
Script on form (not spreadsheet)
function onFormSubmit(e) {
var num_response = FormApp.getActiveForm().getResponses().length
var LIMIT = 20 //or whatever
if (num_response < LIMIT) {
}
else {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(false);
}
}