There was two function wrote in that answer code, one is main and another one is extract details so its take two function, whenever ran the script its throwing error like getdate() not define so i just make as one function as main, now the data tracked automatically into sheet but its reading only first thread which is unread label. if the label having 23 thread , the first thread only repeating as 23 times. even I Have changed threads.messages(0) tracking only first thread which is already read thread, not read and track unread thread. Could you help me
function main(){
var query = {'q':'label:"Sanmina EDI Failed Concurrent Jobs Alert" is:UNREAD'};
var threads = Gmail.Users.Messages.list(userId='me', q=query);
for(i in threads.messages){
var threadIds = threads.messages[0].threadId;
var message = GmailApp.getMessageById(threadIds);
extractDetails(message);
GmailApp.markMessageRead(message);
}
function extractDetails(message)
{
var dateTime = message.getDate();
var bodyContents = message.getBody();
var action= bodyContents.search("Invoice")
var action1=bodyContents.search("Error")
var action2=bodyContents.search("Terminated")
if(action > 0)
{
var out="Need to create SR"
}
else if(action1>0 || action2>0)
{
var out="Need to create SR"
}
else
{
var out="Printing output file"
}
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
activeSheet.autoResizeRows(1, 2);
activeSheet.appendRow([dateTime, bodyContents, out]);
}
}
[enter image description here][1]
Sanmina EDI Failed Concurrent Jobs Alert - subject same name for label
Production Workflow Mailer
May 26, 2017, 7:30 PM
Request Id Program Name Status Requested By
---------- ------------------------------------------------------------ -------- 813511350 Sanmina EDI 850/860 Inbound PO/Change Tabular Report Warning NERODRIG
Try using the Gmail API to search the threads:
function main(){
var query = {'q':'label:"Sanmina EDI Failed Concurrent Jobs Alert" is:UNREAD'};
var threads = Gmail.Users.Messages.list(userId='me', q=query);
for(i in threads.messages){
var threadIds = threads.messages[i].threadId;
var message = GmailApp.getMessageById(threadIds);
extractDetails(message);
GmailApp.markMessageRead(message);
}
}
function extractDetails(message){
var dateTime = message.getDate();
var bodyContents = message.getPlainBody();
var action= bodyContents.search("Invoice")
var action1=bodyContents.search("Error")
var action2=bodyContents.search("Terminated")
if(action > 0)
{
var out="Need to create SR"
}
else if(action1>0 || action2 > 2)
{
var out="Need to create SR"
}
else
{
var out="Printing output file"
}
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
activeSheet.appendRow([dateTime, bodyContents, out]);
}
In case you only want the last message, use threads.messages[0] instead of i.
More information:
List method
Related
I'm trying to assign auto-created label to gmail threads. The label alone was successfully created but it couldn't be assigned to the respective threads due to a TypeError.
Here's the script
//call cell value by column (Sheet: "SupplierNumber")
//label created based on the "Label" column in Sheet: SupplierNumber
//Label applied to related threads
function callLabelcolumn()
{
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('SupplierNumber');
var rg=sh.getRange('A2:A');
var vA=rg.getValues();
var sh3 = ss.getSheetByName("SearchParameter")
var rg3=sh3.getRange('A2');
var searchPara =rg3.getValues();
var threads = GmailApp.search(searchPara);
for (var i = 0; i < threads.length; i++){
var msg = threads[i].getMessages();
for (var j = 0; j < msg.length; j++){
// Get same thread by its ID.
//var threadID = GmailApp.getThreadById(msg[j].getId());
var msgid = msg[j].getId();
}
}
vA.forEach(function(row) {
if(row != "" && row != "#N/A"){
Logger.log('Label: %s', row);
var label = GmailApp.createLabel(row);
Logger.log("label: " + label);
}
var thread = [0];
var thread = GmailApp.getMessageById(msgid).getThread().getId();
var name = GmailApp.getUserLabelByName(label.getName())
for(var t in thread){
console.log("So far so good. Let's add label")
thread[t].addLabel(name);
}
});
}
The error is " TypeError: thread[t].addLabel is not a function " when run script in the editor.
TypeError: thread[t].addLabel is not a function
After debugging, I'm suspecting var name returned "" or null which is why the error is happening.
Debug result: thread[t].addLabel
I tried debugging on var name, some of results were name: undefined, t: undefined which I think is the cause of the TypeError.
Debug result: var name = GmailApp.getUserLabelByName(label.getName())
So is there a way to solve this?
The main problem lies in the line
var thread = GmailApp.getMessageById(msgid).getThread().getId();
The method addLabel() needs to be applied on the thread resource rather than a threadId.
A working code snippet would be:
var thread = GmailApp.getMessageById(msgid).getThread();
var name = GmailApp.getUserLabelByName(label.getName())
console.log("So far so good. Let's add label")
thread[t].addLabel(name);
Other wissue in your code are:
You are trying to loop through thread, but in your case thread is not array, but a single value
You retrieve the msgid within a lopo and overwrite it within each iteration, however you use msgid for retrieving the thread only after exiting the loop - this means only for the very last value of msgid.
I am writing the Date and Subject from specific new emails to a new row of a Google Sheet.
I apply a label to the new mail items with a filter.
the script processes those labeled emails
the label is removed
A new label is applied, so that these emails won't be processed next time.
Problem: When there is a myLabel email, the script processes all emails in the same thread (eg same subject and sender) regardless of their label (even Inbox and Trash).
Question: How to only process new emails i.e. ones with the label myLabel - even when the thread of those messages extends outside the myLabel folder?
My current script:
function fetchmaildata() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('mySheetName');
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 sub = messages[j].getSubject();
var dat = messages[j].getDate();
ss.appendRow([dat, sub])
}
threads[i].removeLabel(label);
threads[i].addLabel(newlabel);
}
}
I hacked a solution for my purposes by changing my for loop to this:
for (var j = messages.length-1; j > messages.length-2; j--)
This says to process only the latest email in the thread, even when there is more than one email of a thread in the myLabel folder. Oddly, the script still changes the Labels of all the myLabel emails, but only the latest one of a thread gets written to the spreadsheet, so it works for me.
I had to make another change to the code because the above code does not run as a time-triggered scheduled task. I changed the code in this way and it now runs on a time schedule !!
//var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss = SpreadsheetApp.openById("myGoogleSheetID");
A label can be on a thread due to being on a single message in said thread. Your code simply goes label -> all label threads -> all thread messages, rather than accessing only the messages in a thread with a given label. That's not really your fault - it's a limitation of the Gmail Service. There are two approaches that you can use to remedy this behavior:
The (enable-before-use "advanced service") Gmail REST API
The REST API supports detailed querying of messages, including per-message label status, with Gmail.Users.Messages.list and the labelIds optional argument. For example:
// Get all messages (not threads) with this label:
function getMessageIdsWithLabel_(labelClass) {
const labelId = labelClass.getId();
const options = {
labelIds: [ labelId ],
// Only retrieve the id metadata from each message.
fields: "nextPageToken,messages/id"
};
const messages = [];
// Could be multiple pages of results.
do {
var search = Gmail.Users.Messages.list("me", options);
if (search.messages && search.messages.length)
Array.prototype.push.apply(messages, search.messages);
options.pageToken = search.nextPageToken;
} while (options.pageToken);
// Return an array of the messages' ids.
return messages.map(function (m) { return m.id; });
}
Once using the REST API, there are other methods you might utilize, such as batch message label adjustment:
function removeLabelFromMessages_(messageIds, labelClass) {
const labelId = labelClass.getId();
const resource = {
ids: messageIds,
// addLabelIds: [ ... ],
removeLabelIds: [ labelId ]
};
// https://developers.google.com/gmail/api/v1/reference/users/messages/batchModify
Gmail.Users.Messages.batchModify(resource, "me");
}
Result:
function foo() {
const myLabel = /* get the Label somehow */;
const ids = getMessageIdsWithLabel_(myLabel);
ids.forEach(function (messageId) {
var msg = GmailApp.getMessageById(messageId);
/* do stuff with the message */
});
removeLabelFromMessages_(ids, myLabel);
}
Recommended Reading:
Advanced Services
Gmail Service
Messages#list
Message#batchModify
Partial responses aka the 'fields' parameter
Tracked Processing
You could also store each message ID somewhere, and use the stored IDs to check if you've already processed a given message. The message Ids are unique.
This example uses a native JavaScript object for extremely fast lookups (vs. simply storing the ids in an array and needing to use Array#indexOf). To maintain the processed ids between script execution, it uses a sheet on either the active workbook, or a workbook of your choosing:
var MSG_HIST_NAME = "___processedMessages";
function getProcessedMessages(wb) {
// Read from a sheet on the given spreadsheet.
if (!wb) wb = SpreadsheetApp.getActive();
const sheet = wb.getSheetByName(MSG_HIST_NAME)
if (!sheet) {
try { wb.insertSheet(MSG_HIST_NAME).hideSheet(); }
catch (e) { }
// By definition, no processed messages.
return {};
}
const vals = sheet.getSheetValues(1, 1, sheet.getLastRow(), 1);
return vals.reduce(function (acc, row) {
// acc is our "accumulator", and row is an array with a single message id.
acc[ row[0] ] = true;
return acc;
}, {});
}
function setProcessedMessages(msgObject, wb) {
if (!wb) wb = SpreadsheetApp.getActive();
if (!msgObject) return;
var sheet = wb.getSheetByName(MSG_HIST_NAME);
if (!sheet) {
sheet = wb.insertSheet(MSG_HIST_NAME);
if (!sheet)
throw new Error("Unable to make sheet for storing data");
try { sheet.hideSheet(); }
catch (e) { }
}
// Convert the object into a serializable 2D array. Assumes we only care
// about the keys of the object, and not the values.
const data = Object.keys(msgObject).map(function (msgId) { return [msgId]; });
if (data.length) {
sheet.getDataRange().clearContent();
SpreadsheetApp.flush();
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
Usage would be something like:
function foo() {
const myLabel = /* get label somehow */;
const processed = getProcessedMessages();
myLabel.getThreads().forEach(function (thread) {
thread.getMessages().forEach(function (msg) {
var msgId = msg.getId();
if (processed[msgId])
return; // nothing to do for this message.
processed[msgId] = true;
// do stuff with this message
});
// do more stuff with the thread
});
setProcessedMessages(processed);
// do other stuff
}
Recommended Reading:
Is checking an object for a key more efficient than searching an array for a string?
Array#reduce
Array#map
Array#forEach
the following script below will read my email and pull a value from an email as well as the recipient of the message. I'm looking to add to the code in which I just get the email address for the recipient.
Currently, the code will process: John Doe *** john.doe#gmail.com ****
- I just want the code to pull john.doe#gmail.com, without the arrow bracket symbols
Any insight on where to add this is greatly appreciated!
// Modified from http://pipetree.com/qmacro/blog/2011/10/automated-
email-to-task-mechanism-with-google-apps-script/
// Globals, constants
var LABEL_PENDING = "example label/PENDING";
var LABEL_DONE = "example label/DONE";
// processPending(sheet)
// Process any pending emails and then move them to done
function processPending_(sheet) {
// Date format
var d = new Date();
var date = d.toLocaleDateString();
// Get out labels by name
var label_pending = GmailApp.getUserLabelByName(LABEL_PENDING);
var label_done = GmailApp.getUserLabelByName(LABEL_DONE);
// The threads currently assigned to the 'pending' label
var threads = label_pending.getThreads();
// Process each one in turn, assuming there's only a single
// message in each thread
for (var t in threads) {
var thread = threads[t];
// Gets the message body
var message = thread.getMessages()[0].getBody();
var recipient = thread.getMessages()[0].getTo();
// Processes the messages here
orderinfo = message.split("example");
rowdata = orderinfo[1].split(" ");
// Add message to sheet
sheet.appendRow([rowdata[1], recipient]);
// Set to 'done' by exchanging labels
thread.removeLabel(label_pending);
thread.addLabel(label_done);
}
}
// main()
// Starter function; to be scheduled regularly
function main_emailDataToSpreadsheet() {
// Get the active spreadsheet and make sure the first
// sheet is the active one
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.setActiveSheet(ss.getSheets()[0]);
// Process the pending emails
processPending_(sh);
}
Regarding your last error message:
var LABEL_PENDING = "example label/PENDING";
var LABEL_DONE = "example label/DONE";
script will search labels with the name: "example label/PENDING" and "example label/DONE". Are you sure that you have the label with name "example label/PENDING" or "example label/DONE"?
Here is a little bit modified code based on your example. You just need to create label "PENDING" and mark some messages with this label.
var LABEL_PENDING = "PENDING";
function processPending () {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
// Date format
var d = new Date();
var date = d.toLocaleDateString();
// Get out labels by name
var label_pending = GmailApp.getUserLabelByName(LABEL_PENDING);
// The threads currently assigned to the 'pending' label
var threads = label_pending.getThreads();
// Process each one in turn, assuming there's only a single
// message in each thread
for (var i = 0; i <threads.length; i++) {
var thread = threads[i];
// Gets the recipient
var recipient = thread.getMessages()[0].getTo();
// Add recipient to sheet
sheet.appendRow([recipient]);
}
}
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";
}
}
I am using the following two scripts to either reply to or forward emails when certain labels are applied. I have two sheets (replySheet and forwardSheet) that hold label names in the first column. replySheet then has the email reply text in the next cell, while forwardSheet has the email address to forward the message to.
Two questions:
I have received the error message "Service invoked too many times for one day" for GmailApp.getUserLabelByName. I understand that the limit for Google Apps for Education is 10,000 per day, but this code should just run every five minutes, or 288 times every day for each label. What am I misunderstanding? Any thoughts for re-writing the code to avoid this?
.moveToArchive() doesn't seem to do anything in replyLabel(). I've tried moving it to different points in the code (before and after sending the reply), but it doesn't archive the thread.
Thank you for any suggestions to either issue. Please let me know if I can make my question any clearer.
var thisSS = SpreadsheetApp.getActiveSpreadsheet();
var forwardSheet = thisSS.getSheetByName('Forwards');
var emailSheet = thisSS.getSheetByName('Email');
var alias = emailSheet.getRange(3, 2).getValue();
var replyTo = emailSheet.getRange(2, 2).getValue();
var fromName = emailSheet.getRange(1, 2).getValue();
var replySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Replies');
function forwardLabel() {
var data = forwardSheet.getRange(2, 1, forwardSheet.getLastRow(), 2).getValues();
for (i in data) {
var row = data[i];
var name = row[0].toString();
var email = row[1].toString();
if (name && (email != "")) {
var label = GmailApp.getUserLabelByName(name);
var threads = label.getThreads(0, 100);
for (i in threads) {
var messages = threads[i].getMessages();
for (j in messages) {
Logger.log(messages[j].getSubject());
messages[j].forward(email, {bcc:alias, from:alias, name:fromName}).markRead();
label.removeFromThread(threads[i]);
}
Utilities.sleep(1000);
}
}
}
}
function replyLabel() {
var data = replySheet.getRange(2, 1, replySheet.getLastRow(), 2).getValues();
var signature = emailSheet.getRange(4, 2).getValue().toString();
var alias = emailSheet.getRange(3, 2).getValue();
for (i in data) {
var labelName = data[i][0].toString();
var label = GmailApp.getUserLabelByName(labelName);
var replyText = data[i][1].toString();
replyText = replyText + signature;
if (label && (replyText !== "")) {
var labeledEmails = label.getThreads(0, 100);
for (j in labeledEmails) {
labeledEmails[j].moveToArchive();
label.removeFromThread(labeledEmails[j]);
var messages = labeledEmails[j].getMessages();
var message = messages[0];
message.reply(replyText,{htmlBody:replyText, bcc:alias, from:alias, name:fromName});
Utilities.sleep(2000);
}
}
}
}
How many labels do you have ? You have nested loops and the 288 gets multiplied by each loop and you could quickly be hitting the 10,000.
Also, note that you are not counting other GMail Read operations like getTHreads() and getMessages().
If you factor in all these, you could have a number exceeding 10,000.