I have this trigger that I want to run every morning at 9am.
function copyFormulasToValues() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[1];
var destination = ss.getSheets()[2];
var range = source.getRange("A1:C6015");
range.copyValuesToRange(destination, 1, 3, 1, 6030);
ScriptApp.newTrigger("myFunction")
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create();
The issue is, after a few days, my triggers fill up and it can't run anymore without me manually clearing them. I know there's a way to build in a function that essentially replaces the old trigger (by way of deletion) with the new trigger each day.
This post here gives an example of how to do it, but I just can't seem to get it to work. Would anybody be able to help me write this so that it deletes the existing trigger and replaces it with the new one each day? I'm a beginner so any help is much appreciated!!
Both of these methods will create a single trigger and replace a single trigger. If you need to create and replace multiple triggers, the code would be slightly different.
Method 1
Creates a new trigger, and replaces any existing triggers that are using the same event handler ("myFunction" in your example). This assumes that you won't change the name of your handler function between executions and that you don't have multiple triggers running with the same handler. It is the weaker of the two methods for those reasons. However, it is slightly easier to understand since it isn't using any services that aren't already in your code sample.
// this is the function that gets called by your trigger
function myFunction() {
Logger.log("I am an event handler");
}
function replaceTrigger(handlerName) {
const currentTriggers = ScriptApp.getProjectTriggers(); // get the projects triggers
const existingTrigger = currentTriggers.filter(trigger => trigger.getHandlerFunction() === handlerName)[0]
if (existingTrigger) ScriptApp.deleteTrigger(existingTrigger) // delete the existing trigger that uses the same event handler
// create a new trigger
if (existingTrigger[0])
ScriptApp.newTrigger(handlerName)
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create();
}
replaceTrigger('myFunction')
Method 2
Store a trigger ID when you create a trigger, then use that ID to delete it. The benefit of using this method is that it will allow you to change the name of your handler function, and it won't have side effects on other triggers in your project. It uses the PropertiesService, which you may be unfamiliar with.
function myFunction() {
Logger.log("I am an event handler");
}
function replaceTriggerWithId(handlerName) {
// read the existing trigger id from script properties and delete it if it exists
const currentTriggers = ScriptApp.getProjectTriggers(); // get the projects triggers
const existingTriggerId = PropertiesService.getScriptProperties().getProperty('triggerId');
if (existingTriggerId) {
const existingTrigger = ScriptApp.getProjectTriggers().filter(trigger => trigger.getUniqueId() === existingTriggerId)[0];
if (existingTrigger) ScriptApp.deleteTrigger(existingTrigger)
}
// create a new trigger and store its id in script properties
const triggerId =
ScriptApp.newTrigger(handlerName)
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create()
.getUniqueId()
PropertiesService.getScriptProperties().setProperty('triggerId', triggerId);
}
replaceTriggerWithId('myFunction');
You could set replaceTrigger or replaceTriggerWithId to run on a time trigger. When they are triggered they will delete any triggers that were defined with the same handler name (method 1), or whichever trigger was last defined by the trigger handler (method 2). After deleting the old trigger, they will create a new one.
References:
ScriptApp
PropertiesService
#RayGun 's answer method 2 seems Error , so I fixed up a little bit:
function myFunction() {
Logger.log("I am an event handler");
}
function replaceTrigger(handlerName) {
const currentTriggers = ScriptApp.getProjectTriggers(); // get the projects triggers
const existingTrigger = currentTriggers.filter(trigger => trigger.getHandlerFunction() === handlerName)[0]
if (existingTrigger) ScriptApp.deleteTrigger(existingTrigger) // delete the existing trigger that uses the same event handler
// create a new trigger
//if (existingTrigger[0]) Yes, I commented this
ScriptApp.newTrigger(handlerName)
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create()
.getUniqueId()
console.log('Create New Triger:...');
}
function main(){
replaceTrigger('myFunction')
}
Don't forget: you must Run 'main' function
, and then 'myFunction' will always be created only once, forever...
Related
How can I run my time based trigger after a certain amount of time. Let's say I would like to start the trigger right now and stop after two weeks. How can I implement that to the installable trigger? Is it even possible? I'd like to know.
ScriptApp.newTrigger("myFunction")
.timeBased()
.everyMinutes(10)
.create();
The easiest method would be to have a function for deleting the trigger and storing the trigger ID in the User Properties.
So you could do something along the lines of this:
function createTrigger() {
var trigObj = ScriptApp.newTrigger("test")
.timeBased()
.everyDays(7)
.create();
var props = PropertiesService.getUserProperties();
var endDate = new Date;
endDate.setDate(endDate.getDate() + 14);
props.setProperty('trigId', trigObj.getUniqueId());
props.setProperty('trigEnd', endDate);
}
function remTrig(){
var props = PropertiesService.getUserProperties().getProperties();
var now = new Date;
var trigs = ScriptApp.getProjectTriggers();
if (now.getDate() == new Date(props['trigEnd']).getDate()){
Logger.log('Trigger will be deleted');
for (var i = 0; i < trigs.length; i++) {
if (trigs[i].getUniqueId() == props['trigId']){
Logger.log(`Deleting ${trigs[i].getUniqueId()}`);
ScriptApp.deleteTrigger(trigs[i]);
}
}
}
else
Logger.log(`Trigger to be deleted on ${props['trigEnd']}`);
}
Obviously the trigger check and deletion would have to happen somewhere inside of the function your trigger is calling or you would need a separate daily trigger for the remTrig() function checking if it's time to delete it.
A few considerations to keep in mind is that this sample is meant for a single trigger per user if you use the first method. For a daily check if it's time to remove the trigger you'd need to have the function that creates the trigger to create a second one for the deletion check.
I have a script which uses googlesheets, I am new to this also, there the trigger onOpen() and when the sheet is opened it runs whats inside it, however I wanted to run a function even when sheet is not opened. Do I need a trigger to do this? What should I use to do this?
I tried:
OnCreate
Installable
OnEdit
I want to create a time-based trigger will the script run, even if the sheet is not open and/or even if I'm not logged in to Sheets.
I tried:
function onOpen() {
// Trigger every 10 minutes.
ScriptApp.newTrigger('myFunc') //Run Update All Sheets
.timeBased()
.everyMinutes(10)
.create();
}
and outside the script. I wonder which of these works on background.
// Trigger every 10 minutes.
ScriptApp.newTrigger('myFunc') //Run Update All Sheets
.timeBased()
.everyMinutes(10)
.create();
function onOpen() {
}
It is possible to add a trigger programmatically.
https://developers.google.com/apps-script/guides/triggers/installable
Managing triggers programmatically
You can also create and delete triggers programmatically with the Script service. Start by calling ScriptApp.newTrigger(functionName), which returns a TriggerBuilder.
The following example shows how to create two time-driven triggers—one that fires every 6 hours, and one that fires every Monday at 9 a.m. (in the time zone that your script is set to).
triggers/triggers.gsView on GitHub
/**
* Creates two time-driven triggers.
*/
function createTimeDrivenTriggers() {
// Trigger every 6 hours.
ScriptApp.newTrigger('myFunction')
.timeBased()
.everyHours(6)
.create();
// Trigger every Monday at 09:00.
ScriptApp.newTrigger('myFunction')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
This next example shows how to create an installable open trigger for a spreadsheet. Note that, unlike for a simple onOpen() trigger, the script for the installable trigger does not need to be bound to the spreadsheet. To create this trigger from a standalone script, simply replace SpreadsheetApp.getActive() with a call to SpreadsheetApp.openById(id).
triggers/triggers.gsView on GitHub
/**
* Creates a trigger for when a spreadsheet opens.
*/
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ss)
.onOpen()
.create();
}
To programmatically modify an existing installable trigger, you must delete it and create a new one. If you have previously stored the ID of a trigger, you can delete it by passing the ID as an argument to the function below.
triggers/triggers.gsView on GitHub
/**
* Deletes a trigger.
* #param {string} triggerId The Trigger ID.
*/
function deleteTrigger(triggerId) {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < allTriggers.length; i++) {
// If the current trigger is the correct one, delete it.
if (allTriggers[i].getUniqueId() === triggerId) {
ScriptApp.deleteTrigger(allTriggers[i]);
break;
}
}
}
Open your google sheet
Click Tools
Click Script Editor
Make a function. Example Birth day greeting every day is shown below
function hbd_mail() { //birth day mail daily 7 am from hrd login
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Emp");
var today=new Date();
lastRow = sheet.getLastRow();
var values = sheet.getRange('A:T').getValues();
for (i=1;i<lastRow;i++){
if (values[i][14]=="") {} else {
if ( Utilities.formatDate( values[i][14] ,"GMT+05:30", "dd-MMM") == Utilities.formatDate( today ,"GMT+05:30", "dd-MMM") && values[i][18]=="Yes" ) {
var message = 'Dear '+ values[i][1]+',\n\nMegawin Collective wishes you a very happy birth day ('+ Utilities.formatDate( values[i][14] ,"GMT+05:30", "dd-MMM")+ ').\n\nHRD Megawin Switchgear Pvt Ltd';
var subject = "Happy Birth Day "+values[i][1];
if (values[i][7]=="") {} else {GmailApp.sendEmail(values[i][7], subject, message);}
}//if
}
}//for
}
Save the script
Click the Clock in the menu
It will ask you the function name - select the above function
Then select time based
Clcik every hour / day / week etc
Save it.
Now the function will run automatically from google server (at the frequency selected by you) without your having to do anything.
It is pretty simple.
When I first started coding in GAS I was using time based triggers and then deleting them when I started the next iteration (to get around time limits). I have since seen someone use a trigger that fires just once so I don't have to worry about deleting it, but I can't seem to find that example.
Old way:
//- This function deletes the running triggers from the repository creation process. it does NOT delete the form or
//- timed run triggers
function deleteTrigger() {
Logger.log("Deleting Triggers?");
// Loop over all triggers and delete them
var allTriggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < allTriggers.length; i++) {
var id = allTriggers[i].getHandlerFunction();
Logger.log(id);
if (id == "callCopy"){ScriptApp.deleteTrigger(allTriggers[i]); Logger.log("I deleted the copy files trigger");}
}
}
//This is the trigger to restart the call copy script in the right place.
function createTrigger() {
// Trigger every 1 minute
Logger.log("Creating File Copy Trigger");
ScriptApp.newTrigger('callCopy')
.timeBased()
.everyMinutes(1)
.create();
}
I have created a script to run as a time based trigger.
I want to disable that trigger during weekends off and holidays.
I ideally want this to be part of a function I already have that
deletes previous form submissions meaning that the user only has one button to press (deleting previous submissions AND disabling the time trigger) at the start of each holiday.
It would then be brilliant if the
trigger was re-enabled the next time a pupil submitted the form
(indicating holidays were over).
Not sure how to use script to disable / enable triggers in this way - grateful for any advice!
//Menu allowing user to choose to delete previous form submissions
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Clear Out Responses')
.addItem('Clear Form & Responses', 'showAlert')
.addToUi();
}
//Alert box deleting previous form submissions on yes, doing nothing if not
function showAlert() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'This will clear all previously registered data',
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
var ui = SpreadsheetApp.getUi();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lastRow = source.getLastRow()
source.deleteRows(2, lastRow)}
var form, urlForm = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
if (urlForm) {}
form = FormApp.openByUrl(urlForm);
if (form) form.deleteAllResponses();
}
//Trigger running record-keeping function each night
function dailyTrigger() {
ScriptApp.newTrigger('dailyRecord')
.timeBased()
.atHour(22)
.everyDays(1)
.create();
}
//Record-keeping function, copy/value-pasting record into next clear column
function dailyRecord() {
var ss = SpreadsheetApp.getActive()
.getSheetByName('History'),
lastColumn = ss.getLastColumn(),
colE = ss.getRange("C:C")
.getValues();
ss.getRange(1, lastColumn + 1, colE.length, 1)
.setValues(colE);
}
Have you looked at getUserTriggers() and deleteTrigger ?
With getUserTriggers() you can identify whether the trigger to call dailyRecord is already in existence. It returns a list of triggers, and you can check the function being called by each trigger by using getHandlerFunction().
With deleteTrigger() you can delete a given trigger. e.g. if your trigger to call dailyRecord is installed (and returned in the list from getUserTriggers(), you can then delete it using this function.
To enable the trigger again, having checked that it doesn't already exist, you can use the trigger builder as per the code you already have, to recreate the trigger.
Armed with these methods, you should be able to achieve the effect you are after.
An existing trigger can not be retrieved and get re-defined. For example, a time based trigger that has already run, can not be retrieved and given a new time to run using code.
I want my stand-alone web app script to run everyday between 14:00 and 15:00 GMT +4:30 (Asia/Tehran). It should open a spreadsheet and check a certain cell.
So far I have the following:
function addTrigger() {
var ssid = "My SpreadSheet ID";
var ss = SpreadsheetApp.openById(ssid);
var everyDay = ScriptApp.newTrigger("checkExitTime")
At this stage, both the forSpreadSheet(String key) and timeBased() show up in the list of available methods. But when I chain one, the other becomes unavailable. So if I use the forSpreadSheet method, the timeBased() methods vanish and the only methods that show up are create() onChange(), onEdit(), onOpen(), and on onFormSubmit(). How can I have both?
I would like to have something like:
var everyDay = ScriptApp.newTrigger("checkExitTime")
.timeBased().everyDays(1).atHour(14).inTimezone("Asia/Tehran")
.forSpreadSheet(ss)
.create()
ScriptApp.newTrigger returns a TriggerBuilder class which further classifies triggers into different type of triggers. So when you will call ScriptApp.newTrigger("yourFn").timeBaesd() will return you a ClockTriggerBuilder class whose methods will return you the same ClockTriggerBuilder class for method chaining, but you will remain in that class.
To access another type of trigger, in your case .forSpreadSheet(ss) will return you SpreadsheetTriggerBuilder class whose builder methods will return the same class for method chaining.
In your code:
var everyDay = ScriptApp.newTrigger("checkExitTime")
.timeBased().everyDays(1).atHour(14).inTimezone("Asia/Tehran")
.forSpreadSheet(ss)
.create();
Using .forSpreadSheet is unnecessary and does not make any sense since you cannot chain the two methods as they are not available in the object returned.
I reckon what you wanted to do is to manipulate the spreadsheet when time based trigger runs; so you should create a time driven trigger and do your task in its handler function:
var everyDay = ScriptApp.newTrigger("checkExitTime")
.timeBased().everyDays(1).atHour(14).inTimezone("Asia/Tehran")
.create();
//// Handler function of time driven trigger
function checkExitTime(e){
var ssid = "My SpreadSheet ID";
var ss = SpreadsheetApp.openById(ssid);
//// do your task
}
However if you also want spreadsheet triggers like onOpen(), onEdit() etc, you have to initiate them separately like "this".
// Creates an edit trigger for a spreadsheet identified by ID.
var ssid = "My SpreadSheet ID";
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ssid)
.onEdit()
.create();
// your onEdit event handler
function myFunction(event){
// your code..
}
will execute myFunction when edit event will occur on your spreadsheet.