Using Gmail labels in Apps Script for Sheets - google-apps-script

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.

Related

RESOURCE_EXHAUSTED when extracting from Google Form to Google Sheet

I'm trying to extract questions and multiple choice options of a Google Form to a Google Sheet. I've found a solution here (Is it possible to export questions and multiple choice options from a Google Form to a Google Sheet?), in the answer by alberto vielma (answer of Oct 28 '19 at 12:06).
However, I'm running into an error ("We're sorry, there was an unexpected error while creating the Cloud Platform project. Error code RESOURCE_EXHAUSTED.") when trying Alberto's script.
This surprised me, because I've used the script succesfully a few months ago... Does anyone have an idea what exactly went wrong?
(I created a new topic, because the 'tips' about answering suggested I shouldn't "contribute" to above mentioned topic if I only had extra questions.)
For your convenience, I'll copy Alberto's script here:
var form = FormApp.openById('YOUR-FORM-ID');
// Open a sheet by ID.
var sheet = SpreadsheetApp.openById('YOUR-SHEET-ID').getSheets()[0];
// variables for putting the questions and answers in the right position
var question_position = 0;
var answers_position = 0;
// main function to run
function getFormValues() {
form.getItems().forEach(callback);
}
// Iterate over all questions
function callback(el){
// check if the question is multiple choice
if (el.getType() == FormApp.ItemType.MULTIPLE_CHOICE) {
// change the type from Item to MultipleChoiceItem
var question = el.asMultipleChoiceItem();
var choices = question.getChoices();
// set the title of the question in the cell
sheet.getRange(question_position +1, 1).setValue(question.getTitle());
var i = 0;
// set the answers in the right cells
for (i; i < choices.length; i++){
sheet.getRange(answers_position + 1, 2).setValue(choices[i].getValue());
answers_position++;
}
question_position += i;
answers_position++;
}
question_position++;
}
This was a known issue impacting some users earlier today (which appears to be no longer persistent.) Ref https://issuetracker.google.com/issues/194826559?pli=1
Was there any value written to your spreadsheet when you got the error?
I wonder it deal with usage limits because you have many setValue to spreadsheet.
Maybe you can try to first push the all single values (questions and choices) to an array values. And finally use setValues(values) to reduce the number of calls of setValue.
Or you could try to add some Utilies.sleep(milliseconds) in each loop.

Google Apps Script for loop not processessing all items from getThreads()

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

GmailApp Won't Unstar Gmail Message [duplicate]

OK, I think web Gmail is being screwy. I run a Google apps script that in part adds my "To-do" label to any thread I manually star, then archives and unstars it. A snippet is below. I'd appreciate any help.
After running the script the thread gets the label and is unstarred, except the star icon next to the thread/message in web Gmail still shows as selected. If I go to Starred messages label/folder nothing is displayed. If I immediately rerun the script it doesn't find any Starred threads. This seems to indicate the script is working OK. The problem seems to be that web Gmail still wants to show it as Starred even though it isn't. Gmail Android app doesn't show the star applied to the thread. Pictures are worth more...
What my inbox looks like after the script runs. Note the star:
Yet no Starred messages:
function addStarred2ToDo() {
var threads = GmailApp.search('is:starred');
for (var h in threads) {
var messages = threads[h].getMessages();
for (var i in messages) {
if (messages[i].isStarred()) {
messages[i].unstar();
}
}
}
}
EDIT:
I also tried this and neither produce what is expected.
function thisOne() {
var threads = GmailApp.search('is:starred');
for (var h in threads) {
var messages = threads[h].getMessages();
for (var i in messages) {
if (messages[i].isStarred()) {
messages[i].unstar().refresh();
}
}
}
}
function andThisOne() {
var threads = GmailApp.search('is:starred');
var toUnstar = [];
threads.forEach(function (thread) {
thread.getMessages().forEach(function (message) {
if (message.isStarred()) {
toUnstar.push(message);
}
});
});
GmailApp.unstarMessages(toUnstar);
}
If you refresh gmail and hover over the star, you will see that the popup says it is Not Stared. Also, this seems to be an issue when you select the star from gmail, as stars that are set by my filters work correctly when my script unstars them.
This display issue is caused because you do not force Gmail to update the message with a call to refresh() after your call to unstar().
Per documentation of GmailMessage#refresh():
Reloads this message and associated state from Gmail (useful in case the labels, read state, etc., have changed).
messages[i].unstar().refresh();
Should be sufficient to inform Gmail of the new starred status.
Alternately, a batch call to modify the messages will be more efficient in terms of quota usage:
var toUnstar = [];
threads.forEach(function (thread) {
thread.getMessages().forEach(function (message) {
if (message.isStarred()) {
toUnstar.push(message);
...
}
});
});
GmailApp.unstarMessages(toUnstar);
In my sample I avoid the assumption that iterating an array is safe with for .. in.. and use the more expressive Array.forEach() to indicate that the code is something that we want to apply to every thread and every message in said thread.
Documentation on GmailApp.unstarMessages():
Removes stars from these messages and forces the messages to refresh.
I'm having a similar problem. I have enabled a superstar, the green-check. I manually set them.
My script finds the relevant threads using "l:^ss_cg" in a search. It finds the starred email, sends a copy of the email somewhere, and then does the unstar.
Afterward, in the web gmail, if i search for the same message again, it shows up with a green star visually, but if I hover over the star icon, it shows 'not starred'.
However, if I run the script again, the thread is found using the search. It doesn't send another copy of the email, however, because I have a check for ".isStarred()" before it sends a copy of the specific email. I have also been able to reduce the number of threads it double-checks by adding a .hasStarredEmails() check to the thread before it starts looking at individual emails.
Doing a search in the web interface for has:green-check reveals only the emails it should.
There is something about .unStar() that doesn't work properly, I think.
I had considered trying to remove the star at the thread level by removing the ^ss_cg label, but that won't work because there is no way to get a GMailLabel object to send to the function.
Still having same issue where the Gmail web app shows the star. But just made interesting finding.
Assuming
var threads = GmailApp.getStarredThreads()
var thread = threads[0]
var message = thread.getMessages()[0]
GmailApp.unstarMessage(message) would immediately give the following results:
var isMessageStarred = message.isStarred() true
var isThreadStarred = thread.hasStarredMessages() true
GmailApp.unstarMessage(message).refreshMessage(message) would immediately give the following results:
var isMessageStarred = message.isStarred() false
var isThreadStarred = thread.hasStarredMessages() true
And GmailApp.unstarMessage(message).refreshMessage(message).refreshThread(thread) would immediately give the following results:
var isMessageStarred = message.isStarred() false
var isThreadStarred = thread.hasStarredMessages() false
I had this case. Actually what was happening was I was starring everything that hit the inbox with a filter. Once my script processed the message I unstarred it. But my script was also forwarding the message and that new sent message was being starred by the filter. So the thread still contained a starred message. Argh.
My solution was to create another filter to delete any messages from me.
Same problem here.
I am unable to unstar() emails which I starred manually in my browser/on my phone.
If I star an email via a rule or a script and then unstar it with a gapps script, unstarring works.
I also tried doing refresh right after unstarring - no difference:
message.unstar()
message.refresh()
I wrote a script which stars always the last message of a previously starred thread to keep my starred threads with the most recent replies at the top of my mailbox. The only problem is that it's not really working if I star an email manually at the beginning and not with a rule or another script.
The code going through my threads:
for(var z=0;z<messages.length;z++){
var message = messages[z]
var messageTime = message.getDate()
var messageTimeInCET = Utilities.formatDate(messageTime,'CET','yyyy-MM-dd HH:mm:ss')
//Logger.log('messageTime: '+ messageTime)
//Let's add star to the last email in the thread and remove stars from all previous emails
if(threadLastMessageDateInCET==messageTimeInCET){
Logger.log('Starred email from: '+ messageTimeInCET)
message.star()
}
else{
message.unstar()
message.refresh()
Logger.log('Unstarred email from: '+messageTimeInCET)
}
}

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