Google Apps Script for loop not processessing all items from getThreads() - google-apps-script

I am attempting to create a function that reads the body of emails and extracts parts to place in a sheet.
I am currently using the below code to pull the emails.
var label = GmailApp.getUserLabelByName("VOIDS");
var threads = label.getThreads();
for (var i = 0; i <= threads.length; i++)
{
var message = threads[i].getMessages();
var body = message[0].getPlainBody();
//email processing//
threads[i].removeLable(label)
}
I've got the loop to do what I need it to do as far as processing the email and placing it where I need it, however it seems to be skipping emails. I've left out the code for the process as it's just a bunch of split() functions on the body variable to extract the appropriate information and paste it into a sheet.
The total number of emails skipped varies based on how many it has to process, but re-running the script results in the same emails being skipped each time.
All emails are having their label removed and all emails are identical save for a few value changes.
This is my first time working with GmailApp outside of sending emails. I'm sure that this is something super simple that I'm just missing, but despite all my Google searching I can't seem to find a solution.
Thank you!

Please bear in mind that getThreads might not return all the threads in your mailbox.
From the official reference docs
getThreads()
Gets the threads that are marked with this label.
This calls fail when the size of all threads is too large for the system to handle. Where the thread size is unknown, and potentially very large, please use getThreads(start, max) and specify ranges of the threads to retrieve in each call.
Resources
https://developers.google.com/apps-script/reference/gmail/gmail-label#getthreads
Related
Trying to understand getThreads in GAS
Make getThreads() app script call count over 500

A thread can contain more than one email
The line var body = message[0].getPlainBody(); implies that you are only proceeding the body of the first message of each thread.
To apply your request t all emails, you need to create a second loop, iterating through each email of each thread.
Sample:
var label = GmailApp.getUserLabelByName("VOIDS");
var threads = label.getThreads();
for (var i = 0; i <= threads.length; i++){
var messages = threads[i].getMessages();
for (var j = 0; j <= messages.length; j++){
var message = messages[j];
var body = message.getPlainBody();
//email processing//
}
threads[i].removeLable(label);
}

Related

Using Gmail labels in Apps Script for Sheets

I am trying to evaluate emails with an existing label called "TestLabel" (applied via a simple Gmail filter on receipt), then once processed via a script called "extractDetails", remove the label. My problem is that old emails, that previously had the label but have already been processed, continue to meet the condition in subsequent executions. I have verified (at least visually) that the label is no longer attached. I have also turned off "conversation" view in Gmail, hoping/believing that the nested thread was the issue. Strangely, not all previous emails get reprocessed - but, for example, the most recent 35 or so from a list of hundreds. The only thing I've found that solves this is permanently deleting the prior emails, but I'd like to avoid doing so, and I am also curious to simply solve the problem.
My code is as follows:
function getGmailEmails(){
var label = GmailApp.getUserLabelByName('TestLabel');
var threads = label.getThreads();
var i = 0;
for(var i = threads.length - 1; i >=0; i--){
var messages = threads[i].getMessages();
for (var j = 0; j <messages.length; j++){
var message = messages[j];
extractDetails(message);`
}
threads[i].removeLabel(label);
threads[i].markRead()
threads[i].moveToArchive(); ;
}
}
Thank you to this community for your assistance. Hopefully I posed this question properly.
Edit: This short script returns a value of 57, when the verfied # of labelled messages in Gmail is 2. The other 55 emails were, at one point, also labelled the same but have been processed and the label has been removed.
function getLabel () {
var label = GmailApp.search('label:TestLabel');
Logger.log(label[0].getMessageCount());
}
Further edit:
I changed the applied label to a new one in the Gmail Filter and the script. Oddly, the legacy emails still are captured - not only without the original label, but most certainly without the new one. I'm baffled.

Gmail Emails Counts by Script

I am trying to count all my emails, with the label " complete - others", with Google Scripts. The thing is, the query is not counting all the emails. If I look into the label, I have, at least, 21k threads, and the script counts me just 591 emails.
Do you know how can I do to get this corrected?
Thanks !
Code sample:
`function countMessages3() {
var spreadsheet = SpreadsheetApp.getActive()
SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName('Mails'))
var threads = GmailApp.search('in:sent');
var receivedCount = 0;
for(var i=0; i<threads.length; i++)
{
receivedCount = receivedCount + threads[i].getMessageCount();
}
spreadsheet.getRange('\'Mails\'!B4').setValue(receivedCount);`
I guess, the code that you have provided is searching messages in sent folder. To search messages in a label I think you do something like:
1.Get label using getUserLabelByName() https://developers.google.com/apps-script/reference/gmail/gmail-app.html#getuserlabelbynamename
2.Get all threads for that label using getThreads() https://developers.google.com/apps-script/reference/gmail/gmail-label.html#getthreads
3.Then aggregate messages count in each thread using getMessageCount()
https://developers.google.com/apps-script/reference/gmail/gmail-thread.html#getmessagecount
Something like this :
var labelName = "complete-others";
var messageCount = 0;
var label = GmailApp.getUserLabelByName(labelName);
var threads = label.getThreads();
threads.forEach(function(thread) {
messageCount += thread.getMessageCount();
});
spreadsheet.getRange('\'Mails\'!B4').setValue(messageCount);
Here's a function that will count your emails.
function countEmailsWithLabel(query) {
var n=0;
var ts=GmailApp.search(query);
ts.forEach(function(t,i){
var ms=t.getMessages();
n+=ms.length;
});
Logger.log(n);
return n;
}
The problem of course is figuring what the search query supposed to be. I have several test emails in a label I created as 'Q0/TEST' but where I search in Gmail I see that the search is 'label:q0-test' which doesn't agree with my label. So I tried searching with a search query of 'label:q0-test' and that didn't work. So now I use the query with the label as I created it so that the search query is now 'label:Q0/TEST' and I get the correct result of 3. So I would recommend that you use the labels as you created them rather than what you see in search box of the Gmail search.
Actually I ran into this inconsistency a few weeks ago and I suspect it has probably caused some difficulties for new programmers.

Is there a more efficient way to get arbitrary numbers of threads from Gmail with GmailApp.search()?

I am trying to get a large number of emails from a specific label in my Gmail and put them in a Google Drive document. How can I use GmailApp.search() to get the messages without missing any of them?
I tried simply using the non-range-defined GmailApp.search(). According to logs, it only returned 500 emails, while I have approximately 2000 in this label. The problem is that if there aren't 500 (or whatever number) more emails available, GmailApp.search() returns a blank array instead.
Say I have 600 emails, but don't know the exact number.
I can return the first 500 using GmailApp.search(searchQuery, 0, 500), but then the second search of 500 will return a blank array because only 100 emails remain. How can I get these remaining 100 in this scenario? Is stepping through the threads one at a time, as described in the code below, really the best solution if I want every single email?
for(var i = 0; i < ; i++) {
//get a single thread
var thread = GmailApp.search(searchQuery, i, 1);
//...
}
I want to output all of the emails, but what happens with GmailApp.search(searchQuery) with no specified range is that 500 emails are returned (at least according to Logger - I have it log a line after every email and it only gets up to 499, starting on 0).
More efficient than var thread = GmailApp.search(searchQuery, i, 1); is to estimate how many emails you maximally expect (e.g. 2000) and run search query 2000/500 times:
var threadArray=[];
for(var j = 0; j <3 ; j++){
var thread = GmailApp.search(searchQuery, j*500, 500);
thread.forEach(function(e){threadArray.push(e.getId());})
//now you have an array containing the Ids of all queried emails
}
This will only append to your array the found elements and no blank entries.
Try this:
function getAllThreads(qry) {
var qry=qry || 'label:qs-financial-institutions-wfb';
var threads=GmailApp.search(qry);
var html="";
for(var i=0;i<threads.length;i++) {
html+=Utilities.formatString('<br />%s -Thread Id:%s First Subject: %s Message Count: %s',i+1,threads[i].getId(),threads[i].getFirstMessageSubject(),threads[i].getMessageCount());
//You can put another loop in here to get all of the messages
}
var userInterface=HtmlService.createHtmlOutput(html).setWidth(1200);
SpreadsheetApp.getUi().showModelessDialog(userInterface, "Threads");
}

How to read all emails in gmail using google apps script

I'm trying to read ALL email in my gmail account - inbox, sent, draft, trash, emails with labels, archive, etc. I could live without the junk but I want everything else.
(all examples below use try {} catch {} to avoid errors with empty labels etc.)
I've tried
for (var i=StartLabel; i<=EndLabel; i++)
{
var label = labels[i].getName();
// get all messages, then join them into a single dimension array
var messages = GmailApp.getMessagesForThreads(GmailApp.search("label:" + label))
.reduce(function(a, b) {return a.concat(b);});
CountByLabels += messages.length;
}
That gives me everything in the labels (I think) but not the other stuff.
I tried other things, to get the inbox (to combine with the above) or all of the emails
var messages = GmailApp.getMessagesForThreads(GmailApp.getInboxThreads()).reduce(function(a, b) {return a.concat(b);});
CountInbox += messages.length;
but I only get about 549 results (GMail shows 5,478). If I add in the results from getPriorityInboxThreads I get 1,829 results.
I tried
// get all messages, then join them into a single dimension array
var messages = GmailApp.getMessagesForThreads(GmailApp.search("(is:unread OR is:read) in:anywhere")).reduce(function(a, b) {return a.concat(b);});
CountByLabels += messages.length;
I get 598 results.
I tried different search terms in the code directly above, eg:
is:unread = 528 results
is:read = 1,037 results
is:read OR is:unread = 599 results
None of them gave the right number, or even close, and incidentally if I try those search terms directly in gmail I get a totally different, and much higher, result for each - several thousand, or 'many'.
I don't think this is related to How to use Google App Scripts to retrieve Gmail emails in a customised way? as the numbers returned are not round numbers (eg 500).
I'm assuming that I can use getSpamThreads, getStarredThreads, getTrashThreads, getDraftMessages to get the relevant folders but until I understand why I'm only getting some emails from the inbox I don't trust those to give me everything.
Can anyone help?
Try this:
function allEmailsInLabels() {
var allLabels,i,j,L,L2,msgCount,theCount,threads,thisLabel;
msgCount = 0;
theCount = 0;
allLabels = GmailApp.getUserLabels();
L = allLabels.length;
for (i = 0; i < L; i++) {
Logger.log("label: " + allLabels[i].getName());
thisLabel = allLabels[i];
threads = thisLabel.getThreads();
//Logger.log('threads: ' + threads);
L2 = threads.length;
for (j = 0; j < L2; j++) {
msgCount = threads[j].getMessageCount();
//Logger.log('thread message count: ' + threads[j].getMessageCount());
// You could do something with threads[j] here like
// threads[j].moveToTrash();
theCount = theCount + msgCount;
};
};
//Logger.log('theCount: ' + theCount);
};
It first gets all the labels, then the threads, then the message count in each thread, and keeps a running count. You'll also need to get the messages in the inbox, that code doesn't include them. This is the sample code from the documentation that shows the basic concept:
// Log the subject lines of your Inbox
var threads = GmailApp.getInboxThreads();
for (var i = 0; i < threads.length; i++) {
Logger.log(threads[i].getFirstMessageSubject());
}
I had the same question. Reading a little bit more in the reference in the Google Developers Website, I discovered, reading about the function moveToInbox, a Google sample that used the Search to get all e-mails that weren't in the Inbox (https://developers.google.com/apps-script/reference/gmail/gmail-thread#movetoinbox). I decided to combine this with the getInboxThreads and with these two, my code was shorter and found every e-mail that I had received (less spam and junk).
function getEmails() {
var generalThreads, inboxThreads;
inboxThreads = GmailApp.getInboxThreads();
generalThreads = GmailApp.search('-in:inbox');
}
Every single email that was in the folder "All mail" in the Gmail was in these two variables after this.
I don't know if this can help anyone, but surely helped me.
I know this is coming a bit delayed, but having had the same problem and looking at some of the solutions offered here, I wanted to offer up my own solution, which also uses the search function:
function getEmails() {
var allEmailThreads = GmailApp.search('label:all')
}
This actually filters for every email, regardless of the mailbox, and seems to me to be the simplest solution to the question.
This is not an answer to your problem (but is probably one of the reasons your total results returned don't agree with what you are seeing in gmail inbox) but highlights one of the problems I encountered when calling getPriorityInboxThreads() is that it ignores any thread that is not flagged as "important" in the primary inbox.
//returns 10 threads and 1st message for each thread
function getThreads(){
var ret = '';
var threads = GmailApp.getPriorityInboxThreads(0, 10);
for (var i = 0 ; i < threads.length; i++) {
var id = threads[i].getId();
var message = GmailApp.getMessageById(id);
ret += "subject: " + message.getSubject() +'\n';
Logger.log("subject: " + message.getSubject());
/*Edited this out as it doesn't return anything
//check for labels on this thread
var labels = threads[i].getLabels();
for (var j = 0; j < labels.length; j++) {
Logger.log(labels[j].getName());
} */
}
return ret;
}
"Important" is classed as a system flag and getPriorityInboxThreads() ignores any thread that is not flagged important....
I would like to select all threads in "Primary" inbox irrespective of being labelled as "important".
To test, simply change any thread in inbox to important or not etc.
After I published a video on how to get Gmail messages into a Google spreadsheet, I received a feedback from some viewers that they could only get a number of messages but others fail to be processed. Therefore, I did some research and found that the process of getting emails may fail and make the system unable to handle the huge amount of emails. This is mentioned in the Gmail API here:
https://developers.google.com/apps-script/reference/gmail/gmail-label#getthreads
The documentation suggests to use getThreads(start, max) where start and max are the limiting parameters.
You may view the video and download the full code from YouTube and GitHub:
https://youtu.be/gdgCVqtcIw4

Removing "read" logic from gmail script

I am fairly new to google app scripts and hoped you could help me… I am sure this is an easy question.
I am running the following script to archive old emails. It currently only archives read emails, but I would like to have it archive messages with these labels regardless of whether they are read or unread. Any help would be appreciated!
function archiveInbox4() {
// Every thread in your Inbox that is read, older than fourteen days, and not labeled "delete me".
var threads = GmailApp.search('label: inbox older_than:14d label:"Calendar"|"wacuho-acuho-i"|"professional-organizations"');
for (var i = 0; i < threads.length; i++) {
threads[i].moveToArchive();
There's a way that you can mark emails read first then archive them as read emails.
Sample code is like:
function markArchivedAsRead() {
var threads = GmailApp.search('label:unread -label:inbox');
GmailApp.markThreadsRead(threads);
};
However this operation can only be applied to at most 100 threads. To fix this, you have to manually do a search for "is:unread" and mark all of them as read before running the script, so that it starts with a clean slate. The script can only process 100 threads per run, so if you give it more than 100 on the first run, that'll obviously bust it. See the reference link.
You can also have this code to get it done by google apps script:
function markArchivedAsRead() {
var threads = GmailApp.search('is:unread');
for (var i = 0; i < threads.length; i++) {
GmailApp.markThreadRead(threads[i]);
}
};