Triggered Email not working since migrating to V8 Runtime - google-apps-script

First let me state that all my coding is self taught so my knowledge is functional but not deep. I am creating a Google sheet for my HR team in my company to track Paid Time Off. I had built triggers to send emails when time was submitted. It worked before the migration to V8 but doesn't now, and I don't know enough about syntax to be able to find the issue.
function sendNotification(e){
if(e.range.getColumn()=13 && e.value='Yes')
{
//Employee Name and Email Address
var EmployeeName = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PTO Earnings").getRange("R3");
var Employee = EmployeeName.getValue();
var EmployeeEmailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PTO Earnings").getRange("S3");
var EmployeeEmailAddress = EmployeeEmailRange.getValues();
//Approver Name and Email Address
var ApproverName = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PTO Earnings").getRange("R4");
var Approver = ApproverName.getValue();
var ApproverEmailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PTO Earnings").getRange("S4");
var ApproverEmailAddress = ApproverEmailRange.getValues();
//HR Email Address
var HREmailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PTO Earnings").getRange("S5");
var HREmailAddress = HREmailRange.getValues();
//Link to Employee's PTO Spreadsheet
var SheetLink = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PTO Earnings").getRange("R1");
var Link = SheetLink.getValue();
//Email Content
var subject = 'PTO Submitted for '+Employee+' for '+e.range.getSheet().getName()+'.';
var body = 'Dear '+Approver+','+"\n\n"+'A PTO Request has been submitted by: '+Employee+', at:'+"\n\n"+Link +"\n\n"+'Thank you.'+"\n\n"+'Imagine This HR';
var recipient = [EmployeeEmailAddress, ApproverEmailAddress,HREmailAddress];
MailApp.sendEmail(recipient, subject, body);
}
}
Please help!

Seems that un-migrating is optional, but Google is auto-migrating scripts to V8.
Part of the problem is that Google is automatically migrating scripts that pass their compatibility tests. And apparently some use of MailApp.sendEmail will pass the test but will in fact fail when the script is run.
https://developers.google.com/apps-script/guides/v8-runtime#automatic_migration_of_scripts_to_v8
This required me to revert/opt out in about 30 sheets that use MailApp just to ensure they don't fail - users really need scripts to work.
There is no mention of MailApp.sendEmail compatibility issues in any of the migration help docs.

The migration to V8 is optional. If you aren't ready or you don't have time to make your code compatible with it, just disable it. To do this, click Run > Disable new Apps Script runtime powered by Chrome V8.

It seems like a lot of triggers don't work with V8 Runtime migration.
I have a trigger on onOpen () and it doesn't work when the sheet is opened by a collaborator. However, it works well when it is opened by the owner. I haven't found a solution yet ...

Related

Google App Script - How to monitor for new users

I wondered if anyone could point me in the right direction here?
I want to monitor the Google Workspace estate, and when a new user has been created send them an email. I’ve looked through the APIs but nothing is jumping out at me. But I know there are 3rd party tools out there that do this, so there’s got to be something I have missed?
I just created this script in Google Apps Script which gets and prints the list of all the users that were created today.
You can use this as a guide and keep testing with it. To accomplish this I used the Reports API to get the admin logs and get the list of all the users that were created today.
function myFunction() {
var userKey = 'all';
var applicationName = 'admin';
var optionalArgs = {
eventName:'CREATE_USER',
startTime: "2022-03-23T12:00:00.000Z",
fields : "items.events.parameters.value"
};
var rep = AdminReports.Activities.list(userKey,applicationName,optionalArgs);
const A = (JSON.parse(rep));
var totalUsers = Object.keys(A.items).length;
for(var i=0; i<totalUsers; i++)
{
var userEmail = A.items[i].events[0].parameters[0].value;
Logger.log(userEmail);
}
}
You would just need to change the startTime value according to the date you need to use and implement the part of sending the email now that you have all the email addresses.
References
API method: activities.list
Apps Script reference: Reports API

DriveApp.getFileById() throwing a server error [duplicate]

This question already has answers here:
DriveApp Error: "We're sorry, a server error occurred. Please wait a bit and try again."
(4 answers)
Closed 1 year ago.
I have a script that I am using to export a google sheet to a PDF. It has been working no problem up until now. I deployed it as a Web App so that it could be used by anyone. Since I deployed it, it has not worked. I tried archiving the Web App and that did not help.
The issue seems to be with the DriveApp.getFileById() function. I inserted numerous console.log() functions along the way of my code to try to narrow down where it actually fails and why. The execution log shows that the error is happening at the same spot that I derived, but the console.log() functions did not help me figure out why. The error I receive is:
Exception: We're sorry, a server error occurred. Please wait a bit and try again.
I have tried researching this for quite some time now, and have tried suggestions that I found. I double checked all my variables and code syntax. I also know that the pdfid is being pulled correctly, as verified by logging it. I have not altered this part of the code, or any part that it depends on, at all since it has been working. I believe the Web App deployment is the issue, but I'm not sure why or how I could fix it. It could also be something else I'm missing, as it still does not work even after archiving the Web App.
Here is the code, the last line shown is where it fails:
function exportLog(type) {
console.log('Starting Export');
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var realExp = sheet.getSheetByName('exportThis');
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export");
//console.log('Spreadsheet created');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sheet = realExp.copyTo(newSpreadsheet);
//console.log('Copied');
SpreadsheetApp.getActiveSpreadsheet().toast('Beginning export...','Loading...',-1);
//console.log('using new sheet');
newSpreadsheet.getSheetByName('Copy of exportThis').showSheet();
//console.log('successfully used new sheet');
newSpreadsheet.getSheetByName('Sheet1').activate();
newSpreadsheet.deleteActiveSheet();
//console.log('deleted');
var pdfid = newSpreadsheet.getId();
console.log(pdfid); // Correctly logs ID
var pdf = DriveApp.getFileById(pdfid);
function exportLog(type) {
const ss1 = SpreadsheetApp.getActive();//this returns the active spreadsheet
const xsh = ss1.getSheetByName('exportThis');
const ss2 = SpreadsheetApp.create("Spreadsheet to export");
const nsh = xsh.copyTo(ss2);
nsh.showSheet();//you can't really show the sheet because ss2 is not the active spreadsheet
ss2.getSheetByName('Sheet1').activate();//again you can't activate this sheet because ss2 is not the active spreadsheet
ss2.deleteActiveSheet();//This is not the active spreadsheet
const pdfid = ss2.getId();//I don't know why you call this a pdf it's a spreadsheet
const pdf = DriveApp.getFileById(pdfid); //again ss2 is a newly created spreadsheet
}
//Input parameter is never used
You should get in the habit of referring to the documentation while you write your code and pay special attention to the types that are returned by methods.

Can I duplicate a trigger with my new spreadsheet?

I have a new client template that I want to keep from being filled out prior to being duplicated. I've set a trigger to run a duplication and renaming function for the file when the file is opened.
function newRecord(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var scheck = sheet.getName();
if (scheck=='#New Client Record'){
var file = DriveApp.getFilesByName('#New Client Record').next();
file.setName('New Client')
file.makeCopy('#New Client Record');
}
}
This checks to see that the file is the correct file before proceeding, changes the name of the template and then duplicates the file and renames it the old template name. I configured it in this way so that when we start filling out the data right away while we're talking on the phone with a client, we aren't sullying our original template which remains pristine. It works nicely except that the trigger from file #1 doesn't transfer to file #2 which takes on the new identity of the original template.
So my question is - can I duplicate the trigger as well? Or is there script that can open the duplicate file and close the template file to protect my form?
EDIT: I threw a bit more at this last night and tried to add an install trigger with limited effect (I probably sound like a boomer talking about smoking the drugs with this - I'm not a programmer and have only a rudimentary vocabulary for script). I added the following function, which is transferring, but does not seem consistent in its functionality (excusing the pun).
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('newRecord')
.forSpreadsheet(ss)
.onOpen()
.create();
}
Thanks for any assistance any of you can provide on this. I've stumbled my way this far on my own with the archives, but I've finally gotten myself stuck.
How to get a form to create a new sheet with values from a template
Here is a quick example of how to carry this out. What you need to see this example in action:
A template sheet that looks like this:
A form that will have 3 "Short answers" that are required.
Client Name
Phone
Email
A standalone script (Drive Web app > New > More > Google Apps Script).
A folder into which all the newly created sheets will go.
At the top of the script (outside any function) you can define 3 global variables with the appropriate IDs:
const FORM_ID = ...
const TEMPLATE_ID = ...
const CLIENT_FOLDER_ID = ...
First, to set up an onFormSubmit trigger, you can do this by running the following function only once.
function createTrigger(){
const form = FormApp.openById(FORM_ID)
ScriptApp.newTrigger("formSubmitReceiver")
.forForm(form)
.onFormSubmit()
.create()
}
After which comes the main function that will:
Receive the formSubmit event and create a range of values from it.
In this example, it will generate a followUpDate that will be 7 days from the submission of the form.
Create a copy of the template file.
Fill the range B1:B4 with the relevant info.
Resulting in a new spreadsheet:
function formSubmitReceiver(e){
const itemResponses = e.response.getItemResponses()
const values = itemResponses.map( itemResponse => [itemResponse.getResponse()] )
const followUpDate = new Date()
followUpDate.setDate(followUpDate.getDate() + 7)
values.push([followUpDate])
const newFile = DriveApp.getFileById(TEMPLATE_ID).makeCopy()
const parentFolder = DriveApp.getFolderById(CLIENT_FOLDER_ID)
newFile.moveTo(parentFolder)
newFile.setName(values[0][0])
const newId = newFile.getId()
const newSpreadsheet = SpreadsheetApp.openById(newId)
const sheet = newSpreadsheet.getSheetByName("Sheet1");
const range = sheet.getRange("B1:B4")
range.setValues(values)
}
Depending on how complex you make your form, and what type of questions you choose (i.e multiple choice etc) this can get more complicated but hopefully it will give you a good idea of how to get something like this working. The simplest way is just to use "Short Answers" as this will just return a string. Also remember to make the questions "Required" if you don't want to handle empty values. Again, this all depends on how exactly you want to implement it and the complexity of the information involved!
References and Further Reading
FormResponse
getItemResponses()
ItemResponse
getFileById(id)
makeCopy()

Sending emails twice with MailApp.sendEmail

I have created a google apps script to automate emails once a Google Form is submitted. The script is quite simple:
function AutoConfirmation(e){
var theName = e.values[1];
var theEmail = e.values[2];
var theSubject= e.values[3];
var myEmail = "myemail#gmail.com";
var theMessage = e.values[4];
var subject = "Contact form response – " + theSubject;
var message = theMessage;
MailApp.sendEmail (myEmail, subject,message);
}
However, fo some reason I can't figure out, every time a form is submitted I get two instant emails:
Has the data submitted (all works as expected)
Is empty (e.g. subject is "Contact form response –")
I even started from scratch in a different Google account I have and the same issue happens.
Appreciate any suggestions!
It appears the issue is being caused by the internal process that syncs form responses to the spreadsheet. Under some circumstances it makes slight updates to the "Timestamp" column of previously submitted form responses, which cause the onFormSubmit triggers to fire again for those rows (albeit with incomplete event objects).
The engineering team is still working on a fix, but in the mean time you can work around this issue by filtering out form submit events that only affect the timestamp column. Since you can reorder the columns in a Form Responses sheet, the best way would be to check if the event's range only covers a single column:
function onFormSubmit() {
if (e.range.columnStart == e.range.columnEnd) return;
// The rest of your code
// ...
}

How to resolve "Too much calls for this service today : gmail" error in Google Apps script?

My question is about an error in Google's spreadsheet using gmail service in a function.
Since a while, an error occur when I run a function (On Google Spreadsheet) for retrieve mails on a label founded in the MailBox (Gmail).
The error message is : "Too much calls for this service today : gmail".
I want to specify that function worked fine before and it hasn't been modified.
The function is launched one time per month (Except in exceptional case)
I did some research on the error message, and the answers found confirmed what I thought,
daily quotas for Google's gmail services are exceed and can not be used until 24 hours.
However, it's the only one that has not worked, while others are working properly with these services without any errors.
Following this, I created a copy of the spreadsheet with the function to test if it isn't the sheet that does not work, but it has not changed.
And I launched it with another Google account, and it worked.
Does anyone know why this message appears please ?
Should we do a special manipulation to make it work again ?
Here is the row that sends an error :
var threads = GmailApp.getUserLabelByName("Label").getThreads();
And the function :
function readMail(){
var threads = GmailApp.getUserLabelByName("Label").getThreads();
var messages = GmailApp.getMessagesForThreads(threads);
for(var i in messages){
var message = messages[i];
for(var j in message){
var mess = message[j];
var sub = mess.getSubject();
if(mess.getTo().indexOf("email#gmail.com") > -1)
continue;
var attach = mess.getAttachments()[0];
var file = {
title: attach.getName()
};
var fileDoc = Drive.Files.insert(file, attach, {convert: false}); // Use Drive API
mess.markRead();
}
}
}