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();
}
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 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...
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.
I am working on pulling data from mySQL database via jdbc into a google spreadsheet everyday at 8 am. I created the following time based trigger.
function createTimeBasedTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if ( triggers[i] === 'myFunction' ) ScriptApp.deleteTrigger(triggers[i]);
};
ScriptApp.newTrigger('myFunction')
.timeBased()
.everyDays(1)
.atHour(8)
.create();
}
I also tried to create the trigger in the script editor and they either execute multiple times or do not execute at all.
A trigger is not a string, it's an object. It can't be equal to 'myFunction'. A correct comparison looks like
if (triggers[i].getHandlerFunction() === 'myFunction') {
ScriptApp.deleteTrigger(triggers[i]);
}
That said, managing triggers via script editor interface is convenient, and they run as expected, provided that the function you are calling actually works.
I automatically create a form with GAS as follows:
var form = FormApp.create(form_name);
ScriptApp.newTrigger('mysubmit')
.forForm(form)
.onFormSubmit()
.create();
The problem is that it creates a trigger every time new form is created.
Is there a way to reuse the same trigger? A problem is that a number of triggers available to my account runs out very quickly.
If you use an Apps Script to create a Form and then create a form submit trigger for that Form, the trigger you have created is attached to the script, not the new Form. This is why you are running into the 20 triggers / user / script quota limit.
Keep in mind that triggers live on scripts, not Docs, Sheets, or Forms. You can use a script to create a new Form, but you can't use a script to create a new script attached to that Form (scripts cannot create other scripts). This means you cannot programmatically create a trigger that lives on another document.
What you can do is create a Forms add-on that, when a new Form is created, a user can hit a menu control to create a form submit trigger for that Form.
My workaroung but still 20 triggers limit exists
function install_submit_trigger(form, fids)
{
var allTriggers = ScriptApp.getProjectTriggers();
for(var i = 0; i < allTriggers.length; i++)
{
ScriptApp.deleteTrigger(allTriggers[i]);
}
for(var i in fids)
{
var fid = fids[i];
try
{
ScriptApp.newTrigger('mysubmit')
.forForm(FormApp.openById(fid.fid))
.onFormSubmit().create();
}
catch(e)
{
Logger.log("Error adding trigger: " + e);
}
}
ScriptApp.newTrigger('mysubmit') .forForm(form) .onFormSubmit() .create();
}