GmailApp.search() seems to fail correct data - google-apps-script

I am trying to get no of emails for yesterday according to one particular subject.
Usually, what I do at midnight 1 o'clock I count the no of emails for a particular subject and send the mail through Google script. At 3 o'clock I trigger one delete trigger which starts deleting mail of previous day.
so by this, I ensure I don't have any mail for the previous day.
var yesterday = "2017/7/10";
var today = "2017/7/11";
var query = "after:"+yesterday+" before:"+today+" subject: abcd";
To count no of emails I have written below function
function getEmailCount(query) {
var threads = GmailApp.search(query, 0, 500);
Logger.log(threads);
var total = 0;
for (var i = 0; i < threads.length; i++) {
var messagesInThread = threads[i].getMessageCount();
var msg = threads[i].getMessages()[0].getSubject()
total = total + messagesInThread;
}
Logger.log(msg)
Logger.log("Query %s", query);
Logger.log("No. Of Threads %s", threads.length);
Logger.log("No. Of Emails %s", total);
return total;
}
When I check for emails in Gmail with above subject I get only 8 but my script is returning 25 mails. Any help will be highly appreacated .

It is interesting, GMailApp does return some emails within threads outside of the search parameters that the Gmail web app was not. I have a suspicion this was a time-zone related thing due to the nature of the specific emails I was looking at.
Some modifications to your code can add for a couple of checks that differentiate the raw results and some of the UX that the web app throws in for free. Additionally, this filters threads and messages that strictly adhere to the search criteria.
function getEmailCount(query) {
var yesterday = "2017/06/07",
today = "2017/06/08",
yesterdayDate = new Date(yesterday),
todayDate = new Date(today);
query = query || "after:" + yesterday + " before:" + today + "";
var threads = GmailApp.search(query).reduce(function(validThreads, thread, idx) {
var messages = thread.getMessages().reduce(function(messages, message, idx) {
var isChat = message.isInChats(),
isDeleted = message.isInTrash(),
sent = message.getDate();
if (!isDeleted && !isChat && sent < todayDate && sent > yesterdayDate) {
messages.push(message.getSubject());
}
return messages;
}, []);
if (messages.length > 0) { validThreads[idx] = messages; }
return validThreads;
}, {});
var totalMessages = Object.keys(threads).reduce(function(count, thread) {
return count += threads[thread].length;
}, 0);
Logger.log("Query: %s", query);
Logger.log("No. Of Threads: %s", Object.keys(threads).length);
Logger.log("No. Of Emails: %s", totalMessages);
return totalMessages;
}

Related

How to filter out all emails that came from a mailing list in Gmail

Is there a way to filter out all emails that came from a mailing list within Gmail or Google Apps Script using a search query. I know you can filter out a specific email address using list:info#example.com. But I want a catch-all type of query or even a query to catch-all from a specific domain such as list:#example.com. However, this does not work. Any ideas? Any help is greatly appreciated, thank you!
This function will trash all messages from all inbox thread that are not in the list.
function emailFilter() {
var list=['a#company.com','b#company.com','c#company.com','d#company.com','e#company.com'];
var threads=GmailApp.getInboxThreads();
var token=null;
for(var i=0;i<threads.length;i++) {
if(threads[i].getMessageCount()) {
var messages=threads[i].getMessages();
for(var j=0;j<messages.length;j++) {
if(list.indexOf(messages[j].getFrom()==-1)) {
messages[j].moveToTrash();
}
}
}
}
}
I haven't tested it because I keep my inbox empty all of the time. You might want to replace 'moveToTrash()' to 'star()' for testing
What I could understand from your question and your comments, you need to filter the emails in a user's inbox that he has received, which don't only contain a certain label, but also a certain domain. If I understood well this code can help you:
function checkLabels() {
// Get the threads from the label you want
var label = GmailApp.getUserLabelByName("Label Test List");
var threadArr = label.getThreads();
// Init variable for later use
var emailDomain = '';
// Iterate over all the threads
for (var i = 0; i < threadArr.length; i++) {
// for each message in a thread, do something
threadArr[i].getMessages().forEach(function(message){
// Let's get the domains from the the users the messages were from
// example: list:#example.com -> Result: example.com
emailDomain = message.getFrom().split('<').pop().split('>')[0].split('#')[1];
// if emailDomain is equal to example.com, then do something
if(emailDomain === 'example.com'){
Logger.log(message.getFrom());
}
});
}
}
Using the Class GmailApp I got a certain label with the .getUserLabels() method and iterate through the threads thanks to the .getInboxThreads method. With a second loop and the .getMessages() you can get all the messages in a thread and for knowing the one who sent them, just use the .getFrom() method.
Docs
For more info check:
Gmail Service.
Class GmailMessage.
Class GmailThread.
So I was able to avoid replying to emails that come from a mailing list address by using the getRawContent() method and then searching that string for "Mailing-list:". So far the script is working like a charm.
function autoReply() {
var interval = 5; // if the script runs every 5 minutes; change otherwise
var date = new Date();
var day = date.getDay();
var hour = date.getHours();
var noReply = ["email1#example.com",
"email2#example.com"];
var replyMessage = "Hello!\n\nYou have reached me during non-business hours. I will respond by 9 AM next business day.\n\nIf you have any Compass.com related questions, check out Compass Academy! Learn about Compass' tools and get your questions answered at academy.compass.com.\n\nBest,\n\nShamir Wehbe";
var noReplyId = [];
if ([6,0].indexOf(day) > -1 || (hour < 9) || (hour >= 17)) {
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('from:#example.com is:inbox after:' + timeFrom);
var label = GmailApp.getUserLabelByName("autoReplied");
var repliedThreads = GmailApp.search('label:autoReplied newer_than:4d');
// loop through emails from the last 4 days that have already been replied to
for (var i = 0; i < repliedThreads.length; i++) {
var repliedThreadsId = repliedThreads[i].getMessages()[0].getId();
noReplyId.push(repliedThreadsId);
}
for (var i = 0; i < threads.length; i++) {
var message = threads[i].getMessages()[0];
var messagesFrom = message.getFrom();
var email = messagesFrom.substring(messagesFrom.lastIndexOf("<") + 1, messagesFrom.lastIndexOf(">"));
var threadsId = message.getId();
var rawMessage = message.getRawContent();
var searchForList = rawMessage.search("Mailing-list:");
var searchForUnsubscribe = rawMessage.search("Unsubscribe now");
// if the message is unread, not on the no reply list, hasn't already been replied to, doesn't come from a mailing list, and not a marketing email then auto reply
if (message.isUnread() && noReply.indexOf(email) == -1 && noReplyId.indexOf(threadsId) == -1 && searchForList === -1 && searchForUnsubscribe === -1){
message.reply(replyMessage);
threads[i].addLabel(label);
}
}
}
}

How to check Gmail Thread for Replies from Email

I am creating a basic CRM that needs to mark when a thread has been replied to.
I have created a script that can scan my inbox for threads from a list of emails in a sheet, check the last message in each thread and collect the .getFrom in order to see if I was the last to reply.
However, I can't figure out how to check if there has been a response from the person who's been contacted throughout the whole thread.
Here's the script that checks for the last message. (It's an extract of a larger script in case any references are missing):
Example Sheet
function UpdateStatus() {
// Connect to our active sheet and collect all of our email addresses in column G
var sheet = SpreadsheetApp.getActiveSheet();
var totalRows = sheet.getLastRow();
var range = sheet.getRange(2, COLUMN_WITH_EMAIL_ADDRESSES, totalRows, 1);
var emails = range.getValues();
// Attempt to iterate through 100 times (although we'll timeout before this)
for (var cntr = 0; cntr<100; cntr++ ) {
// If we've reached the end of our last, wrap to the front
if (lastRowProcessed >= totalRows) lastRowProcessed = 1;
// Increment the row we're processing
var currentRow = lastRowProcessed+1;
// Get the email address from the current row
var email = emails[currentRow-2][0];
// If the email address field is empty, skip to the next row
if (!email) {
lastRowProcessed = currentRow;
cache.put("lastRow", currentRow, 60*60*24);
continue;
}
// Look for all threads from me to this person
var threads = GmailApp.search('from:me to:'+email);
// If there are no threads, I haven't emailed them before
if (threads.length == 0) {
// Update the spreadsheet row to show we've never emailed
var range = sheet.getRange(currentRow,13, 1, 4 ).setValues([["NEVER", "", "", ""]] );
// And carry on
lastRowProcessed = currentRow;
cache.put("lastRow", currentRow, 60*60*24); // cache for 25 minutes
continue;
}
// Beyond a reasonable doubt
var latestDate = new Date(1970, 1, 1);
var starredMsg = "";
var iReplied = ""
// Iterate through each of the message threads returned from our search
for (var thread in threads) {
// Grab the last message date for this thread
var threadDate = threads[thread].getLastMessageDate();
// If this is the latest thread we've seen so far, make note!
if (threadDate > latestDate) {
latestDate = threadDate;
// Check to see if we starred the message (we may be back to overwrite this)
if (threads[thread].hasStarredMessages()) {
starredMsg = "★";
} else {
starredMsg = "";
}
// Open the thread to get messages
var messages = threads[thread].getMessages();
// See who was the last to speak
var lastMsg = messages[messages.length-1];
var lastMsgFrom = lastMsg.getFrom();
// Use regex so we can make our search case insensitive
var re = new RegExp(email,"i");
// If we can find their email address in the email address from the last message, they spoke last
// (we may be back to overwrite this)
if (lastMsgFrom.search(re) >= 0) {
iReplied = "NO";
} else {
iReplied = "YES";
}
}

Time-Driven trigger for mail forwarding

I'm attempting to test a script that I'm working on. The script is fine, it executes successfully when I trigger it manually. When I add a time driven script of every minute interval the scrips starts throwing an exception after couple of hrs .
Exception: Service invoked too many times for one day: gmail
I checked the daily quota of email and found that i still have mail quota left
var quota = MailApp.getRemainingDailyQuota();
Logger.log(quota);
Also I am able to receive the try catch email but the mails are not forwarded .
Is this because of the execution time quota associated with the trigger? Below is the code
function MailForward() {
try{
var glabel = createLabel_("Mail-Forwarded");
var rtm_email = 'abc#abc.com';
var from_email = Session.getActiveUser().getEmail();
var threads = GmailApp.search('in:inbox is:unread newer_than:1d');
var mForward = 0;
for (var i=0;i<threads.length;i++) {
var messages=threads[i].getMessages();
for (var m = 0; m < messages.length; m++){
if (messages[m].isUnread()){
mForward = 0;
var mlabels = threads[i].getLabels();
for (var j = 0; j < mlabels.length; j++) {
Logger.log(mlabels[j].getName());
if (mlabels[j].getName() === "Mail-Forwarded") {
mForward = 1;
}
}
if (mForward===0) {
// Logger.log(messages.length)
// Logger.log(messages[m].getFrom());
var from = messages[m].getFrom();
//Logger.log(messages[m].getDate());
var date = messages[m].getDate();
// Logger.log(messages[m].getSubject());
var subject = messages[m].getSubject();
// Logger.log(messages[m].getTo());
var to = messages[m].getTo();
var body = messages[m].getBody();
var attachment = messages[m].getAttachments();
var emailoptions = ("---------- Forwarded message ----------" +'<br>'+'From: '+from+ "<'" + from.replace(/^.+<([^>]+)>$/, "$1") +"'>"+'<br>'+ 'Date: '+date+'<br>'+ 'Subject: ' +subject+'<br>'+
'To: ' +to+ "<'" + to.replace(/^.+<([^>]+)>$/, "$1") +"'>"+'<br>'+'<br>'+'<br>');
messages[m].forward(rtm_email,{htmlBody: emailoptions + body , Attachment: attachment});
glabel.addToThread(threads[i]);
Logger.log(glabel.getName());
messages[m].markRead();
mForward = 1;
}
}
}
}
} catch(e) {
MailApp.sendEmail("abc#abc.com", "Exception found in Sript", e );
Logger.log(e);
}
}
You checked quota using MailApp.getRemainingDailyQuota(); not GmailApp. These are two different services.
The quota method returns only "the number of remaining emails a user can send for the rest of the day." But the limit you are hitting is the number of times you invoked the service, for whatever purpose.
You are using GmailApp a lot to access existing messages, not so much to send new ones. In particular, you are checking every message in every thread from today, and do it every minute. That's a lot of API calls: getMessages, isUnread, etc.
One way to reduce the number of API calls is to have more targeted search. The after: search parameter accepts Unix timestamp, which makes it possible to do the following:
function doSomethingWithNewEmail() {
var interval = 5; // if the script runs every 5 minutes; change to 1 if it runs every minute
var date = new Date();
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('is:inbox after:' + timeFrom);
for (var i = 0; i < threads.length; i++) {
// do something
}
}
I successfully used the above approach with 5 minute interval. It may work with 1 minute too, since most of the time search will be the only API call made by the script.

Inconsistencies between app scripts GmailApp.search and the search in gmail interface

I'm trying to build a google app script to import mail received from an online form to a spreadsheet.
I am using two labels: One "to_process" is added by a gmail filter, the other one "processed" is added by this script after the email was added to the sheet.
I am searching for all emails that have "to_process" but not "processed" using the search query 'label:to_process !label:processed in:all'
I got it working partially (see the core of the script below)
I'm running the script using the script editor run function.
The problem is that using the same query in gmail interface i get more than 100 emails, but in the log of the script I get 6, and they are all processed.
Am I missing something?
function extractInfo() {
var step = 30;
var max = 500;
var currentStep = 0;
while(max--) {
var threads = GmailApp.search('label:to_process !label:processed in:all', currentStep++ * step, step);
if(threads.length == 0) break;
Logger.log("-------- found threads: " + threads.length);
var threadId = threads.length;
while(threadId--) {
var thread = threads[threadId];
thread.refresh();
if(hasLabel(thread, "processed")) {
Logger.log("was processed: " + thread.getPermalink())
continue;
}
if(!hasLabel(thread, "to_process")) {
Logger.log("isn't mark to process: " + thread.getPermalink())
continue;
}
var messages = thread.getMessages();
var messageId = messages.length;
while(messageId--) {
var message = messages[messageId];
var row = extractMessageData(message);
sheet.appendRow(row);
GmailApp.markMessageRead(message);
}
threads[threadId].addLabel(processedLabel);
}
}
}
function hasLabel(thread, name) {
var labels = thread.getLabels();
var l = labels.length;
while(l--) {
if(labels[l].getName() == name) {
return true;
}
}
return false;
}
After much trail and error I partially figured this out.
What I see in gmail ui is in fact message, and not threads. But handling labels is a per thread thing.
I simply pulled all message of all threads from a given label, and then processed theses.
If a thread has a label "processed" it doesn't mean all it's messages were processed, which is a problem.
There are still inconsistencies regarding the number of messages i see in the UI and what I get using the API though.

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);