Timed script not working - google-apps-script

I have a google script that I would like to run automatically every weekday at 8:11 am. I have set my time zones to make sure that everything is correct, but it never seems to work correctly. I am still weak at scripting. Does anyone see where I might have error in this script?
function myFunction(){
try {
var d = new Date();
if (d.getDay() == 6 || d.getDay() == 0) return;
if (d.getHours() != 08 && d.getMinutes() != 11) return; // This will stop the script from running unless it is 8:11am
} catch (e) {
MailApp.sendEmail("pthompson#ucc.on.ca", "Error report", e.message);
}
}
Thank you,
Paul

To run a function based on time, setup a project trigger (under Resources tab) with the Event "Time-driven". You can even setup a notification.
If you wanted to manage this trigger programmatically you could do something like this:
function createTimeTrigger() {
// Once a day at 8AM, near minute 11 for function 'writeSomething()'
var dailyHourNearMinute = ScriptApp.newTrigger("writeSomething")
.timeBased()
.everyDays(1)
.atHour(8)
.nearMinute(11)
.create();
}
You will notice that this creates a trigger for you that can be viewed under 'Current project's triggers'. It will say between 8AM-9AM. As far as google time events, they work well at getting it within the hour but not on the exact minute. The load in the system can also throw this off. So, the best you can really plan for is, between 8AM - 9AM.

Related

Time based GAS trigger disabled for internal error

I'm trying to create a trigger to launch a function in GAS every 15 minutes starting from a precise time (every day). So the trigger has to run the script at (for example) 10:02 and then every 15 minutes: 10:17, 10:32, 10:47, 11:02, etc.
So I've found a great source to run a script in a certain time (without using atHour and nearminute(0) because they have a confidential interval of +/- 15 minutes), here is the source (big up for the guy David Weiss): https://github.com/davidtheweiss/Apps-Script-Season-3-Script-Service/blob/master/Episode%201.1.gs
And there you can find the adapted code of the trigger for my purpouse:
function copyPicked(){
// code of the function I want to run
}
////////////////// TRIGGER
function immutableTrigger() {
ScriptApp.newTrigger('mutableTrigger')
.timeBased()
.everyMinutes(15)
.create();
}
function mutableTrigger() {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth();
const day = today.getDate();
const functionName = 'copyPicked';
ScriptApp.getProjectTriggers().forEach(trigger => {
trigger.getHandlerFunction() === functionName ? ScriptApp.deleteTrigger(trigger) : 0;
});
ScriptApp.newTrigger(functionName)
.timeBased()
.at(new Date(year, month, day, 10, 02))
.create();
}
The problem is that when I run the functions mutableTrigger and immutableTrigger they run perfectly and smoothly but then a time trigger is created with the name of the function I want to run (copyPicked) with a precise time (10:02) and at 10:02 the function is automatically disabled for internal error! Why?
I found this could be a problem related to the runtime: V8 runtime has an issue with this type of time triggers -> https://issuetracker.google.com/issues/150756612
So I tried to enable the old runtime: DEPRECATED_ES5 as described in the above issue but it gives me a generic error at this point of the code related to the trigger:
ScriptApp.getProjectTriggers().forEach(trigger => {
I don't know this old runtime!
I have tried it myself but I have no problem with running copyPicked.
Have you provided enough permission for copyPicked?
Arrow function (trigger => {) is not available in ES5

Trigger error: We're sorry, a server error occurred while reading from storage. Error code PERMISSION_DENIED

My Gapps script called from time based trigger is logging tons of errors "We're sorry, a server error occurred while reading from storage. Error code PERMISSION_DENIED".
I assume there are users, who installed the app and trigger, but later removed permissions for script to run. Which, obviously, may cause this issue.
I'm still trying and failing to avoid it...
This is how trigger is installed:
var triggerFunction = "processRecurrentLists"; //trigger callback function name
//---------------------------------------------
function createTriggers(tmz) {
// Trigger running at 1am depending on timezone specified in settings
// If no timezone specified, script timezone is used (GMT)
ScriptApp.newTrigger(triggerFunction)
.timeBased()
.everyDays(1)
.inTimezone(tmz)
.atHour(1)
.create();
}
This is the triggered function:
function processRecurrentLists() {
// Check if the actions of the trigger requires authorization that has not
// been granted yet; if not, then end - nothing to do.
if (!isScriptAuthorized()) {
console.warn("RecGTasks script is not authorized to run. Please, authorize first.");
return;
}
// ... more code here
}
And function testing authorization:
function isScriptAuthorized() {
try {
var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);
return (authInfo.getAuthorizationStatus() == ScriptApp.AuthorizationStatus.NOT_REQUIRED);
} catch (e) {
console.warn("Script not authorized. err=%s",e.message);
return false;
}
}
My assumption is that function isScriptAuthorized returns false if script is not authorized and script ends gracefully, but it seems not to be the case...
Any idea on this?
Update 1
I have made script accessible for all users as a workaround for https://issuetracker.google.com/issues/175139769 and on the same day message changed to "Authorization is required to perform that action.", so no more PERMISSION_DENIED. Investigating further.
Update 2
Yes, as #ziganotschka pointed out, it is related to V8. I used following script to test it thoroughly:
function doGet() {
// if trigger not installed
if (ScriptApp
.getProjectTriggers()
.filter(function(t){return t.getHandlerFunction() === "myFunction"})
.length == 0
){
// create trigger firing every 1min
ScriptApp.newTrigger("myFunction")
.timeBased()
.everyMinutes(1)
.create();
}
return HtmlService.createHtmlOutput('<h1>Trigger test installed</h1>');
}
function myFunction() {
Logger.log("Hello from triggered function");
return ("Hello from server");
}
It seems to be a bug related to the v8 runtime
Have a look here and here.
This problem seems to occur when users are signed in with multiple accounts at once.
While Google is fixing the bug, I suggest you to temprorily disable the v8 runtime to avoid the problem.

How do I create a timeBased Add-on trigger that runs every hour?

Extension of my previous post here, I just figured this was more specific and should be it's own post.
Add-ons have restrictions on triggers. One such is that it can only run once an hour. I cannot figure out how to make that work.
Running the below script will produce the error: "attempted to perform an action that is not allowed" when it is run as an add-on. So if the below is not the proper add-on method for a once an hour script, what is, or did I find a bug?
ScriptApp.newTrigger('updateDay').timeBased().everyHours(1).create();
I tried adding the authorization check as Spencer suggested, and outlined in the documentation here. It passes the authentication, but still produces the same error.
function installTrigger(e) {
var addonTitle = 'Lab Scheduler';
var props = PropertiesService.getDocumentProperties();
var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);
if (authInfo.getAuthorizationStatus() ==
ScriptApp.AuthorizationStatus.REQUIRED) {
var lastAuthEmailDate = props.getProperty('lastAuthEmailDate');
var today = new Date().toDateString();
if (lastAuthEmailDate != today) {
if (MailApp.getRemainingDailyQuota() > 0) {
var html = HtmlService.createTemplateFromFile('AuthorizationEmail');
html.url = authInfo.getAuthorizationUrl();
html.addonTitle = addonTitle;
var message = html.evaluate();
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),
'Authorization Required',
message.getContent(), {
name: addonTitle,
htmlBody: message.getContent()
}
);
}
props.setProperty('lastAuthEmailDate', today);
}
} else {
// Authorization has been granted, so continue to respond to the trigger.
try{
var as = SpreadsheetApp.getActiveSpreadsheet();
var userTriggers = ScriptApp.getUserTriggers(as);
var userTriggerL = userTriggers.length;
if (userTriggers.length == 0){
ScriptApp.newTrigger('updateDay').timeBased().everyHours(1).create();
}
} catch(err){
catchToString_(err);
} // End try catch
}
}
The issue you are running against is the scope of authorization your Add-on is running in when the trigger gets created or ran. Installed Triggers are run in AuthMode.FULL. You need to test for the current level of authorization before you can run the trigger. You use the ScriptApp.getAutorizationInfo(authMode) to get the status of the authMode the add-on is running in.
https://developers.google.com/apps-script/reference/script/script-app#getAuthorizationInfo(AuthMode)
Here is an example bit of code from the Apps Script documentation:
https://github.com/googlesamples/apps-script-form-notifications-addon/blob/master/Code.gs
var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);
// Check if the actions of the trigger require authorizations that have not
// been supplied yet -- if so, warn the active user via email (if possible).
// This check is required when using triggers with add-ons to maintain
// functional triggers.
if (authInfo.getAuthorizationStatus() ==
ScriptApp.AuthorizationStatus.REQUIRED) {
// Re-authorization is required. In this case, the user needs to be alerted
// that they need to reauthorize; the normal trigger action is not
// conducted, since it authorization needs to be provided first. Send at
// most one 'Authorization Required' email a day, to avoid spamming users
// of the add-on.
sendReauthorizationRequest();
} else {
// All required authorizations has been granted, so continue to respond to
// the trigger event.
}
I think you are using the trigger for specific days at specific hour, so in your example you would need to specify the day (e.g. Mondays) and atHour(1) would make the trigger to run every monday at 1.
For specifying how often it should be triggered you would need to write:
ScriptApp.newTrigger('myFunction').timeBased().everyHours(1).create();
I have come to the conclusion that this is a bug or .timebased() triggers are not supported as an add-on, (which I thought they were).
Please star this issue to help get this working again.
https://code.google.com/p/google-apps-script-issues/issues/detail?id=4524&q=.timeBased()%20add-on%20trigger&colspec=Stars%20Opened%20ID%20Type%20Status%20Summary%20Component%20Owner

Google Spreadsheet Triggers

I'm trying to create a trigger that activates on weekdays only and at a specific time, but I don't know what I'm doing wrong. Here is my code.
function createTriggers() {
var days = [ScriptApp.WeekDay.MONDAY, ScriptApp.WeekDay.TUESDAY,
ScriptApp.WeekDay.WEDNESDAY, ScriptApp.WeekDay.THURSDAY,
ScriptApp.WeekDay.FRIDAY];
var d = new Date();
var time = d.toLocaleTimeString();
if (time == '3:05:00 PM EDT') {
for (var i = 0; i < days.length; i++) {
ScriptApp.newTrigger(Lunch1)
.timeBased().onWeekDay(days[i])
.everyMinutes().create();
}
}
}
I'm guessing that you mean you want to make it so a function is triggered (runs) every weekday at a certain time of the day. Here's what I would do.
First, in the Script Editor:
Go to Resources - Current Project's Triggers
Click Add a new trigger
Select the function that you want to be triggered
Change the next box to "Time-driven"
Change the next box to "Hour timer"
Then change the last box to "every hour"
Then at the very beginning of your function, add this code:
var d = new Date();
if (d.getDay() == 6 || d.getDay() == 0) return;
// more info here: http://www.w3schools.com/jsref/jsref_getday.asp
That will stop the rest of the script from running if it's a Saturday or Sunday.
Then, say you want the script to only run at one specific time each weekday, you could set the time-driven trigger to "Every minute" and then add another if statement after the one above:
if (d.getHours() != 15 && d.getMinutes() != 5) return;
That will stop the rest of the script from running if it's not exactly 15:05 (3:05 PM).
This is definitely not the most efficient way to do this, but it works.
Also, this is worth mentioning: I'm not sure if there's anything that would prevent you from triggering the script every minute. Google does impose quotas for what your scripts can do, but I didn't see anything about how many times a script can be triggered in a day. Here's the chart: https://developers.google.com/apps-script/guides/services/quotas
You should do something like this:
function createTriggers(func, hour) {
var weekdays = [ScriptApp.WeekDay.MONDAY, ScriptApp.WeekDay.TUESDAY,
ScriptApp.WeekDay.WEDNESDAY, ScriptApp.WeekDay.THURSDAY,
ScriptApp.WeekDay.FRIDAY];
for (var dayIndex in weekdays) {
var day = weekdays[dayIndex];
ScriptApp.newTrigger(func).timeBased()
.onWeekDay(day).atHour(hour).create();
}
}
This code creates a trigger for weekdays in a specific time calling the func function.

Google Apps Script : not reliable time triggers

is it allowed to chain time triggers in Google App script like this :
function doGet(e){ //first invocation by user, HTTP GET
if (e == null || e.parameters == null) {
return ContentService.createTextOutput("EMPTY");
}
saveGetParametersForUser(Session.getUser().getEmail(), e);
//trigger 10 seconds
var timeTrigger = ScriptApp.newTrigger("timeDrivenEvent").timeBased().after(10 * 1000).create();
}
function timeDrivenEvent() { //runs until there are some data in ScriptDB
Logger.log("INVOKED AT " + new Date());
removeAllPreviousTriggers(); //removes old time triggers
var somedata = loadTaskData({email: "" + Session.getUser().getEmail()});
var remainingData = processTaskData(somedata);
if(remainingData == null){
return; //we are finished here
}
removePreviousAndSaveRemainingTaskData(remainingData);
var timeTrigger = ScriptApp.newTrigger("timeDrivenEvent").timeBased().after(10 * 1000).create();
}
First invocation by user doGet()
Until all data are processed script invokes itself with 10 sec intervals (e.g. 2minutes of processing, 10 seconds nothing happens, then again 2 minutes of processing...)
size of processed data is ~ few kilobytes and processing time takes usually 1-2mins.
What happens to me that sometimes script is interrupted and data are not fully processed ! I am not getting any email alerts and nothing is in log or execution transcript - everything looks fine.
I am starting to think that maybe 10 seconds is quite quick to start script method but it`s in the API after all...
Any ideas ?
This is ONLY POSSIBLE solution how to chunk big task into smaller pieces as Google App Script cannot run for more than ~ 5-6 minutes (see quotas).
Having one periodic time trigger worked well as it was recommended in comments.
I just wonder why time trigger chaining didnt worked well ! What principle i did broke that Google App Script didnt like that.
Documentation : https://developers.google.com/apps-script/class_clocktriggerbuilder
method everyMinutes Sets the trigger to be created to fire on an interval of the passed in number of minutes which must be one of 1, 5, 10, 15 or 30.