google apps script and GmailApp: get just new messages - google-apps-script

I'm trying to implement a simple google script that processes each message that is received by a Gmail user.
I've found an example that does something like this:
var threads = GmailApp.getInboxThreads();
for (var i=0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j=0; j < messages.length; j++) {
if (!messages[j].isUnread()) {
continue;
}
//process message
}
}
That is: I iterate through all messages in the inbox and search for the unread ones. This is very slow on just 1800 messages.
Ideally, I'm looking for a trigger that gets fired once each new message is received.
If there is no such thing, I would try to make use of this that I saw:
GmailApp.getMessageById(id)

Sorry for the late response but I just had the same type of problem and I ended up using GmailApp.search() ... hope this helps.
// find unread messages
var threads = GmailApp.search('is:unread');
....
WARNING
This call will 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 the 'paged' call, and specify ranges of the threads to retrieve in each call.
Take a look at GmailApp.search(query) and
GmailApp.search(query, start, max)

Unfortunately there in no trigger that fires for each recieved message. There is however a good workaround:
Set up a filter rule that assigns a special label, "ToBeProcessedByScript" as example, to all incoming messages. Since wildcards don't really work in Gmail filters use the to: field.
Run a time-triggered script that collects all new message threads with GmailApp.getUserLabelByName("ToBeProcessedByScript").getThreads(). Remove the special label just before processing the new messages.

you can use
GmailApp.getInboxThreads(0, 50);
to initialize the variable with first fifty mail.

I have extended the code with checking if the first message is really the one which is unread. If it is not it will check the next message and will continue untill it finds the unread message:
function getUnreadMails() {
var ureadMsgsCount = GmailApp.getInboxUnreadCount();
var threads;
var messages;
var k=1;
if(ureadMsgsCount>0)
{
threads = GmailApp.getInboxThreads(0, ureadMsgsCount);
for(var i=0; i<threads.length; i++)
{
if(threads[i].isInInbox())
{
messages = threads[i].getMessages();
for(var j=0; j<messages.length; j++)
{
while (messages[j].isUnread() === false)
{
threads=GmailApp.getInboxThreads(k, ureadMsgsCount);
messages = threads[i].getMessages();
k++;
}
Logger.log(messages[j].getSubject());
// process unread message
}
}
}
}
}

You can create a time trigger as djtek mentioned but instead of labeling all messages and then retrieve labeled messages, you can just get the number of the unread messages, and retrieve threads from 0 to the number of the unread messages, following a code that works for me:
function getUnreadMails() {
var ureadMsgsCount = GmailApp.getInboxUnreadCount()
if(ureadMsgsCount>0)
{
var threads = GmailApp.getInboxThreads(0, ureadMsgsCount);
for(var i=0; i<threads.length; i++)
{
if(threads[i].isInInbox())
{
var messages = threads[i].getMessages();
for(var j=0; j<messages.length; j++)
{
Logger.log(messages[j].getSubject());
// process unread message
}
}
}
}
}

function getUnreadMessages(threadLimit) {
function flatten(arr) { return [].concat.apply([], arr) }
var threadsWithUnreadMessages = GmailApp.search('is:unread', 0, threadLimit)
var messageCollection = threadsWithUnreadMessages.map(function(thread) {
return thread.getMessages()
})
var unreadMessages = flatten(messageCollection).filter(function(message) {
return message.isUnread()
})
return unreadMessages
}
getUnreadMessages(100)

Related

Getting the body of individual emails from gmail to google sheets

I'm really new at using Google Apps Script, so if what I'm trying doesn't make sense, or just isn't possible please let me know.
Everyday I get several emails that look like the following:
Your Name: FirstName LastName
Phone Number: 555 867 5309
Email Address: FakeEmail#email.com
What do you need help with? Request someone makes.
I'm attempting to automatically send the body of these emails to a new line in a Google Sheet when they come in.
As of right now I have every email get the label "myLabel" when it comes in. I then run the following script, which is a slightly modified version of something I found here:
function myFunction() {
var ss = SpreadsheetApp.getActiveSheet();
var label = GmailApp.getUserLabelByName("MyLabel");
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 msg = messages[j].getBody();
ss.appendRow([msg])
}
threads[i].removeLabel(label);
}
}
I'm attempting to run this code with a timer trigger every 15 minutes. The issue I've run into is that every time the code runs it pulls from every email in the thread. I would like it to just pull from the emails that are new since the last time it ran. Any advice would be greatly appreciated.
Why not mark the messages as read when you finish processing them? Here is a sample from one of my scripts.
var pendingEmailLabel = "MyLabel";
var threads = GmailApp.getUserLabelByName(pendingEmailLabel).getThreads();
for (var t = 0; t < threads.length; ++t) {
var thread = threads[t];
var messages = thread.getMessages();
for (var m = 0; m < messages.length; ++m) {
var message = messages[m];
if (message.isUnread()) {
// INSERT YOUR CODE HERE THAT TAKES ACTION ON THE MESSAGE
message.markRead();
}
}
}
}

permanently delete only one gmail message from a thread using a google script

I want to permanently delete a Gmail message inside a thread already in the trash.
I merged a few scripts around there, so I can delay and track emails. It works by saving a draft, then the script copy the draft into a new email, send it at the specified time and send the original draft to trash. The problem is that once in a while, the drafts that are in the trash are sent again (i haven't been able to figure out why yet)...
As a workaround, I was using the following code that that was originally posted here: delete forever emails 1:
function cleanUp() {
var threads = GmailApp.search("in:trash is:draft");
Logger.log(threads.length);
for (var i = 0; i < threads.length; i++) {
Logger.log(threads[i].getId());
Gmail.Users.Message.remove('me',threads[i].getId());
}
}
This was working fine, until a while ago. If the draft was inside a thread with more than 1 message, only the draft was deleted... I got now an error on line 6 that says: "Cannot call method "remove" of undefined".
In this post: delete forever emails 2, it is suggested to replace line 6 by
Gmail.Users.Threads.remove('me',threads[i].getId());
This dosn't get any errors, but if the draft is in a thread with more than one message, the whole thread is deleted instead of only the draft...
So, is there a way to get only the draft erased?
I tried calling the message id of the draft inside the thread and use the original line 6:
function cleanUp2() {
var threads = GmailApp.search("in:trash is:draft");
Logger.log(threads.length);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
Logger.log(messages.length);
for (var j = 0; j < messages.length; j++){
if (messages[j].isDraft()){
Logger.log('id msg: ' + messages[j].getId());
Gmail.Users.Message.remove('me',messages[j].getId());
}
}
}
}
But I got the same error, now on line 10...
I also tried using this function:
function deleteMessage(userId, messageId) {
var request = gapi.client.gmail.users.messages.delete({
'userId': userId,
'id': messageId
});
request.execute(
function(resp) { });
}
That you can find in the developers page of google: here. In the "try this API" section it works, but in my implementation i got an error on line 2 that says (translated from Spanish so i don't know if it will be exact): "a name (?) is missing behind (after?) operator "."" And if i copy the function in a separated tab, i can save it and the same error is showed...
Any help will be appreciated...
Regards,
i finally made it trough an http request:
function cleanUp2() {
var threads = GmailApp.search("in:trash is:draft");
Logger.log(threads.length);
var userId = 'xxxxx#gmail.com';
var options = {
'method' : 'delete',
'muteHttpExceptions': true
};
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
Logger.log(messages.length);
for (var j = 0; j < messages.length; j++){
if (messages[j].isDraft()){
Logger.log('id msg: ' + messages[j].getId());
var url = 'https://www.googleapis.com/gmail/v1/users/' + userId + '/messages/' + messages[j].getId();
var response = UrlFetchApp.fetch(url,options);
Logger.log(response);
}
}
}
}

Google Script star an e-mail

I have a Google Script that processes the inbox looking for missing e-mails, and then sends out a summary of missing e-mails to my inbox:
var user;
var summary = "";
Logger.log("Checking last emails...");
user_list.forEach(function(user) {
var no_user_hit = true;
var query = 'from:'+user.user+' in:anywhere newer_than:' + user.deadline + 'd';
Logger.log(query);
var threads = GmailApp.search(query);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
if (check_subject(messages[j].getSubject(), user.subject)) {
no_user_hit = false;
}
}
}
if (no_user_hit == true) {
Logger.log("Sending email with summary...");
summary = summary + "No messages from "+user.user+" with subject "+user.subject+" for the last "+user.deadline+" days \n";
}
});
if (summary.length > 0) {
GmailApp.sendEmail(me, email_subject_to_your, summary);
}
}
I would like to star each e-mail being sent as a summary, I have tried starMessage(message) but that hasn't worked out.
The problem appears to be that the GmailApp.starMessage() accepts a GmailMessage object, but you are supplying the method with a string (as seen from the error message you are getting).
After you have sent your message, you will need to find it again in user's mailbox and then star it.
There is an answered question about retrieving a just-sent message for further processing - that might help with this.

reply time between threads [google apps script]

I want to develop a script in GAS to get the reply time of all of my threads in Gmail in a specific period. The example script below seems to be a way, but im not sure on how to proceed.
function processInbox() {
// get all threads in inbox
var threads = GmailApp.getInboxThreads();
for (var i = 0; i < threads.length; i++) {
// get all messages in a given thread
var messages = threads[i].getMessages();
// iterate over each message
for (var j = 0; j < messages.length; j++) {
// log message subject
Logger.log(messages[j].getSubject());
}
}
};
Maybe use:
Google Documentation - getDate() GmailMessage Class
When you say you want to "get" the information, how do you want to get it? Be sent an email? Store it in a spreadsheet? Record it in a .csv file?

How do I fix "Exceeded maximum execution time" error on this Google Apps Script?

I want to devise a script that will clean up my email. I want to create a few labels called "Auto Archive/# days" where # is a number between 0 and 9. I wrote this script below, but every time it runs, I receive an "Exceeded maximum execution time" error.
I have a time-driven (hour timer) trigger set up to run every 12 hours. I call the autoArchive method in the trigger. I tried adding Utilities.sleep a few times, but it didn't help. [Should I put them somewhere else in the code?]
Any help is greatly appreciated! Thank you in advance!
function cleanUp(delayDays) {
//var delay2Weeks = 14 // Enter # of days before messages are moved to archive
//var delay2Days = 2 // Enter # of days before messages are moved to archive
if (typeof delayDays != 'number') {
return null;
}
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var label = GmailApp.getUserLabelByName("Auto Archive/" + delayDays + " days");
var threads = label.getThreads();
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()<maxDate)
{
var randnumber = Math.random()*5000;
Utilities.sleep(randnumber);
Utilities.sleep(randnumber);
threads[i].moveToArchive();
}
}
}
function autoArchive()
{
for (var i = 1; i < 10; i++) {
cleanUp(i);
}
}
So it seems I was getting all items with "Auto Archive/X days" and not limiting the result set to only items within the Inbox. After correcting that, the maximum execution time error went away. I corrected it by choosing the inbox items first, and next the items with the label.
function cleanUp(delayDays) {
//var delay2Weeks = 14 // Enter # of days before messages are moved to archive
//var delay2Days = 2 // Enter # of days before messages are moved to archive
if (typeof delayDays != 'number') {
return null;
}
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var inboxItems = GmailApp.getInboxThreads();
for (var i = 0; i < inboxItems.length; i++) {
if (inboxItems[i].getLastMessageDate()<maxDate) {
var autoItems = inboxItems[i].getLabels();
for(var j=0; j < autoItems.length; j++) {
if (autoItems[j].getName() == "Auto Archive/" + delayDays + " days") {
inboxItems[i].moveToArchive();
break;
}
}
}
}
}
function autoArchive()
{
Session.getActiveUser().getEmail();
for (var i = 1; i < 10; i++) {
cleanUp(i);
}
}
There are many ways to speed it up. For starters dont call sleep as it will make the problem worse (makes the script take even more time from your daily quota and trigger 5min limit).
After that if the problem is that you have too many threads it might help to write a list of threads to archive (in scriptdb for example store the thread ids) but dont archive them yet.
Later from another trigger (say every 10min) you process your list by chunks (see https://developers.google.com/apps-script/reference/gmail/gmail-app#getThreadById(String)) and use more triggers if needed to avoid the 5min limit.