How to add a timer to a Google Script - google-apps-script

I'm looking at a cooldown to a function I've created so that once the function has been ran and the "else' tag has been ran that it wouldn't attempt to run again for 15 minutes.
Is there a simple way of doing this. My code is below and I've inserted a add here section for where I would want it to run.
function Cancel() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
// first prompt
var presult = ui.prompt(
"Please Enter the Password!",
ui.ButtonSet.OK_CANCEL);
var password = "Money2022";
var pbutton = presult.getSelectedButton();
var ptext = presult.getResponseText();
// User clicked "OK" on first prompt
if (pbutton == ui.Button.CANCEL) {
ui.alert('Wrong Answer Buddy!');
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else if (pbutton == ui.Button.CLOSE) {
ui.alert('Wrong Answer Buddy!');
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else if (ptext != password) {
Password();
} else {
**!!INSERT FUNCTION HERE!!**
}
}
function Password() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var response = ui.alert("Wrong Answer Buddy!",
ui.ButtonSet.OK_CANCEL);
if (response == ui.Button.CANCEL) {
ui.alert("BYE BYE!");
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else if (response == ui.Button.CLOSE) {
ui.alert("BYE BYE!");
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else {
Cancel();
}
}

Seems like Cooper's suggestion of using Utilities.sleep() already worked for you but I'd like to add a little more information and another way to do this for anyone else with a similar question.
Since you mentioned that you wanted to cooldown for 15 minutes you should know that the maximum sleep time for Utilities.sleep() is 300000 milliseconds or 5 minutes. You can find this documented here.
As an alternative to bypass this limit you can consider using the Properties service. This allows you to save properties for either the users or the entire script, so you can set a property with a time x minutes from now and the script can check if it's passed this time before running. This allows you to set the cooldown as long as you want, and choose between having different cooldowns for each user or lock the entire document.
Here's an example of how you could do it:
function yourProcess(){
var properties = PropertiesService.getUserProperties() //gets all properties
var cooldownprop = properties.getProperty("cooldown") //gets cooldown property
var now = new Date()
//if cooldown has not been set it just sets the current time so the user can go through
var cooldown = cooldownprop == null ? now : new Date(cooldownprop)
if (now >= cooldown){
//run your code
//this sets the cooldown if needed, add the time in milliseconds
properties.setProperty("cooldown", new Date(now.getTime()+900000))
} else {
//tell the user they're on cooldown
}
}
For more information and samples on how to use the properties you can check out the documentation.
Sources:
Utilities.sleep()
Properties Service

Related

Google Script problem while updating events to Google Calendar (trying not to make duplicates)

Kinda new to Google Apps Script. I'm trying to create Google Calendar events from GoogleSheet. It won't be revealing that I'm using someone's answers where some answers are here from stackoverflow. Unfortunately, I have not found a complete answer to my problem. Or if there was I couldn't recreate it :)
GoogleSheet
That's the Google Sheet that I'm using (trying to make a shipping calendar where someone can track deliveries). Columns marked as 0 are not used to make calendar events, only these marked as 1. For example what should be in Calendar Event:
Title: L-H-19/22/Description: Driver 1 Mobile: 1234567890/Date:2022-05-16
Note: Date probably will be changed to start/end date with hours. (2022-05-16 06:00 - 2022-5-16 07:00). Also, I mentioned "Description" which I'm not using but I'll give it a go, while I change ".createAllDayEvent" to ".createEvent", that's why also there is commented "DATA_2".
For now, it creates an event but I cannot figure out how to update it without making duplicates.. Tried to use .deleteEventSeries() and update it, also tried to use trigger onEdit() but still without luck.
Here is code which I use to add events to calendar:
function initMenu() {
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('SPEDYCJA')
menu.addItem('DODAJ DO KALENDARZA','DodanieSpedycjiDoKalendarza')
menu.addItem('AKTUALIZUJ KALENDARZ','AktualizacjaWydarzenia')
menu.addToUi();
}
function onOpen() {
initMenu();
}
function DodanieSpedycjiDoKalendarza() {
var ui = SpreadsheetApp.getUi();
var WcisnietyPrzycisk = ui.alert("Czy na pewno chcesz uruchomić skrypt??",ui.ButtonSet.YES_NO);
if (WcisnietyPrzycisk == ui.Button.YES) {
let AktywnyArkusz = SpreadsheetApp.getActiveSheet();
let KalendarzSpedycja = CalendarApp.getCalendarById("cbvdt6iek0qbgujgbhq68et950#group.calendar.google.com");
let TabelaDanych = AktywnyArkusz.getRange(12,8,8,9).getValues();
for (let x=0; x<TabelaDanych.length; x++) {
const ZMIANA = TabelaDanych[x];
const TYTUŁ = ZMIANA[0];
const DATA_1 = ZMIANA[7];
//const DATA_2 = ZMIANA[14];
const ID_Wydarzenia = ZMIANA[8];
if (ID_Wydarzenia == "") {
const NoweWydarzenie = KalendarzSpedycja.createAllDayEvent(TYTUŁ,DATA_1);
const ID_NoweWydarzenie = NoweWydarzenie.getId();
AktywnyArkusz.getRange(8+x,8).setValue(ID_NoweWydarzenie);
}
try {
var event = KalendarzSpedycja.getEventSeriesById(ID_Wydarzenia);
event.deleteEventSeries();
//entry[9] = '';
} catch(e) {
//nie rób nic
}
var newEvent = KalendarzSpedycja.createAllDayEvent(TYTUŁ,DATA_1);
// entry[9] = newEvent;
debugger;
}
ui.alert("Dodano spedycje do kalendarza!")
} else if (WcisnietyPrzycisk == ui.Button.NO) {
ui.alert("Nie uruchomiłeś skryptu.");
}
}
Also, sample code where I tried to use trigger onEdit() while then in the main function I did not used "try { .deleteEventSeries() }", there was only loop for but still failed..
function AktualizacjaWydarzenia(e) {
var ZaktualizowaneWiersze = e.range.getRange();
var ZaktualizowaneDane = e.source.getActiveSheet().getRange(ZaktualizowaneWiersze, 8, 8, 9).getValues()[4];
var ID_ZaktualizowaneWydarzenie = ZaktualizowaneDane[9]
try {
var Wydarzenie = CalendarApp.getEventById(ID_ZaktualizowaneWydarzenie);
ID_ZaktualizowaneWydarzenie.setTitle(ZaktualizowaneDane[1]);
ID_ZaktualizowaneWydarzenie.setDate(ZaktualizowaneDane[8]);
} catch(err) {
console.log("Wystąpił błąd podczas aktualizowania wydarzeń do kalendarza. Konkretne wydarzenie może jeszcze nie istnieć.");
}
}
I will be very grateful where someone will paste answer with code and point me/critique in existing one what I did wrong.
Thank you, have a nice day.
To prevent duplicates,
you need to save in one column the event id when you create that event. Then, if you want to update, you have to take account of this id.
reference
setTime(startTime, endTime)
try
function AktualizacjaWydarzenia(ID_Wydarzenia,TYTUŁ,DATA_1,DATA_2) {
var Wydarzenie = CalendarApp.getEventById(ID_ZaktualizowaneWydarzenie);
Wydarzenie.getEventById(ID_Wydarzenia).setTitle(TYTUŁ)
Wydarzenie.getEventById(ID_Wydarzenia).setTime(DATA_1,DATA_2)
}

Google Apps Script - Variable Passed Between Functions Shows Up as Undefined

I have some code where there are some if/else if statements to determine the reason that a Google Form has closed. I want to pass this reason to another function which will send an email notifying me that the form has closed and the why. I don't know why the reason, which should be a string, is not getting passed to the function that sends the email.
I'm not dealing with the form at all in this code. For simplicity, I opened a new standalone script, not linked to any Google Form or Sheet, to test just this portion (determine reason and send email). Here is the code:
First function to determine reason the form closed and to call second function to send email:
function FindReason() {
var Day1Seats = 5;
var Day2Seats = 5;
var Day1Responses = 6;
var Day2Responses = 1;
var FormClose = false;
if(Day1Responses >= Day1Seats) {
var reason = "All seats taken for Day 1";
SendAnEmail(reason);
}
else if(Day2Responses >= Day2Seats) {
var reason = "All seats taken for Day 2";
SendAnEmail(reason);
}
else if(FormClose) {
var reason = "Form close date";
SendAnEmail(reason);
}
}
Second function, which sends email (email address obviously changed for privacy):
function SendAnEmail(closereason) {
var recipientTO = "name#example.com";
var subject = "This is the subject";
//var closereason = "All seats taken for Day 1";
MailApp.sendEmail({
to: recipientTO,
subject: subject,
htmlBody: (new Date()) + "<br>Your form has been closed because: <br>" + closereason + "<br>End of message."
});
}
When I run FindReason(), I do get an email but the body doesn't properly show the reason. This is an example of the emails I get:
Sun Feb 24 2019 12:26:01 GMT-0800 (PST)
Your form has been closed because:
undefined
End of message.
I don't know why the reason is shown as undefined. I tried setting the reason inside the SendAnEmail function (the line that has been commented out) and that worked properly, but I need to be able to change the reason.
Edit: It works now. It turns out the "run" button was set to run the "SendAnEmail" function rather than the "FindReason" function. Thanks to everyone who offered suggestions.
What's probably happening is that else if(FormClose = TRUE) is not going to work.
= is the assignment operator, you want equality, which is ===.
TRUE is lowercase in js, so you want true.
You also use False, which should be false.
If you look in the google scripts editor's console, you should see an error of some sort, because it won't know what False or TRUE are (it will think they are undeclared variables).
try else if(FormClose === true) or even more simply, else if (FormClose), because if it evaluates to true, that will do the job.
function FindReason() {
var Day1Seats = 5;
var Day2Seats = 5;
var Day1Responses = 6;
var Day2Responses = 1;
var FormClose = false;
if(Day1Responses >= Day1Seats) {
var reason = "All seats taken for Day 1";
SendAnEmail(reason);
} else if(Day2Responses >= Day2Seats) {
var reason = "All seats taken for Day 2";
SendAnEmail(reason);
} else if(FormClose) {
var reason = "Form close date";
SendAnEmail(reason);
}
}

Password Protect Google Apps Script

so I have created a small script here for my google sheets. Since google sheets doesn't allow you to use password protection on individual sheets, I was wondering if there was a way to protect my script with a password so that only certain people can use it. Here is my code.
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Custom Menu')
.addItem('Record', 'Record')
.addItem('Cancelation', 'Cancel')
.addToUi();
}
function Record() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Nightly Stats'),
row = sheet.getLastRow()
range = sheet.getRange("A3:G3");
sheet.insertRowAfter(row);
range.copyTo(sheet.getRange(row + 1, 1), {contentsOnly:true});
}
I would greatly appreciate any suggestions that you can provide.
Ok so I actually figured out how to do a password system via prompting. Here was what I did in case anyone needs this in the future.
function Cancel() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
// first prompt
var presult = ui.prompt(
"Please Enter the Password to Use this Feature.",
ui.ButtonSet.OK_CANCEL);
var password = "Test";
var pbutton = presult.getSelectedButton();
var ptext = presult.getResponseText();
// User clicked "OK" on first prompt
if (pbutton == ui.Button.CANCEL) {
ui.alert('The Process Was Ended.');
} else if (pbutton == ui.Button.CLOSE) {
ui.alert('The Process Was Ended.');
} else if (ptext != password) {
Password();
} else {
"Insert whatever action you would want them to do after the password works here"
}
}
function Password() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var response = ui.alert("The Password is Incorrect. Retry?",
ui.ButtonSet.OK_CANCEL);
if (response == ui.Button.CANCEL) {
ui.alert("The Process Was Ended.");
} else if (response == ui.Button.CLOSE) {
ui.alert("The Process Was ended.");
} else {
Cancel();
}
}
I only gave a piece of the code so sorry if it looks a little weird. I just didn't want to give the whole code and make you search for everything. Hope that helps :)
It is a trick but it's useful to free account user!
Use onOpen() function and little code below.
var inputPassword;
function checkPassword(){
var refAddress = 'https://script.google.com/d/' + ScriptApp.getScriptId() + '/edit';
var curAddress = refAddress;
Logger.log(curAddress);
var referPassword = 1234;
if( refAddress == curAddress){
while(referPassword != inputPassword){
inputPassword = Browser.inputBox('[Password Check]', '[Input Password!]', Browser.Buttons.OK_CANCEL);
}
}
}
Refer to my blog for more information.
So you're trying to protect the script itself?
Host the script in a different location and use a library:
https://developers.google.com/apps-script/guides/libraries

GAS - Button in script gadget runs target script, then disappears

I'm trying to create a time-tracking tool on Google Sites for my coworkers to log their hours, with the following needs:
- 3 buttons: One to log a timestamp, one to tally up logs, and one to view a logsheet.
- all users can see button 1, only specific email addresses can see boxes 2 & 3.
- When button 1 is pressed, logging script is run and gadget returns to button, so user can press again.
I have created the below Google Appscript to draw the buttons in a Gadget IEFrame, run the timestamp script on click, and ideally return the user's attention back to the button.
My problem is that, after clicking the button in the appscript gadget, the script runs fine, but the gadget area turns blank. I have to refresh the page to get the button back. I've tried ending the script by reloading the HTML document, but the OutputToHTMLFile command doesn't seem to work the last time I run it, even though it runs ok when the page is first loaded. Is there a way to run the script without the buttons disappearing?
Here is how the issue looks on my test site:
Image of button Disappearance
SendtoLog Script:
function doGet() {
var e = Session.getActiveUser().getEmail();
if( /*e == "user1#email.com" ||*/
e == "user2#email.com" ||
e == "user3#email.com")
{
return HtmlService.createHtmlOutputFromFile('adminButtons');
}
else
{
return HtmlService.createHtmlOutputFromFile('callerButtons');
}
}
function sendToLog(e) {
var email = Session.getActiveUser().getEmail(); // Get current user
var now = new Date(); //Pull login time for user
var nowMinute
var currentDate = now;
//open spreadsheet for tracking and open the sheet for the current user
var folderID = openFolderByName("TimeTrack");
var folderToWrite = DriveApp.getFolderById(folderID);
var logSheetID = openFileByName ("Logsheet");
var logSheet = SpreadsheetApp.openById(logSheetID);
try{logSheet.setActiveSheet(logSheet.getSheetByName("Sheet1"));
logSheet.deleteActiveSheet();}
catch (e){Logger.log("Sheet1 is already deleted")};
try{logSheet.setActiveSheet(logSheet.getSheetByName(email));}
catch (e) {logSheet.insertSheet(email);}
try{logSheet.setActiveSheet(logSheet.getSheetByName("Sheet1"));
logSheet.deleteActiveSheet();}
catch (e){Logger.log("Sheet1 is already deleted")};
//get last row of the sheet so we can check what was done on the last click
var lastRow = logSheet.getLastRow();
if (lastRow < 1) // If there is no data on the sheet, this will be our first logon
var logType = "logon"; //We will assume that current logon event is a logon unless noted otherwise on spreadsheet.
else
{
var dataRange = "A1:B"+ lastRow;
var dataWithoutHeaders = logSheet.getRange(dataRange).getValues() // Pull data from the sheet for comparison
var lastLogType = logSheet.getRange("C"+lastRow).getDisplayValue(); // Pull the description of the last log event
//if last row is login, add logoff, otherwise, add logon.
switch(lastLogType){
case "logon":
var logType = "logoff";
break;
case "logoff":
var logType = "logon";
break;
default:
var logType = "Logon"; //We will assume if last entry could not be compared, this is a logon event.
break;
}//End switch
}//End else statement
logSheet.appendRow([email, currentDate, logType]); //Write email, time stamp, and log event to sheet
var e = Session.getActiveUser().getEmail();
if( /*e == "user1#mail.com" ||*/
e == "user2#mail.com" ||
e == "user3#email.com")
{
return HtmlService.createHtmlOutputFromFile('adminButtons');
}
else
{
return HtmlService.createHtmlOutputFromFile('callerButtons');
}
}//end function SendtoLog
function openFileByName (FileNameString)
{
var FileIterator = DriveApp.getFilesByName(FileNameString);
while (FileIterator.hasNext())
{
var file = FileIterator.next();
if (file.getName() == FileNameString)
{
var sheet = SpreadsheetApp.open(file);
var sheetID = sheet.getId();
}
}
return sheetID;
}
function openFolderByName (FolderNameString)
{
var FolderIterator = DriveApp.getFoldersByName(FolderNameString);
var folderFound = false;
while (FolderIterator.hasNext() || !folderFound)
{
var folder = FolderIterator.next();
if (folder.getName() == FolderNameString)
{
folderFound = true;
var folderID = folder.getId();
}
}
return folderID;
}
callerButtons HTML Page:
<div style="display:block;text-align:left"><input type="button" onclick="location.href='https://script.google.com/a/macros/mycompany.com/s/AKfycbwM842S-Y6Y18sDV7p0fZlb9LqdP6juXcMz1QFa4s6-h4w/exec';" value="Time Check-In" />
adminButtons HTML Page:
<div style="display:block;text-align:left"><input type="button" onclick="location.href='https://script.google.com/a/macros/mycompany.com/s/AKfycbwM842S-Y6Y18sDV7p0fZlb9LqdP6jueL7bXcMz1QFa4s6-h4w/exec';" value="Time Check-In" />
<br>
<div style="display:block;text-align:left"><input type="button" onclick="location.href='https://script.google.com/a/macros/mycompany.com/s/AKfycbwM842S-Y67p0fZlb9LqdP6jueL7bXcMz1QFa4s6-h4w/exec';" value="Tabulate Times" />
<br>
<div style="display:block;text-align:left"><input type="button" onclick="location.href='https://script.google.com/a/macros/mycompany.com/s/AKfycbwM842S-Y6Y18sDV7p0fZlb9LqdP6jueL7bXcMz1QFa44w/exec';" value="CSV Folder" />
OK, so this was a question from someone who didn't even know how to open a developer's console to troubleshoot. For others wondering how to check Javascript code in Chrome, press CTRL+SHIFT+I.
It turns out one of my variables was a broken reference in one of the onclick functions. All fixed!

copy paste google spreadsheet

So, I made a Google Spreadsheet for a group of people to use to keep track of weekly "counts" for a large group of people on a Reddit sub. The things I'm trying to automate are two things. The one I'm having problems with is the one I thought would be the easiest, just copying the values from one set (G2:G200) to overwrite the values in another (E2:E200). I'm having some other issues as well, but I'd be more interested in an explanation for what I'm doing wrong there than just an answer. The biggest one is that this is supposed to be making a custom menu on the sheet, and I can't seem to get that working, even though I basically copied the script from the Google Tutorial for that. I've tried the script for this two ways, one using the same script as Excel printed out when recording the same basic thing:
function UpdateLore_() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'Only do this once per week, at end of updates.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
Range("G2:G200").Select;
Selection.Copy;
Range("E2:E200").Select;
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False;
} else {
// User clicked "No" or X in the title bar.
ui.alert('No Changes Made.');
}
}
This returns an arror on the "Selection.PasteSpecial" line. The other way I tried it was using what I could find online for this:
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValue = inputRange.getValue();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200");
}
The top part of the code looks like this:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Weekly Update')
.addItem('Update for Lore', 'UpdateLore')
.addItem('Update for XP Master', 'UpdateMaster')
}
I feel like I'm missing something very obvious, especially with the whole "doesn't seem to change the sheet in anyway" part. Thanks for any help
Got some answers and now it works, thanks for all the help:
Got it, thanks for all the help. New code looks like this:
function UpdateLore() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'Only do this once per week, at end of updates.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
copyFunction ();
}
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValues = inputRange.getValues();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200").setValues(inputValues);
}
if (result ==ui.Button.NO) {
// User clicked "No" or X in the title bar.
ui.alert('No Changes Made.');
}
}
To add data to a sheet you need to use:
setValue()
setValues()
appendRow()
You've got a function inside of the if body:
if (result == ui.Button.YES) {
// User clicked "Yes".
function copyFunction () {
. . . .
}
}
If you want to call another function at that point, you could use:
if (result == ui.Button.YES) {
// User clicked "Yes".
copyFunction ();
};
function copyFunction () {
. . .
};
You need to set the values from the inputRange to the outputRange. Use the .setValues() on your outputRange to do this.
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValues = inputRange.getValues();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200").setValues(inputValues);
}
None of this is valid apps script code:
Range("G2:G200").Select;
Selection.Copy;
Range("E2:E200").Select;
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False;