Web Gmail shows message as starred even after Apps Script unstars it - google-apps-script

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

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.

GmailThread.getlables() fails randomly with the exception "Exception: Gmail operation not allowed."

This might be a weird one but I have some code that goes through my email and uses labels to trigger certain tasks. For example, if someone applies via Indeed, Indeed sends me an email. A filter will see this this email and apply the "Applications/indeedApplication" label. Then, a script that runs once per minute will find this label, and based on the label, perform some actions automatically.
This works perfectly about 99.97% of the time. The script runs with no issues and does the tasks, applies a completed label, and everything works as intended. 0.03% of the time, the task will fail, always at the same point, and always with the same exception:
Exception: Gmail operation not allowed.
It fails when the scrip tries to run the getLables() function on a GmmailThread (class of GmailApp API).
Code in question:
function addICandidate() {
var label = GmailApp.getUserLabelByName("Applications/indeedApplication"); //Defines the label that identifies indeed applicatoins
var requests = label.getThreads(); //Returns an array of threads which have this label
if(requests.length > 3){requests.length = 3}; //This makes it so the script doesn't waste time looking through every application ever.
for ( var i in requests ) { //Starting a normal for loop
iLabels = requests[i].getLabels(); //Gets the labels of the Randomly fails here and only here
var checker = false; //used later to check if tasks were already completed
for (var k = 0 in iLabels) { //Starting another normal for loop
var individualLabel = iLabels[k].getName(); //Gets a list of the labels
if (individualLabel === completedLabelName) { //Checks if the label for completes tasks is present
var checker = true; //If they are, set checker to true (also used later)
}
}
if(checker){ //If the checker above is set to true
Logger.log("true"); //Do nothing (logger is just used in debugging)
}
else{ //Else (if the label is not completed and therefore, the tasks have not been done yet)
var thread = requests[i].Dostuff(); //Confidential stuff lives here
}
requests[i].addLabel(completedlabel); //After everything, apply the completed label so tasks are not done again on the next pass
}
}
}
Does anyone know why it would fail here and only 1 in 3000 attempts. I obviously cannot debug this with brute force so any ideas as to why this might happen would be really helpful.

Using Google Apps Script, how do you remove built in Gmail category labels from an email?

I'd like to completely undo any of Gmails built in category labels. This was my attempt.
function removeBuiltInLabels() {
var updatesLabel = GmailApp.getUserLabelByName("updates");
var socialLabel = GmailApp.getUserLabelByName("social");
var forumsLabel = GmailApp.getUserLabelByName("forums");
var promotionsLabel = GmailApp.getUserLabelByName("promotions");
var inboxThreads = GmailApp.search('in:inbox');
for (var i = 0; i < inboxThreads.length; i++) {
updatesLabel.removeFromThreads(inboxThreads[i]);
socialLabel.removeFromThreads(inboxThreads[i]);
forumsLabel.removeFromThreads(inboxThreads[i]);
promotionsLabel.removeFromThreads(inboxThreads[i]);
}
}
However, this throws....
TypeError: Cannot call method "removeFromThreads" of null.
It seems you can't access the built in labels in this way even though you can successfully search for label:updates in the Gmail search box and get the correct results.
The question...
How do you access the built in Gmail Category labels in Google Apps Script and remove them from an email/thread/threads?
Thanks.
'INBOX' and other system labels like 'CATEGORY_SOCIAL' can be removed using Advanced Gmail Service. In the Script Editor, go to Resources -> Advanced Google services and enable the Gmail service.
More details about naming conventions for system labels in Gmail can be found here Gmail API - Managing Labels
Retrieve the threads labeled with 'CATEGORY_SOCIAL' by calling the list() method of the threads collection:
var threads = Gmail.Users.Threads.list("me", {labels: ["CATEGORY_SOCIAL"]});
var threads = threads.threads;
var nextPageToken = threads.nextPageToken;
Note that you are going to need to store the 'nextPageToken' to iterate over the entire collection of threads. See this answer.
When you get all thread ids, you can call the 'modify()' method of the Threads collection on them:
threads.forEach(function(thread){
var resource = {
"addLabelIds": [],
"removeLabelIds":["CATEGORY_SOCIAL"]
};
Gmail.Users.Threads.modify(resource, "me", threadId);
});
If you have lots of threads in your inbox, you may still need to call the 'modify()' method several times and save state between calls.
Anton's answer is great. I marked it as accepted because it lead directly to the version I'm using.
This function lets you define any valid gmail search to isolate messages and enables batch removal labels.
function removeLabelsFromMessages(query, labelsToRemove) {
var foundThreads = Gmail.Users.Threads.list('me', {'q': query}).threads
if (foundThreads) {
foundThreads.forEach(function (thread) {
Gmail.Users.Threads.modify({removeLabelIds: labelsToRemove}, 'me', thread.id);
});
}
}
I call it via the one minute script trigger like this.
function ProcessInbox() {
removeLabelsFromMessages(
'label:updates OR label:social OR label:forums OR label:promotions',
['CATEGORY_UPDATES', 'CATEGORY_SOCIAL', 'CATEGORY_FORUMS', 'CATEGORY_PROMOTIONS']
)
<...other_stuff_to_process...>
}

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

Google Script Quota Issues: Gmail Read

I'm having a bit of trouble with my google script code. I'm trying to make a script that will automatically take any email that is sent with 'Events' in the subject and parse the text to add it to the calendar. I have a filter that will add the email to the label 'Events', then remove the label.
The script is working fine and is posted below. I have it triggering every minute. However, every day I get a huge failure notification email that I've exceeded the quota for gmail reads on line 4 [GmailApp.getUserLabelbyName]. I tried googling and I couldn't find more specifics.
So, according to the quota website I have a limit of 10,000 reads per day. This has been failing with no emails at all in the 'Events' bin, so no actual emails appear to be read. The calendar should be accessing it once per minute, i.e. 1,440 times a day.
So why am I getting the quota failure for gmail read? Does 'gmail reads' mean accesses to gmail (getting the label variable) or reading email bodies (getThreads) or both?
Any help would be greatly appreciated. Thanks!
function MakeEvents() {
var label = GmailApp.getUserLabelByName("Events");
if (label) {
var threads = label.getThreads();
for (var x in threads) {
var messages = threads[x].getMessages();
for (var y in messages) {
var myHTMLContent = messages[y].getBody();
textContent=getTextFromNode(Xml.parse(myHTMLContent, true).getElement());
CalendarApp.getDefaultCalendar().createEventFromDescription(textContent);
}
threads[x].removeLabel(label);
}
}
}
function getTextFromNode(x) {
switch(x.toString()) {
case 'XmlText': return x.toXmlString();
case 'XmlElement': return x.getNodes().map(getTextFromNode).join('');
default: return '';
}
}