Counting emails received in date range (current count too high) - google-apps-script

I am trying to set up a very simple script to count the amount of individual emails (not threads) received in Gmail over a specific timeframe. Currently testing with set dates where I know what the individual message count should be.
I have used the following code
function emailCount() {
var receivedCount = 0;
var threads = GmailApp.search("to:("my email") after:2023/02/01 before:2023/02/02");
Logger.log(threads.length);
for (var i = 0; i < threads.length; i++)
{
receivedCount = receivedCount + threads[i].getMessageCount();
}
Logger.log(receivedCount);
}
Which returns the correct amount of email threads, but the email count is too high. The script is counting individual emails from the returned threads, even if those emails do not fit my search parameters (i.e. not in my date range).
Is there a way to return an individual email count from a date range, so I can get an accurate count for how many emails were received within a certain date range?
Similar posts have been made about email counts (I got the script from this post- Google Apps Script to count number of emails sent/received yesterday, that has a certain label, then save # daily to spreadsheet) but I can't see how I can achieve the count I am after.
Solved!
Thanks to #Tedinoz for their post below and advice. Amended their code slightly to create a function that generates an email count on a daily basis and saves it in a spreadsheet.
//Not my code! All credit to #Tedinoz and others recognised at the end of the post.
function emailCount() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1')
var startDate = sheet.getRange('C1').getValue(); // the start date of search range
var endDate = sheet.getRange('E1').getValue(); // the end date of serach range
Logger.log("DEBUG: Search Start date = "+startDate+",\nSeachEnd date = "+endDate)
var startDateFormated = formatDate(startDate); // using function to covert date to yyyy/mm/ddd so it can be used in a gmail search.
Logger.log ("DEBUG: Formated start date = "+startDateFormated);
var endDateFormated = formatDate(endDate);// using function to covert date to yyyy/mm/ddd so it can be used in a gmail search.
Logger.log ("DEBUG: Formated end date = "+endDateFormated);
Logger.log("to:(henry.ward#york.ac.uk) after:"+startDateFormated+" before:"+endDateFormated); // log of what the gmail search will be
var threads = GmailApp.search("to:(henry.ward#york.ac.uk) after:"+startDateFormated+" before:"+endDateFormated); // gmail search
var threadCount = threads.length // pulls threads from search
var messsageCounter = 0 // sets the message counter as 0
for (var i=0;i<threadCount;i++){ // loops through the threads to get messages
var messages = threads[i].getMessages()
var messageCount = messages.length
Logger.log("DEBUG: Number of messages: "+messageCount)
for (var m=0;m<messageCount;m++){ // loops through the messages to get dates
var messageDate = messages[m].getDate()
var subject = messages[m].getSubject() // pulls subject for easy debugging
var sender =messages[m].getFrom() // pulls from as will need to exlude certain senders
//Compare message date to search parameters. As well as dates needed to exclude emails sent by myself, as these were being included in the message counts from threads.
if (startDate.valueOf() <= messageDate.valueOf()&&messageDate.valueOf()<=endDate.valueOf()&&sender.valueOf()!="My name <my email address>"){
Logger.log("DEBUG: Valid message: message date = "+messageDate+" subject = "+subject+" from = "+sender)
messsageCounter++
}
else{
Logger.log("DEBUG: Failed message: message date = "+messageDate)
}
}
}
Logger.log("Number of valid messages = "+messsageCounter)
// last section of script saves returned count into my spreadsheet. I have a trigger for a daily count, so it saves it into the last entry of a column.
//Also not my original code, but cannot find where I originally copied it from!
var columnIndex = 8 ; //col h - this is the col where I want to save the email count
var values = sheet.getRange(1, columnIndex, sheet.getMaxRows()).getValues();
for (var t = 0; t < values.length; t++) {
if (values[t] == "") {
var emptyCellIndex = t + 1;
break;
}
}
sheet.getRange(emptyCellIndex, columnIndex).setValue(messsageCounter);
}
To format the date, so it can be used in a gmail search function, I used the following code from #user3470953. Found as an answer to the post - Format JavaScript date as yyyy-mm-dd
function formatDate(date) {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2)
month = '0' + month;
if (day.length < 2)
day = '0' + day;
return [year, month, day].join('/');
}

GmailApp.search returns threads. In order to evaluate/identify the specific messages that satisfy the dates in the search query, one must loop through the messages, get the message date, and compare the message date to the "from"/"to" dates of the query.
The following script is an example of looping through the messages recovered from GMail.search. The number of threads returns was 1 (one); the number of messages = 5 (five); however only 2 (two) messages satisfied the criteria for the "from"/"to" dates.
function myFunction() {
var startDate = new Date(2023, 0, 24); // the month is 0-indexed
var endDate = new Date(2023, 0, 27); // the month is 0-indexed
Logger.log("DEBUG: Search Start date = "+startDate+",\nSeachEnd date = "+endDate)
var threads = GmailApp.search("subject:(Britax Safe-n-Sound Compaq AHR) after:2023/1/24 before:2023/1/27");
var threadCount = threads.length
var messsageCounter = 0
for (var i=0;i<threadCount;i++){
var messages = threads[i].getMessages()
var messageCount = messages.length
Logger.log("DEBUG: Number of messages: "+messageCount)
for (var m=0;m<messageCount;m++){
var messageDate = messages[m].getDate()
//Compare message date to search parameters
if ((startDate.valueOf() <= messageDate.valueOf()) ||(messageDate.valueOf()<=endDate.valueOf)){
Logger.log("DEBUG: Valid message: message date = "+messageDate)
messsageCounter++
}
else{
Logger.log("DEBUG: Failed message: message date = "+messageDate)
}
}
}
Logger.log("Number of valid messages = "+messsageCounter)
}
RESULTS

Related

Email notification when date due expired

first of all I would like to say that I am really a rookie at Apps Script.
I have found Script Code for automated Email notification when certain date is expired. I tried to alter the code for my SpreadSheet, but unfortunately it does not work properly for me:
function offboardingReminder() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// set active sheet to first sheet
spreadsheet.setActiveSheet(spreadsheet.getSheets()[0]);
var sheet = spreadsheet.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
var startRow = 2;
// grab all data from user to days left
var range = sheet.getRange(startRow, 1, lastRow - startRow + 1, 45);
var values = range.getValues();
var users = [];
// loop all data per row
values.forEach(function(row) {
// if days left is 3
if(row[45] <= 30) {
// add user if 3 days left
users.push(row[0]);
}
});
// if users has elements
if(users) {
// Formatted the message as html to look nicer
var message = "<html><body><h2>Reminder</h2><p>The following device/s service is due in 30 days or less:</p>";
// created bulleted list for list of users
var emails = "<ul>";
users.forEach(function(user){
emails = emails + "<li>" + user + "</li>";
});
emails += "</ul>";
message = message + emails + "</body></html>";
MailApp.sendEmail("norbert.jedrzejczak#kbcpl.pl", "Reminder Date Dues Services", "", {htmlBody: message, noReply: true});
}
}
So what I would like to accomplish here is to send automated notification every week with the list of expired services.
I have a spreadsheet with 51 columns and I would like to:
Take the name of the customer (column 31)
Take the date of last service (column 41)
Take the number of days left to next service (column 44) - and take these values which are equal or less than 30
And send a message with these data.
Subject: "Reminder Date Dues Services" + Current Date (of sending each email)
Body:
"Reminder! The following device/s service is due in 30 days or less:"
And below - a list of devices to be serviced - e.g. "CUSTOMER 1 - DEVICE 1 - SERIAL NUMBER 1 - DATE OF LAST SERVICE - DATE TO NEXT SERVICE"
Could you please help me out with the code? I would be grateful for your tips and recommendations.
Just to get you started:
function lfunko() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const sr = 2;
const vs = sh.getRange(sr,sh.getLastRow() - sr + 1, sh.getLastColumn()).getValues();
vs.forEach((r,i) => {
let n = r[30];
let dt = r[40];
let ds = r[43];
if(ds < 30 ) {
//complete message and email in here
}
})
}

Why does my spreadsheet script throw a TypeError when I call getTime()?

I am writing a small function using the Google Apps Script editor that checks that an event date in a spreadsheet is within 12 hours of the current date to send me a morning email notification. The for loop below loops through the dates column in my spreadsheet to find the current date (entered in the spreadsheet formatted as a date expressed as MM/DD/YYYY). I can't figure out why schedule[i][0].getTime() is giving me the following error:
TypeError: Cannot find function getTime in object .
Here's the function:
function sendEmail() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Schedule"); // get sheet
var lastRow = spreadsheet.getLastRow(); // find length of sheet
var schedule = spreadsheet.getRange(6, 1, lastRow, 5).getValues(); // this an array with dates and info on events (first column has dates)
var now = new Date().getTime(); // get today's date in ms
var MILLIS = 1000 * 60 * 60 * 12 // get number of milliseconds in 12 hours
for (var i=0; i <= lastRow; i++) {
var eventDate = schedule[i][0].getTime()
if (now - eventDate < MILLIS && now > eventDate) {
...send email...
};
};
};
I've checked to make sure that the schedule[i][0] object is a valid date (e.g. myvar instanceof Date, debugger, format in google sheet, etc.), and everything indicates that it is. Yet, any method I've tried that nests the if clause in a function or calls getTime() more than once in the if statement causes a TypeError.
What am I doing wrong that is causing this error? How can I edit my code so that the condition in my if statement only runs if the difference between now and the date in schedule[i][0] is less than 12 hours?
Here's a link to a spreadsheet showing how the data is formatted.
Thanks for your help!
Edit: I edited the question to match the "minimal reproducible example" format. The problem is with the for loop, which doesn't account for the header. Thanks to tehhowch for the fix.
This works on your data.
Using your data and a few minor tweaks to the code. I also used valueOf() instead of getTime() ...it's some thing I prefer using.
function sendEmail() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName("Sheet177");
var schedule = sh.getRange(6, 1, sh.getLastRow(), 1).getValues(); // this an array with dates and info on events (first column has dates)
var halfday = 1000 * 60 * 60 * 12;
var idxA=[];
for (var i=0;i<schedule.length;i++) {
var now=new Date().valueOf();
var sked=new Date(schedule[i][0]).valueOf();
var diff=now-sked;
if ((diff<halfday) && (now>sked)) {
idxA.push(i);//I am just collecting the indexes of the var schedule that are within 12 hours of now.
//send email
}
}
Logger.log(idxA);
}
Per tehhowch, the problem is that my for loop did not account for the header rows in my spreadsheet or that an array is 0-indexed.
Removing the empty rows in the array and giving the right limit in the for loop fixed the problem:
...
function sendEmail() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Schedule"); // get active sheet
var lastRow = spreadsheet.getLastRow(); // find length of sheet
var schedule = spreadsheet.getRange(6, 1, lastRow - 5, 5).getValues(); // get values from first column of dates
var now = new Date().valueOf(); // get today's date
var MILLIS = 1000 * 60 * 60 * 12 // get number of ms in 12 hours
for (var i=0; i <= schedule.length - 1; i++) {
var eventDate = schedule[i][0].getTime()
if (now - eventDate < MILLIS && now > eventDate) {
...send email...
};
};
};

How to sum up date values to a number (days' sum)?

In Google Sheets I have a list of activities with a start date, and a number that specifies the duration in days of that activity. I need to use Google Apps Script to sum those numbers to the date, to obtain the deadline for the activity.
I've tried the solution posted in this question: Adding Days to a Date - Google Script.
The problem with that solution is that the script editor of the spreadsheet doesn't recognize the "Date" Class, so I can't instantiate a Date element.
Summing directly only takes the date and the number as a string.
Trying the method above results in a #NUM! error in the cell I want to convert.
EDIT:
I've tried this, where V3 holds the date I want to sum:
var fecha= new Date (ss.getSheetByName(camada).getRange("V3").getValue());
var fecha2= new Date();
fecha2.setDate(fecha.getDate() + 1);
ss.getSheetByName(camada).getRange("W3").setValue(fecha2);
It apparently works, but the problem is that V3 holds 5/13/2019 and the date returned is 4/14/2019, so it is a day more (13->14) but it is a month less (5->4).
The answer was in Adding Days to a Date - Google Script.
Three things:
don't define fecha2 as new Date(); this gives it no context and instead returns today's date.
let fecha2 be a variable name
the correct statement is var fecha2 = new Date(fecha.setDate(fecha.getDate() + 1));
function so55593876() {
var ss = SpreadsheetApp.getActiveSheet();
var range = ss.getRange("C3");
var value = range.getValue();
Logger.log("DEBUG: Date Range: "+range.getA1Notation()+", Date value: "+value);//DEBUG
var date = new Date(value); // make the sheet value a date object
Logger.log("DEBUG: new date(value) = "+date);//DEBUG
var dateTime = new Date(date.getTime()+1*3600000*24);
Logger.log("DEBUG: Method 1 (add one day = getTime()+1*3600000*24) = "+dateTime);//DEBUG
var dateDate = new Date(date.setDate(date.getDate()+1));
Logger.log('DEBUG: Method 2 (add one day = getdate()+1) = '+dateDate);//DEBUG
ss.getRange("C4").setValue(dateDate);
Logger.log("<<<<<<<<<<<FETCHA>>>>>>>>>>>>>");
var fecha = new Date(value); // make the sheet value a date object
Logger.log("DEBUG: fecha: new date(value) = "+fecha);//DEBUG
var fecha2= new Date(); // note there are no parameters; this will return TODAY's date
Logger.log("DEBUG: fecha2 = "+fecha2);//DEBUG
var fecha3 = fecha2.setDate(fecha.getDate() + 1);
Logger.log("DEBUG: fecha3 = "+fecha3); //DEBUG
var fecha2 = new Date(fecha.setDate(fecha.getDate() + 1));
Logger.log("DEBUG: fecha2 = "+fecha2); //DEBUG
ss.getRange("C5").setValue(fecha2);
}
I have left all the Logger statements in the code so that you can identify the various values at different states of the script.

Send email from G-Sheets after 4 weeks

I am using Google Sheets, and I'm trying to send an email to myself, after 4 weeks of a specified date, with information in the body of that email extracted from a certain cell. The cell is in the same row (Col B) that the date is in (Col Q). I have successfully figured out the timing issue, but I just can't get the info from the cell to go into the body of the email.
Here is the code I have.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getRange(9,17,31).getValues();
var now = Date.now();
for (var i = 0; i < data.length; i++) {
var row = data[i];
var date = new Date(row[0]);
var remind_date = new Date(date.getTime() + 28 * 24 * 60 * 60 * 1000);
var diff = now - remind_date;
var case_name = row[2];
if ((diff >= 0) && (diff < 24 * 60 * 60 * 1000)) {
GmailApp.sendEmail('myemail#me.com', 'Case Inquiry Reminder','Reminder, inquire about status of ', case_name);
}
}
}
First part
On the question it's mentioned that the date is on column Q but the code is getting column A instead.
var date = new Date(row[0]);
Maybe the correct code is
var date = new Date(row[16]);
While some Spreadsheet Service methods use 1 based index, JavaScript arrays use zero based index. Because of this, the index of column Q is 16.
Second part
For the same reason mentioned previously regarding JavaScript indexes, as you said that you need the value of column B, instead of
var case_name = row[2];
the code should be
var case_name = row[1];
Reference
Array
Third part
There is a problem on the following line
GmailApp.sendEmail('myemail#me.com', 'Case Inquiry Reminder','Reminder, inquire about status of ', case_name);
It uses four arguments but the the fourth isn't of the proper type. Maybe you intend to write
GmailApp.sendEmail('myemail#me.com', 'Case Inquiry Reminder','Reminder, inquire about status of ' + case_name);
(replace the last comma , by a plus sign +)
Reference
sendEmail(recipient, subject, body, options)

Cannot find function getMonth in object 33463

I have created a script to send emails to a specific people with Birthday Reminder. This use to work till day before yesterday. I don't know why am I getting this error that Cannot find function getMonth, Can anyone tell where is the mistake
function emailAlert() {
// Short circuit if email notice is set to "No". This causes an error and the script stops.
if (turnOnEmailNotice.toLowerCase() == "no")
{
Logger.log("The Email Notification is NOT turned ON. System will Exit.");
exit
}
//Get the total number of filled row in the sheet.
var currentRowAT = 2;
var currentCellValueAT = "start";
while (currentCellValueAT != ""){
if (currentCellValueAT = birthdaysSheet.getRange("G" + currentRowAT).getValue() != ""){
currentRowAT = currentRowAT +1;
}
}
var birthdaysSheetLastRow = currentRowAT - 1;
// Get today's Date (with Month, Date and Year)
var today = new Date();
var todayMth = today.getMonth()+1;
var todayDate = today.getDate();
var todayYear = today.getFullYear();
// Check sheet cell for match to alertDate, k is the current row number being checked. Starting at 2 as the row #1 is for the headers
for (k=2; k < birthdaysSheetLastRow + 1; k++)
{
var targetBday = new Date();
targetBday = birthdaysSheet.getRange("P" + k).getValue();
// If Birthday is not speicified, continue with the next row
if (targetBday == ""){continue};
var unadjTargetBday = new Date();
var unadjTargetBdayMth = targetBday.getMonth()+1;
var unadjTargetBdayDate = targetBday.getDate();
var unadjTargetBdayYear = targetBday.getFullYear();
var unadjTargetBday = targetBday;
targetBday.setDate(targetBday.getDate()-daysInAdvance); // Calculating how many days in advance you want to trigger the notification. This is set in Settings Tab.
var targetBdayMth = targetBday.getMonth()+1;
var targetBdayDate = targetBday.getDate();
if (targetBdayMth + " " + targetBdayDate == todayMth + " " + todayDate)
{
var targetBirthDateYearsOld = (today.getYear() - unadjTargetBday.getYear())-1900;
prepareAndSendEmail(k, targetBirthDateYearsOld);
}
}
}
getValue will return the type string/date/number depending on the cell type in the spreadsheet (see menu Format -> Number). To be sure, just always convert to Date Type. This is the right way to convert it:
var targetBday = new Date(birthdaysSheet.getRange("P" + k).getValue());
Short answer
Check that the cell value is a valid date for Google Sheet.
Explanation
From https://developers.google.com/apps-script/reference/spreadsheet/range#getvalue (emphasis mine)
getValue()
Returns the value of the top-left cell in the range. The value may be
of type Number, Boolean, Date, or String depending on the value of the
cell. Empty cells will return an empty string.
To avoid this kind of problems, you may include some sort of data validation. You could use build-in features like conditional formatting, data validation, or something like a on edit script together with try / catch or a redundant validation in your emailAlert script.