apps script: Process emails that contain specific text **only** - google-apps-script

I have some production code that I am repurposing. Currently, it processes everything based on the gmail label and puts a specified portion into a spreadsheet. This will not change (though it could if that was the best way to solve this problem).
I now need to process a subset of those messages and put a portion of the message body into a spreadsheet.
The body of the text is as follows:
This is an email alarm.
Room Lights OFF.
(Other examples might say "Room Lights ON", "ALARM", or "OK".
I only need to get the portion of the body after "Room Lights".
Also included in this gmail label are emails that do not mention "Room Lights" at all. These should be ignored and not added to the spreadsheet.
Example:
This is an email alarm.
Network has returned.
or
This is an email alarm.
Generator is OFF.
Question: How can I edit my code so that only "Room Lights" emails are added to the spreadsheet?
Example code:
function lightFunction() {
newMonth();
var label = GmailApp.getUserLabelByName("Lights");
var label2 = GmailApp.getUserLabelByName("Processed");
var threads = label.getThreads();
var data = new Array();
var newData = new Array();
var alarmKeys = "This is an email alarm.";
var keys = alarmKeys.split(",");
var range = "A2:B";
// get all the email threads matching label
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
// archive thread
// label2.addToThread(threads[i]);
// label.removeFromThread(threads[i]);
// get each individual email from the threads
for (var j = 0; j < messages.length; j++) {
var bodyText = messages[j].getPlainBody();
for (k in keys) {
bodyText = bodyText.replace(keys[k], "");
}
bodyText = bodyText.trim();
var date = messages[j].getDate(); // date/time
var lines = [date, bodyText];
// Turn the first element in the array into a date element, format it, and put it back
lines[0] = Utilities.formatDate(new Date(lines[0]), "America/Phoenix", "M/d/yy HH:mm:ss");
// Put the array to a new item in the data array for further processing
if (curMonth == (new Date(lines[0]).getMonth())) {
data.push(lines);
}
}
}
checkAdd(data, range);
}

ALTERNATE METHOD
You could also tweak the part with data.push(lines); with regex using a match method in one line, something like this:
lines[1].match(/Room Lights|OK|ALARM/gm) ? data.push(lines) : null;
You may also check this sample regex test.
In my understanding here is the flow:
Read emails under labels Lights / Processed
Check each email that may match the following texts:
"Room Lights OFF", "Room Lights ON", "ALARM", or "OK"
Only add the matched emails on the spreadsheet
Tweaked Script
function lightFunction() {
newMonth();
var curMonth = new Date().getMonth()
var label = GmailApp.getUserLabelByName("Lights");
var label2 = GmailApp.getUserLabelByName("Processed");
var threads = label.getThreads();
var data = new Array();
var newData = new Array();
var alarmKeys = "This is an email alarm.";
var keys = alarmKeys.split(",");
var range = "A2:B";
// get all the email threads matching label
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
// archive thread
// label2.addToThread(threads[i]);
// label.removeFromThread(threads[i]);
// get each individual email from the threads
for (var j = 0; j < messages.length; j++) {
var bodyText = messages[j].getPlainBody();
for (k in keys) {
bodyText = bodyText.replace(keys[k], "");
}
bodyText = bodyText.trim();
var date = messages[j].getDate(); // date/time
var lines = [date, bodyText];
// Turn the first element in the array into a date element, format it, and put it back
lines[0] = Utilities.formatDate(new Date(lines[0]), "America/Phoenix", "M/d/yy HH:mm:ss");
// Put the array to a new item in the data array for further processing
if (curMonth == (new Date(lines[0]).getMonth())) {
//Tweaked part
lines[1].match(/Room Lights|OK|ALARM/gm) ? data.push(lines) : null;
//Tweaked part
}
}
}
checkAdd(data, range);
}
Demonstration
Sample emails
Log test of the data variable
References
Match
Regex
Conditional (ternary) operator

Although I'm not sure whether I could correctly understand your showing script, if your data is used in checkAdd(data, range); and data is put to the Spreadsheet, and you want to filter data by Question: How can I edit my code so that only "Room Lights" emails are added to the spreadsheet?, how about the following modification?
From:
var bodyText = messages[j].getPlainBody();
To:
var bodyText = messages[j].getPlainBody();
if (!bodyText.includes("Room Lights")) continue;
Or, if you want to check multiple texts, how about the following modification?
var bodyText = messages[j].getPlainBody();
var searchTexts = ["Room Lights", "sample",,,]; // Please set search texts you want.
if (!searchTexts.some(e => bodyText.includes(e))) continue;
Reference:
includes()

Related

Move emails with labels to trash after 1 day fail

When I use the script below, sourced from the web, it only works for one (Cam1) of the two labels . The labels within Gmail are associated with the emails and they are older than 1 day.
Why is this script only working as written on one label?
Im new at this so please keep it simple! Thanks
function oldEmailDeletion() {
//Age of email threads that will be deleted (i.e. older_than: # days)
var daysAgo = 1;
//Expiration date variable
var expirationDate = new Date();
//Set the older_than date. Any email older than this date will be deleted
expirationDate.setDate(expirationDate.getDate()-daysAgo);
//Labels associated with emails to be included in deletion
var labels = [
'Cam1',
'Cam2'
];
//Loop through each email label found in the "labels" variable array
for(var i = 0; i < labels.length; i++){
//Retrieve label information based on value in "labels" variable array
var label = GmailApp.getUserLabelByName(labels[i].toString());
//Access all email threads associated with the retrieved label
var emailThreads = label.getThreads(); //getThreads(###,###) if a specific range of email threads to retrieve
//Loop through each email thread set to "emailThreads" variable
for(var j = 0; j < emailThreads.length; j++){
//If an email thread is older than the expiration date, then delete
if(emailThreads[j].getLastMessageDate() < expirationDate){
emailThreads[j].moveToTrash();
}
}
}
}
Try it this way:
function oldEmailDeletion() {
var dt=new Date();
var exp=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1).valueOf();
var labels=['Cam1','Cam2'];
for(var i=0;i<labels.length;i++){
var label=GmailApp.getUserLabelByName(labels[i].toString());
var emailThreads=label.getThreads();
for(var j=0;j<emailThreads.length;j++){
if(emailThreads[j].getLastMessageDate().valueOf()<exp){
emailThreads[j].moveToTrash();
}
}
}
}
If you actually want to delete the messages delete the message older than one day then this approach should work for you. And you will need to Enable the Gmail API.
function oldEmailDeletion() {
const dt=new Date();
const exp=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1).valueOf();
const labels=['Cam1','Cam2'];
var idA=[];
for(var i=0;i<labels.length;i++){
var label=GmailApp.getUserLabelByName(labels[i].toString());
var emailThreads=label.getThreads();
for(var j=0;j<emailThreads.length;j++){
if(emailThreads[j].getMessageCount()>0) {
var msgs=emailThreads[j].getMessages();
for(var k=0;i<msgs.length;j++) {
var msg=msgs[k];
if(new Date(msg.getDate()).valuefOf()<exp) {
idA.push(msg.getId());
}
}
}
}
}
if(idA.length>0) {
var request={"ids":idA};
Gmail.Users.Messages.batchDelete(request, "me");
}
}
If you just want to move them to the trash. Then this approach should work for you.
function oldEmailTrash() {
const dt=new Date();
const exp=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1).valueOf();
const labels=['Cam1','Cam2'];
var idA=[];
for(var i=0;i<labels.length;i++){
var label=GmailApp.getUserLabelByName(labels[i].toString());
var emailThreads=label.getThreads();
for(var j=0;j<emailThreads.length;j++){
if(emailThreads[j].getMessageCount()>0) {
var msgs=emailThreads[j].getMessages();
for(var k=0;i<msgs.length;j++) {
var msg=msgs[k];
if(new Date(msg.getDate()).valueOf()<exp) {
idA.push(msg.getId());
}
}
}
}
}
if(idA.length>0) {
idA.forEach(function(msg){msg.moveToTrash();});
}
}
Thanks Cooper. I tried for days but lacked the skills to debug your scripts. I found and modified the script below and it works.
First I created a new filter in gmail that labeled all cam emails 'Clean' then set the script below on a trigger. This was the only way I was able to achieve my goal.
Thanks again!
function gmailCleaningRobot() {
var delayDays = 1; // will only impact emails more than 24h old
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays); // what was the date at that time?
// creating an array containing all the search strings matching the emails we want to be treated automatically
var searches = [
'label:Clean older_than:1d' //with label clean and older than 1d
//'"is now available on Spotify" from:spotify is:unread', // Spotify new album notification
//'YOUR NEW SEARCH STRING HERE', // any other search string
//'YOUR NEW SEARCH STRING HERE', // any other search string
//'YOUR NEW SEARCH STRING HERE', // any other search string
//'YOUR NEW SEARCH STRING HERE' // any other search string
];
// creating an array containing all the threads matching the searches above
var threads = [];
for (var i = 0; i < searches.length; i++) {
var tmp_threads = GmailApp.search(searches[i], 0, 500); // I limit the search to 500 results but you can adjust this one
var threads = threads.concat(tmp_threads);
}
// we archive all the threads if they're older than the time limit we set in delayDays
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()<maxDate)
{
threads[i].moveToTrash();
}
}
}

Google Script to format information pulled through script

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

google apps script very very slow

I am using a google spreadsheet as a simple database of members and I want to create a user interface for searching through them (The primary users for this are not very technically adept and there is quite allot of data associated with each member so viewing it as a spreadsheet can be a bit tedius)
I have written the folowing script which worked last night but appears to run so slowly it times out today and I have no idea why.
function findMember() {
// set spreadsheet variable
var SS = SpreadsheetApp.getActiveSpreadsheet();
// set sheet variables
var memberSearchSheet = SS.getActiveSheet();
var memberDataSheet = SS.getSheetByName("Member Data");
// get the search variables
var searchFirstName = memberSearchSheet.getRange('C2').getValue();
var searchLastName = memberSearchSheet.getRange('C3').getValue();
// get last row of data
var lastRow = memberDataSheet.getLastRow();
for (var i = 2;lastRow;i=i+1){
if (searchFirstName == memberDataSheet.getRange('R'+i+'C2').getValue() && searchLastName == memberDataSheet.getRange('R'+i+'C3').getValue()){
memberSearchSheet.getRange('C5').setValue(memberDataSheet.getRange('R'+i+'C5').getValue());
//throw new Error("ouch")
}
}
// small pop up notification in bottom right corner .toast(message, title, display time)
//var message = "Your search for " + searchFirstName + " " + searchLastName + " is complete.";
//SS.toast(message,"Search Complete",15);
};
You can probably trying getting all the data inside an array in one step and then quickly compare your value with those in the array. Something like this:
var sheet = SpreadsheetApp.getActive().getSheetByName("Member Data");
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
if (data[i][0] == firstName && data[i][1] == secondName) {
throw("Found");
}
}

Google Apps Script: Submit data into specified column

I am trying to find a script, or begin writing one, that takes a simple Google Form with a drop-down list of names (i.e. Tom, Jane) and a text area, and inputs both the date and the text into columns based on the selected name (i.e. Tom Date, Tom Comment). This is so I can make a quick entry feedback form for leaving individualized, date-based feedback for students, which they can then access later.
I looked through the GAS documentation and looked for examples, but as I am a novice, I really didn't know where to begin.
Any ideas on how to do this?
I think I did something similar but mine is for admins to observe teachers. I'm just learning as well, so I'm sure there are better ways to do this but it works. I definitely should have broken it up into more functions.
My form has a trigger to fire the onClose() when submitted. onClose() produces a Google Doc by reading the spreadsheet containing the form data in a nice format that the observer can then edit and share with the teacher. I wanted the Google Doc produced to have the name of the teacher being observed in the file name and I wanted it shared with the admin who did the observing.
The fact that some of the fields are dropdowns doesn't matter, it is just an itemResponse from the list of all responses.
function onClose()
{
var form = FormApp.openById(' whatever your form id is');
//the spreadsheet of the form responses
var formResponses = form.getResponses();
var d = new Date();
var currentTime = d.toLocaleTimeString();
var date = d.toLocaleDateString();
//Need to get the name of the teacher being observed
var formResponse = formResponses[formResponses.length-1];
var itemResponses = formResponse.getItemResponses();
var itemResponse = itemResponses[0]; //the teacher name dropdown box
var teacherName = itemResponse.getResponse() + '-' + itemResponses[1].getResponse();
//create the new document
var fileName = 'Observation-'+ teacherName + '-' + date + '-' + currentTime;
var doc = DocumentApp.create(fileName);
var activeDoc = DocumentApp.getActiveDocument();
var files = DriveApp.getFilesByName(fileName);
while (files.hasNext()) {
var file = files.next();
if (file.getName().equals(fileName))
{
//this is the last item on my form the name of the observer
var itemResponse21 = itemResponses[21];
var observer = itemResponse21.getResponse();
// Logger.log('Person to share with is ' + observer);
// share this google doc with the observer
file.addEditor(observer);
}
}
//ommittd a bunch of styles
//This would get all forms submitted, but I only need the last one
// so I just set the loop to get the last form submitted.
//leaving for loop just so I remember I can go through all forms again
//if I want to.
for (var i = formResponses.length-1; i < formResponses.length; i++) {
var formResponse = formResponses[i];
var itemResponses = formResponse.getItemResponses();
//get the individual responses within the form.addCheckboxItem()
for (var j = 0; j < itemResponses.length; j++) {
//pull the first item out again (even though I did for file name)
var itemResponse = itemResponses[j]; //teacher name from a dropbox
var itemResponse2 = itemResponses[j+1]; //date
var itemResponse3 = itemResponses[j+2]; //time
if (j == 0) //the first field on the form
{
//put the headings in
par3 = doc.appendParagraph(' SCHOOL NAME');
par3 = doc.appendParagraph('WALK IN OBSERVATION');
par3 = doc.appendParagraph('2013-2014');
//THIS is the teacher being observed and the date and time --- all on same line
var headingLine = itemResponse.getItem().getTitle() + '\t\t' + itemResponse2.getItem().getTitle() + ' / ' + itemResponse3.getItem().getTitle();
par1 = doc.appendParagraph(headingLine);
var answerLine = itemResponse.getResponse() + '\t\t\t\t\t' + itemResponse2.getResponse() + ' / ' + itemResponse3.getResponse();
par2 = doc.appendParagraph(answerLine);
j++; //do this to skip over date and time
j++;
} //end of j = 0;
else
// then I have a bunch of if statements for some of the
// specific fields I need to do something special with.
// After the last if, I just have an else to handle all other
// form responses that I don't do anything special for other than display.
//my last else is:
else
//THIS ELSE IS HANDLING ALL NON CHECK BOXES AND JUST DISPLAYING THE TITLE IN BOLD FONT, THE COMMENTS IN REGULAR FONT
{
par1 = doc.appendParagraph(itemResponse.getItem().getTitle());
par1.setAttributes(style);
par2 = doc.appendParagraph( itemResponse.getResponse());
par2.setAttributes(style);
} //END OF ELSE
} //end of for going through each cell in a row of the repsonses
} //end of for going through each row -- only had it set to do the very last row

GmailApp and forward/reply Invoked too many times - Not clear why (plus movetoarchive not working)

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.