How can I pass a parameter to a time-based Google App Script trigger?
Asked Answered
B

6

21

In my script, I am reading data from a Spreadsheet and creating a time based trigger to make a POST request with some of that data at a specific time.

The problem is, I can't find any way to pass the data to the function that is called by the trigger. All that the Google App Script doc offers is the ability to name the function to call, but no way to pass it parameters.

 var triggerDay = new Date(2012, 11, 1);
 ScriptApp.newTrigger("makePostRequest")
   .timeBased()
   .at(triggerDay)
   .create();

Does anyone know how I can pass makePostRequest parameters so the function will execute with the needed data?

Breakwater answered 21/9, 2015 at 14:22 Comment(0)
F
27

This is possible but requires multiple steps. The most important thing here are event objects (mentioned by @St3ph).

var RECURRING_KEY = "recurring";
var ARGUMENTS_KEY = "arguments";

/**
 * Sets up the arguments for the given trigger.
 *
 * @param {Trigger} trigger - The trigger for which the arguments are set up
 * @param {*} functionArguments - The arguments which should be stored for the function call
 * @param {boolean} recurring - Whether the trigger is recurring; if not the 
 *   arguments and the trigger are removed once it called the function
 */
function setupTriggerArguments(trigger, functionArguments, recurring) {
  var triggerUid = trigger.getUniqueId();
  var triggerData = {};
  triggerData[RECURRING_KEY] = recurring;
  triggerData[ARGUMENTS_KEY] = functionArguments;

  PropertiesService.getScriptProperties().setProperty(triggerUid, JSON.stringify(triggerData));
}

/**
 * Function which should be called when a trigger runs a function. Returns the stored arguments 
 * and deletes the properties entry and trigger if it is not recurring.
 *
 * @param {string} triggerUid - The trigger id
 * @return {*} - The arguments stored for this trigger
 */
function handleTriggered(triggerUid) {
  var scriptProperties = PropertiesService.getScriptProperties();
  var triggerData = JSON.parse(scriptProperties.getProperty(triggerUid));

  if (!triggerData[RECURRING_KEY]) {
    deleteTriggerByUid(triggerUid);
  }

  return triggerData[ARGUMENTS_KEY];
}

/**
 * Deletes trigger arguments of the trigger with the given id.
 *
 * @param {string} triggerUid - The trigger id
 */
function deleteTriggerArguments(triggerUid) {
  PropertiesService.getScriptProperties().deleteProperty(triggerUid);
}

/**
 * Deletes a trigger with the given id and its arguments.
 * When no project trigger with the id was found only an error is 
 * logged and the function continues trying to delete the arguments.
 * 
 * @param {string} triggerUid - The trigger id
 */
function deleteTriggerByUid(triggerUid) {
  if (!ScriptApp.getProjectTriggers().some(function (trigger) {
    if (trigger.getUniqueId() === triggerUid) {
      ScriptApp.deleteTrigger(trigger);
      return true;
    }

    return false;
  })) {
    console.error("Could not find trigger with id '%s'", triggerUid);
  }

  deleteTriggerArguments(triggerUid);
}

/**
 * Deletes a trigger and its arguments.
 * 
 * @param {Trigger} trigger - The trigger
 */
function deleteTrigger(trigger) {
  ScriptApp.deleteTrigger(trigger);
  deleteTriggerArguments(trigger.getUniqueId());
}

function example() {
  var trigger = ScriptApp.newTrigger("exampleTriggerFunction").timeBased()
    .after(5 * 1000)
    .create();

  setupTriggerArguments(trigger, ["a", "b", "c"], false);
}

function exampleTriggerFunction(event) {
  var functionArguments = handleTriggered(event.triggerUid);
  console.info("Function arguments: %s", functionArguments);
}

In case you are using the script properties for other values as well you might have to nest the trigger values.

Additionally you might have to use the script lock to prevent concurrent modification of the script properties.

Fiden answered 4/3, 2018 at 23:55 Comment(1)
Thank you so much! Super key, so helpful!Inductive
A
4

You can't pass a parameter when a function is launched from a trigger.

You have to store this information somewhere to allow script find it. For example with what you say I understand you have some data in a spreadsheet, you can put this information in the spreadsheet. The function will manage the way to find appropriate information depending time it is fired.

Stéphane

Apices answered 21/9, 2015 at 15:21 Comment(5)
That makes things really unnecessarily complicated. Bummer. THanks for the answer thoughBreakwater
this answer is the only way. to simplify you could use the trigger id and store in script properties as the map nameDegraded
As horrible as it sounds what you could do if there are only a few possible values for the parameter (for example: WEEK, MONTH, YEAR), is to create 3 functions fooWeek(), fooMonth() and fooYear() and have a different trigger for each one of them.Tilla
For me the problem is that I need to pass an argument to the function with the trigger so I can tell if the function was triggered automatically or not. Know any way to do that?Hirai
Hi For that when a function is fired by trigger there is an event send to the function. For example function(e) if it is run by trigger in the 'e' you will find the trigger id for example, if launch manually there is no value in 'e'. Check doc : developers.google.com/apps-script/guides/triggers/eventsApices
J
3

How about this?

function test(a,b) {
  GmailApp.sendEmail('your_mail_address',a,b);
}
// Created a new function in global scope.
var test1 = test.bind(null,'aaaaa','bbbb');

function setTrigger(){ 
  ScriptApp.newTrigger('test1').timeBased().after(1 * 60 * 1000).create();
}
Jamieson answered 17/6, 2023 at 11:51 Comment(0)
C
1

I'm not sure if it would address your specific issue, but the most convenient workaround I've found is to have a function with parameters wrapped in a function with no parameters, and then get the parameters from a static variable that you set in the top level of your script.

You still have to set the values within the script, but at least you can keep the logic separate so that you can use the base function with different values.

function functionAToTrigger(){
  functionToTriggerWithParams(myAParams);
}
var myAParams = {
  url: 'https://aurl.com',
  date: new Date(2012, 11, 1)
};
function functionBToTrigger(){
  functionToTriggerWithParams(myBParams);
}
var myBParams = {
  url: 'https://burl.com',
  date: new Date(2017, 11, 1)
};
function functionToTriggerWithParams(myParams){
   // Add some code to run some checks
   // Add some code here to log the results
}
ScriptApp.newTrigger(functionAToTrigger).timeBased().everyMinutes(10).create();
ScriptApp.newTrigger(functionBToTrigger).timeBased().everyMinutes(10).create();
Charqui answered 27/2, 2017 at 16:9 Comment(0)
A
0

triggerDay is a Trigger, that has uniqueid . first argument of makePostRequest is Time-driven event, that has undocumented property "triggerUid" , So as said @St3ph you need to store pair of you "uniqueid" and "parameters" somehow and get it from storage by "triggerUid"

Athelstan answered 31/5, 2017 at 12:15 Comment(0)
V
0

The solution from Osteria is much more elegant than the other ones, but I couldn't make it work just by having a variable creating the bind. It only worked if my trigger calls a function that creates the bind, like this:

function scheduler(){
  ScriptApp.newTrigger('run').timeBased().everyMinutes(1).create()
}

function run(){
  myFunction.bind(null, SOME_VALUE)()
}

It's important to add () after the bind to the function is actually called.

Vanadium answered 6/12, 2023 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.