Set Script Properties Instant or After Execution? - google-apps-script

I have a script that needs to execute at the top of the hour every day.
I set up automatic time triggers that execute a checking script every 10 minutes to see if it's 10 minutes before the next hour. The script sends out text and email to our list and it works (although it takes 5-20 minutes to execute).
The problem is that people are receiving double text and emails.
I suspect the reason is that the checker script sets a script property, but it doesn't actually get set until the completion of the script (similar to writing cells). If the script takes longer than 10 minutes to execute, the script will start executing again without the new property and cause it to run double. My question is: "does the setProperty() happen instantly or does it happen at the end of the script"?
If it happens instantly, then I need to find another reason the script seems to exectute each one twice. Here's my checker script for reference. Note that when I execute function sendComs(time) manually, without the time triggers, it only executes once. So it seems the propblem is something below.
function sendDaily () {
var today = new Date();
var hours = today.getHours();
var mins = today.getMinutes();
var scriptProperties = PropertiesService.getScriptProperties();
var timeLastTriggered = scriptProperties.getProperty('LastTime');
switch (timeLastTriggered) {
case '0':
if (hours >= 4 && mins >= 50 || hours >= 5) {
scriptProperties.setProperties({'LastTime':'5'})
sendComs('5:00 AM');
}
break;
case '5':
if (hours >= 5 && mins >= 50 || hours >= 6) {
scriptProperties.setProperties({'LastTime':'6'});
sendComs('6:00 AM');
}
break;
case '6':
if (hours >= 6 && mins >= 50 || hours >= 7) {
scriptProperties.setProperties({'LastTime':'7'});
sendComs('7:00 AM');
}
break;
case '7':
if (hours >= 7 && mins >= 30 || hours >= 8) {
scriptProperties.setProperties({'LastTime':'7.5'});
sendComs('7:30 AM');
}
break;
case '7.5':
if (hours >= 7 && mins >= 30 || hours >= 8) {
scriptProperties.setProperties({'LastTime':'7.6'});
sendComs('7:40 AM');
}
break;
case '7.6':
if (hours >= 7 && mins >= 30 || hours >= 8) {
scriptProperties.setProperties({'LastTime':'7.7'});
sendComs('7:50 AM');
}
break;
case '7.7':
if (hours >= 7 && mins >= 30 || hours >= 8) {
scriptProperties.setProperties({'LastTime':'7.8'});
sendComs('7:55 AM');
}
break;
case '7.8':
if (hours >= 7 && mins >= 30 || hours >= 8) {
scriptProperties.setProperties({'LastTime':'8'});
sendComs('8:00 AM');
}
break;
case '8':
if (hours >= 8 && mins >= 50 || hours >= 9) {
scriptProperties.setProperties({'LastTime':'9'});
sendComs('9:00 AM');
}
break;
case '9':
if (hours >= 11 && mins >= 50 || hours >= 12) {
scriptProperties.setProperties({'LastTime':'12'});
sendComs('12:00 PM');
}
break;
case '12':
if (hours >= 14 && mins >= 50 || hours >= 15) {
scriptProperties.setProperties({'LastTime':'15'});
sendComs('3:00 PM');
}
break;
case '15':
if (hours >= 17 && mins >= 50 || hours >= 18) {
scriptProperties.setProperties({'LastTime':'18'});
sendComs('6:00 PM');
}
break;
case '18':
if (hours >= 20 && mins >= 50 || hours >= 21) {
scriptProperties.setProperties({'LastTime':'21'});
sendComs('9:00 PM');
}
break;
case '21':
if (hours >= 23 && mins >= 50 || hours < 5) {
scriptProperties.setProperties({'LastTime':'0'});
sendComs('12:00 AM');
}
break;
}
}

I think this will answer the question for you. Paste it and run it.
function doesItHappenInstantly() {
var ps=PropertiesService.getScriptProperties();
ps.setProperty("testvalue",0);
for(var i=0;i<5;i++) {
var tv=ps.getProperty("testvalue");
if(tv!=i) {
SpreadsheetApp.getUi().alert("It does not happen instantly");
return;
}
ps.setProperty("testvalue",i+1);
}
SpreadsheetApp.getUi().alert("It happens instantly");
}
Warning: Don't run this with large number of iterations or you will get the error service invoked too many times for one day.

Properties are stored instantly.
Your script must have other problems that are causing the double messages.

Related

Gmail auto reply script - how to set conditional time and stop multiple replies?

I have adapted a Gmail auto reply script to send emails to people who message me on my non-working days. I would like the script to begin sending replies on a Friday from 16:30, but I'm not sure if my code is correct.
My script did not appear to be sending any replies this past Friday when tested, but was working fine on Saturday, Sunday and Monday.
function autoReply() {
var interval = 5;
var date = new Date();
var day = date.getDay();
var hour = date.getHours();
var minute = date.getMinutes();
if ([1,6,0].indexOf(day) > -1 || (day == 1 && hour < 8) || (day == 5 && hour == 16 && minute >= 30)) {
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('is:inbox after:' + timeFrom);
for (var i = 0; i < threads.length; i++) {
if (threads[i].isUnread()){
threads[i].reply("Hi," + "\n\n Thanks for your email. My working days are Tuesday to Friday, so I won't pick up your message until Tuesday morning.");
threads[i].markRead();
threads[i].star();
}
}
}
}
Additionally, is there any way to adapt my script so that it won't send the auto reply message to senders that have already received it (as with Gmail's standard auto reply feature). I have searched Stack Overflow for this solution, using terms such as 'conditional replies', but to no avail. (I appreciate I may be using the wrong terms entirely!)
Any help would be greatly appreciated!
You need to complement your if statement by the condition (day == 5 && hour >= 17)
To verify either an email has been sent to certain senders already, you can use PropertiesService. This allows you to save the senders in cache and to compare for every new email either its sender is already contained in the properties:
function autoReply() {
var interval = 5;
var date = new Date();
var day = date.getDay();
var hour = date.getHours();
var minute = date.getMinutes();
if ([1,6,0].indexOf(day) > -1 || (day == 1 && hour < 8) || (day == 5 && hour == 16 && minute >= 30)|| (day == 5 && hour >= 17)){
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('is:inbox after:' + timeFrom);
for (var i = 0; i < threads.length; i++) {
if (threads[i].isUnread()){
var sender=threads[i].getMessages()[0].getFrom();
if(PropertiesService.getScriptProperties().getKeys().length==0){
PropertiesService.getScriptProperties().setProperty('from', '');
}
var scriptProperties = PropertiesService.getScriptProperties().getProperty('fromArray');
if(scriptProperties.indexOf(sender)==-1){
threads[i].reply("Hi," + "\n\n Thanks for your email. My working days are Tuesday to Friday, so I won't pick up your message until Tuesday morning.")
threads[i].markRead();
threads[i].star();
scriptProperties=scriptProperties+sender;
PropertiesService.getScriptProperties().setProperty('from', scriptProperties);
}
}
}
}
}

How to correct this script to don't run triggers on specific dates

Im currently using this script
function shouldRunTrigger() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var fiestas = ss.getSheetByName("x");
var data = fiestas.getRange(2,1,fiestas.getLastRow()-1,4).getValues();
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var date = new Date();
var day = days[date.getDay()];
var hours = date.getHours();
var FiestaEmpieza = date
var FiestaAcaba = date
for (var row in data){
FiestaEmpieza = data[row][2] ;
}
for (var row in data){
FiestaAcaba = data[row][3] ;
}
// Don't run from Friday 6pm until Saturday 10Pm
if ((day === "Fri" && hours >= 18) || (day === "Sat" && hours <= 21)){
return false;
}
// Dont turn from specific dates from Tab "x" from 6pm until 10pm
else if (date >= FiestaEmpieza && hours >= 18 && date <= FiestaAcaba && hours <= 21){
return false;
}
else {
return true;
}
}
The purpose of the script: I have to run some triggers every 5 mins, but if the day is between Friday 6pm and Saturday 10pm the trigger doesn't run This part is working.
I need also this to don't run in specific dates (line 31 of the script)
which takes dates from my spreadsheet Tab "x" , the Start date is Column C and the End date is Column D, and the data looks like this:
The problem I believe is the line 31 of the script
else if (date >= FiestaEmpieza && hours >= 18 && date <= FiestaAcaba && hours <= 21){
Which is not detecting the Dates and Hrs correctly , I'm not sure if the && are correct.
Any help on how to solve this please ?
I think this might work. If I can have access to your spreadsheet so that I can just copy the data then I would test it.
function shouldRunTrigger() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getSheetByName("x");
var vA=sh.getRange(2,1,sh.getLastRow()-1,4).getValues();
var days=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var today=new Date();
var day=days[today.getDay()];
var hours=today.getHours();
if((day=="Fri" && hours>=18) || (day=="Sat" && hours<=22)){return false;}
for(var i=0;i<vA.length;i++){
var dt1=new Date(vA[row][2]).valueOf();
var dt2=new Date(vA[row][3]).valueOf();
if(today.valueOf()>=dt1 && hours >=18 && today.valueOf() <= dt2 && hours <= 21){
return false;
}
}
return true;
}
FiestaEmpeiza y FiestaAcaba will always be the last row because you loop through the date values and assign, but then don't perform any of your conditional checks. You need to consolidate the loops into one, and then move your if statements into the loop.
I recommend changing some of the variable names to be more descriptive and pass the trigger time to your function so that you can more easily test it.
Also, make sure that you maintain consistency with your date checks–date objects include time. The way you had it written, you were comparing dates objects, meaning that "April 1 # 4pm" is earlier than "April 1 # 4:01pm". So your condition could fail depending on the value you had defined in date. You can fix this simply by setting hours. (Be careful with time zone issues.)
function test_shouldRunTrigger() {
var triggerTime = new Date("Fri Apr 19 17:00:00 GMT+08:00 2019"); // Change the date to test
Logger.log(shouldRunTrigger(triggerTime));
}
function shouldRunTrigger(triggerTime) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("x");
var fiestas = sheet.getRange(2,1,sheet.getLastRow()-1,4).getValues();
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var triggerDay = days[triggerTime.getDay()]; // Day of the week
var triggerHour = triggerTime.getHours(); // Hour of the trigger
var triggerDate = new Date(triggerTime.setHours(0, 0, 0, 0)); // Set time to 00:00:00
Logger.log(fiestas)
for (var row in fiestas) {
var fiestaEmpieza = new Date(fiestas[row][2].setHours(0, 0, 0, 0)); // Set time to 00:00:00
var fiestaAcaba = new Date(fiestas[row][3].setHours(0, 0, 0, 0)); // Set time to 00:00:00
// Don't run from Friday 6pm until Saturday 10pm
if ((triggerDay === "Fri" && triggerHour >= 18) || (triggerDay === "Sat" && triggerHour <= 21)) {
return false;
} else if ( // Dont run from specific dates from Tab "x" from 6pm until 10pm
triggerDate >= fiestaEmpieza && // Compares dates and we know the times are all 00:00:00
triggerHour >= 18 &&
triggerDate <= fiestaAcaba && // Compares dates and we know the times are all 00:00:00
triggerHour <= 21
) {
return false;
} else {
return true;
}
}
}

How to target multiple specific days in this Google Apps Script

I have this script, it sends an email when the spreadsheet hasn't been modified by a specific time on set of days:
function checkWriting() {
const emailAddress = "your#email.com";
var now = new Date();
var day = now.getDay();
var hours = now.getHours();
//Set the days and between which hours to check
//Sun=0 Mon=1 Tue=2 Wed=3 Thu=4 Fri=5 Sat=6
if ((day >= 3) && (day <= 5) && (hours >= 18) && (hours <= 19)) {
// Get the dates
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var formatDate = sheet.getRange(lastRow, 1).getValue().replace("at", "").replace("AM", "").replace("PM","") + " GMT+0100";
var lastWrite = new Date(formatDate);
var lastWriteDate = lastWrite.getFullYear() + "-" + lastWrite.getMonth() + "-" + lastWrite.getDate();
var nowDate = now.getFullYear() + "-" + now.getMonth() + "-" + now.getDate();
if (nowDate > lastWriteDate) {
// Send an email out
MailApp.sendEmail(emailAddress, "You didn’t write today.", "Get on that.");
}
}
}
Right now I have it set to run between Wednesday and Friday.
My question is how do I target lets say Monday, Wednesday, Friday?
Would I write it so I just have multiple separate functions? For example
function checkWritingMonday() {
const emailAddress = "your#email.com";
var now = new Date();
var day = now.getDay();
var hours = now.getHours();
//Set the days and between which hours to check
//Sun=0 Mon=1 Tue=2 Wed=3 Thu=4 Fri=5 Sat=6
if ((day = 1) && (hours >= 18) && (hours <= 19)) {
I realize I can probably also just have my trigger run only on Monday, Wed, or Friday. That's what I'm going to do in the interim.
I am just looking for a way to do it in the script.
You could simply create a more complex if, like this:
//Sun=0 Mon=1 Tue=2 Wed=3 Thu=4 Fri=5 Sat=6
if ((day == 1 || day == 3 || day == 5) && (hours >= 18) && (hours <= 19)) {

Whats wrong with my if else statement

I have a $name variable containing a string, and when I test it, I never get the "Name should be between 2 and 40 characters" error even when it's less than 2 characters long or more than 40. Why?
if (strlen($name) < 2 && strlen($name) > 40) {
$nameError = 'Name should be between 2 and 40 characters';
}
How can the length be less than 2 and greater than 40? You want || ("or"), not && ("and").
if (strlen($name) < 2 || strlen($name) > 40) {
// -------------------^^

Number of working days between two dates in Google Apps Script

Could anyone please help with generating number of working days between two dates in Google Apps Script.
Thank you.
Based on the answer given here: How do I calculate number of given weekday between range using Moment JS?
It works similar in AppsScript
function workdays_between(start_date_string, end_date_string) {
var startDate = new Date(start_date_string)
var endDate = new Date(end_date_string)
// Validate input
if (endDate <= startDate) { return 0; }
// Calculate days between dates
var millisecondsPerDay = 86400 * 1000; // Day in milliseconds
startDate.setHours(0, 0, 0, 1); // Start just after midnight
endDate.setHours(23, 59, 59, 999); // End just before midnight
var diff = endDate - startDate; // Milliseconds between datetime objects
var days = Math.ceil(diff / millisecondsPerDay);
// Subtract two weekend days for every week in between
var weeks = Math.floor(days / 7);
days -= weeks * 2;
// Handle special cases
var startDay = startDate.getDay();
var endDay = endDate.getDay();
// Remove weekend not previously removed.
if (startDay - endDay > 1) { days -= 2; }
// Remove start day if span starts on Sunday but ends before Saturday
if (startDay == 0 && endDay != 6) { days--; }
// Remove end day if span ends on Saturday but starts after Sunday
if (endDay == 6 && startDay != 0) { days--; }
return days
}
function test_it() {
let start_date_string = "March 12, 2021"
let end_date_string = "October 15, 2021"
console.log(workdays_between(start_date_string, end_date_string) + " workdays between " + start_date_string + " and " + end_date_string)
}
From user KBA (https://stackoverflow.com/users/453331/kba) from response How do I calculate number of given weekday between range using Moment JS?:
var firstDate = new Date("March 1, 2015");
var secondDate = new Date("March 25, 2015");
function getWeekdaysBetweenDates(firstDate, secondDate, dayOfWeek) {
var MILISECONDS_IN_DAY = 86400000;
function getNextDayOfWeek(date, dayOfWeek) {
date.setDate(date.getDate() + (7 + dayOfWeek - date.getDay()) % 7);
return date;
}
firstDate = getNextDayOfWeek(firstDate, dayOfWeek);
if (firstDate > secondDate) {
return 0;
}
return 1 + Math.floor(((secondDate - firstDate) / MILISECONDS_IN_DAY) / 7);
}