0

I need to run a function exactly x minutes after another one was ran in Google Scripts.

I need something reliable, and my reading of How to use Utilities.sleep function suggests that it might hold up other calls of the script, especially after more than a few thousand milliseconds.

// Delay for x minutes 
Utilities.sleep(sMinutes * 60 * 1000 )

Is this going to cause issues with the DoPost being called again? Is this the best way? I can't see how to make a Trigger work effectively really.

Craig Lambie
  • 2,888
  • 2
  • 14
  • 17
  • I have to apologize for my poor English skill. Unfortunately, I cannot understand `I need to run a function exactly x minutes after another one was ran in Google Scripts.` and `Is this going to cause issues with the DoPost being called again?`. Can I ask you about the detail of your current issue and your goal? First, I would like to correctly understand your question. – Tanaike Apr 06 '23 at 13:00
  • Essentially I want a function to run say x = 60 minutes after the DoPost is called. But I can't have it affecting another call. – Craig Lambie Apr 06 '23 at 13:09
  • 1
    @CraigLambie Apps script has a 6 minute runtime limit per execution([see documentation](https://developers.google.com/apps-script/guides/services/quotas#current_limitations)), so your script will time out long before `Utilities.sleep` completes (Apps Script executes synchronously so it will block/hold-up execution for every line of code it runs). You can try using a time-based trigger but accuracy may be a concern since, as stated in the documentation, triggers are typically not precise and can be offset within -15 to +15 minutes of your desired time. – TheAddonDepot Apr 06 '23 at 13:30
  • That is the problem yes. I am not that worried about 15 minutes, so I could just add 15 minutes to the trigger, and then +-15 from that time is fine. – Craig Lambie Apr 06 '23 at 13:42
  • 2
    @CraigLambie The offset is random (it falls within a range of values from -15 to +15 minutes), so you won't have precision. Just adding or subtracting 15 minutes will not fix the issue. – TheAddonDepot Apr 06 '23 at 13:45
  • well if x was 60, then x + 15 is now()+75 Then imprecise trigger runs between 60 and 90 minutes, I am okay with that. I would like a more precise method mind. I could use Zapier or something, they have a webhook, delay, webhook method, but seems convuluted. – Craig Lambie Apr 06 '23 at 13:57
  • If precision is not a concern, then you should try working with installable triggers. Here's a link to the documentation: https://developers.google.com/apps-script/guides/triggers/installable – TheAddonDepot Apr 06 '23 at 14:01
  • Thank you for replying. Now, I noticed that the discussion has already been advanced. In this case, I would like to respect the discussion. – Tanaike Apr 06 '23 at 22:42
  • @TheAddonDepot do you want to post an answer? I think it needs a fully scripted answer. I have ended up using 2 methods, and chose the later. 1. running the trigger at an imprecise time or 2. running a time based trigger every 5 minutes that looks for a status in a field in the spreadsheet "database" I think it needs to be "answered" so if you don't, I will answer my own question, but prefer you got the credit. – Craig Lambie Apr 13 '23 at 18:18

1 Answers1

1

I appreciate the comments here that lead me to the answers below, but since no one has posted an answer, I thought I would show the next searcher of this question how I made it happen.

There are 2 ways to do this, both rely on a Google Sheet record as a session variable - time ran.

Step 1 Create a sheet with information and 2 columns, for me in Col E "Off?" and Col F "Time" optionally third and fourth columns: "Time turned off" and "Trigger ID" respectively

The first way is to create a trigger that runs at a specific time. Pitfalls

  • imprecise as @TheAddonDepot pointed out in comments above
  • requires creating and deleting triggers, which meant adding yet another column to the sheet

function createOneTimeTrigger(sMinutes) {
  // Get the current date and time
  var now = new Date();
  
  // Set the trigger time to 60 minutes after now
  //var triggerTime = new Date(now.getTime() + (sMinutes * 60 * 1000));
  
  // Create the time-based trigger
  var trigger = ScriptApp.newTrigger("IFTTTOff")
    .timeBased()
    .after(sMinutes * 60 * 1000)
    //.after(triggerTime)
    .create();

    return trigger.getUniqueId() 
    
}


/**
 * Deletes a trigger - call after the above trigger runs
 * @param {string} triggerId The Trigger ID.
 * @see https://developers.google.com/apps-script/guides/triggers/installable
 */
function deleteTrigger(triggerId) {
  // Loop over all triggers.
  const allTriggers = ScriptApp.getProjectTriggers();
  for (let index = 0; index < allTriggers.length; index++) {
    // If the current trigger is the correct one, delete it.
    if (allTriggers[index].getUniqueId() === triggerId) {
      ScriptApp.deleteTrigger(allTriggers[index]);
      break;
    }
  }
}

The second way, which I settled on as the best method for this is

  1. Create a function that checks if the Column "Off?" has an "On" value, and if so, use that data to do what you want to do and then change the value to "Off" and update "Time ran" Column.
  2. Then create a time based trigger that runs every x minutes - and checks for the "On" and then if the requisite amount of time has passed.

This method to me is more reliable. I set the trigger to run every 5 minutes, which means it is going to be a maximum of 5 minutes imprecision.

function searchSheet() {
  // Get the active spreadsheet and sheet
  var ssID = "<sheetid>";
  var ss = SpreadsheetApp.openById(ssID);
  var shName = "<sheetName>";
  var sh3 = ss.getSheetByName(shName);
  
  // Get the range of cells to search
  var range = sh3.getDataRange();
  
  // Get the values of the cells in the range
  var values = range.getValues();
  
  // Loop through the rows to find the row with column 5 set as "On"
  for (var i = 0; i < values.length; i++) {
    if (values[i][4] == "On") {

      //Get Time and Time since ran
      var sName = values[i][0]
      var sReference = values[i][1]
      var sAmount = values[i][2]
      var sTimeToUse = WhatTimeToUse(sAmount)
      var sCurrency = values[i][3]
      var sTimeSince = values[i][5]
      var sTimeOff = new Date(sTimeSince.getTime() + (sTimeToUse * 60 * 1000));
      var date = new Date()
      //var sTriggerID = values[i][7];

      if (date > sTimeOff) {
        Logger.log ("Called OFF")

        //Email ?
        MailApp.sendEmail({
        to: "your@email.com",
        subject: "your subject",
        htmlBody:  "Your body"

        });


        CallIFTTT(sReference, "Off")
        sh3.getRange(i+1, 5).setValue("Off");
        sh3.getRange(i+1, 7).setValue(date);
       // deleteTrigger(sTriggerID)

      } else {
        Logger.log("Time not past")
      }
    }
  }

Note, I left the extra lines in here if you use method one, and want to delete the trigger etc.

Craig Lambie
  • 2,888
  • 2
  • 14
  • 17