Getting google spreadsheets to send me an email - google-apps-script

I have a spreadsheet on Google spreadsheets that has "D2" through all of "D" (because it will expand as used and when I add more) with a drop down box for priority status. The drop down has "Low, Medium, High". I want to get a script to send me an email when that priority gets edited to "High"
This is the script I did last night at 2AM half asleep.
{
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("TC52 Bugs or Issues and Improvements.");
var valueToCheck = sheet.getRange(D).getValue();
var rangeEdit = e.range.getA1Notation();
if(rangeEdit == "D")
{
if(valueToCheck > High)
{
MailApp.sendEmail("austin.hendrix#decathlon.com", "High Priority please check now.", "Check spreadsheet" + valueToCheck+ ".");
}
}
}````

This article has a tutorial.
On that note, something along the lines of (Heavily edited by MetaMan)
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange(1,4,sh.getLastRow()) or getRange('D1:D'+sh.getLastRow()));
var data = dataRange.getValues();
for (var i in data) {
var row = data[i];
var emailAddress = "youremail#email.com"; // First column
var message = "High priority set with data in A column "+sheet.getRange(("A"+i+1)).getValues()[0]; // Second column
var subject = 'High priority set in D'+i;
if(row[0]=="High"){
MailApp.sendEmail(emailAddress, subject, message);
}
}
}

The function will send you an email when column D is edited to "High".
You will need to fill in 'Your Spreadsheet name' the recipient email address and what ever subject you wish. Also you can edit the body as well. The current body just tells you which row issue the email. Alternately, if you wish to provide more information I'll do it for you.
function onMyEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Your Sheet Name' && e.range.columnStart == 4 && e.range.rowStart >1 && e.value == 'High') {
GmailApp.sendEmail('recipient','subject',`row ${e.range.rowStart} has been set to high`)
}
}
Since sending an email requires permission you will have to create an installable trigger for this function.
You can do it manually or use a function like the one below which will prevent you from creating more that one trigger.
function createTrigger() {
const ts = ScriptApp.getProjectTriggers().map(t=>t.getHandlerFunction());
if(!~ts.indexOf('onMyEdit')) {
ScriptApp.newTrigger('onMyEdit').forSpreadsheet(SpreadsheetApp.getActive().getId()).onEdit().create();
}
}
ScriptApp
Please Note: that you cannot run onEdit triggered functions from the Script Editor or even from a menu. They require the onEdit trigger which populates the single parameter e with the event object. You can run them from another function as long as it provides the event object.

Related

Google App Script unable to send notification only for specific sheet and previously empty cell

I'm new to Google Script and I'm trying to create this script that will send notification to the messenger app when there is a new request filled into a specific tab and column.
So the tab's name is "Additional Requests" and only when a cell in column B "Request Number" is filled, it will trigger the notification. It shouldn't trigger notification when a filled cell is edited.
I have managed to make the trigger only happening to column B. However, the trigger also happens when other tabs' column B is edited even when I have already specified the tab "Additional Requests" in the script. Additionally, it also trigger notification when the filled cell in column B is edited. May I know what has gone wrong in the script? I have searched through a lot of similar questions to find answers but none worked.
Thank you in advance!
function API() {
var cell = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Additional Requests").getCurrentCell();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Additional Requests");
var columnofCellEdited = cell.getColumn();
if ((sheet.getSheetName() === "Additional Requests") && (columnofCellEdited === 2)) {
if((cell.value !== null) && (cell.oldValue == null)) {
var payload = JSON.stringify({
"tag": "text",
"text": {
"content": "--",
"email_list": ["--#123.com"],
"at_all": false
}
});
You need to use the event object and check the edited range:
Simple triggers and installable triggers let Apps Script run a function automatically if a certain event occurs. When a trigger fires, Apps Script passes the function an event object as an argument, typically called e. The event object contains information about the context that caused the trigger to fire.
You can then use the event object passed to the edit function to get infromation about the edited cell:
function API(e) {
var cell = e.range
var ss = e.source.getActiveSpreadsheet()
var sheet = ss.getSheetByName("Additional Requests")
var editedSheet = ss.getActiveSheet()
var columnofCellEdited = cell.getColumn()
if (editedSheet.getName() === "Additional Requests") {
if (columnofCellEdited === 2) {
if (cell.value !== null && cell.oldValue == null) {
/*
* Your onEdit() code goes here
*/
}
}
}
}
The thing you're not doing is checking to see if the active sheet is called "Additional Requests", but if the sheet that you manually obtained is - which it always will be.
This also assumes already that your API() method runs on an installable onEdit trigger.

Google App Script to trigger on cell value change

I am a newbie and have been using a simple App Script to send out emails with triggers onEdit and onChange. However, my Worksheet has over ten sheets and any edits/changes (done by me or by computations) in any of the sheets sends out an email, causing unintended spam! To avoid this, if I could use some code that sends the email based only on ANY CHANGE to a specific cell's value, in a specific sheet, my problem would be solved. My outgoing email message is short and the whole message is in just ONE cell (C2). If I can add a line of code which monitors for ANY change in that cell C2, and sends out an email if there is a change, that's it! I'd be done. My Script is as follows:
function sendEmail(){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Email');
var emailAddress = sheet1.getRange(2,1).getValue();
var subject = sheet1.getRange(2,2).getValue();
var message = sheet1.getRange(2,3).getValue();
MailApp.sendEmail(emailAddress, subject, message);
}
Answer:
You can do this with an onEdit() and a conditional.
Code Example:
function onEdit(e) {
const specificSheet = "Email" // for example
const specificCell = "C2" // for example
let sheetCheck = (e.range.getSheet().getName() == specificSheet)
let cellCheck = (e.range.getA1Notation() == specificCell)
if (!(sheetCheck && cellCheck)) {
return
}
else {
sendEmail()
}
}
Rundown of this function:
Defines the sheet and A1 notation of the specific cell to check
Gets the Sheet and the A1 notation of the cell which was just edited
Returns if either the Sheet or the Cell are not the defined specific cell (using De Morgan's law)
Runs sendEmail() if the cell and Sheet are correct
References:
Event Objects | Apps Script | Google Developers
Simple Triggers | Apps Script | Google Developers
De Morgan's laws - Wikipedia
Working from the answer, this is how it ended up for me since I had several ranges to check:
function onEdit(e){
if(wasEdited(e, range1)){ // e.g. range1 = "Sheet1!A5"
// handle range1 change
}
if(wasEdited(e, range2)){ // e.g. range2 = "Sheet1!A7"
// handle range2 change
}
}
function wasEdited(e, range){
let tab = getTabFromA1Range(range)
let cell = getRangeFromA1Range(range)
return e.range.getSheet().getName() == tab && e.range.getA1Notation() == cell
}
function getTabFromA1Range(a1Range){
return a1Range.substring(0, a1Range.indexOf("!"))
}
function getRangeFromA1Range(a1Range){
return a1Range.substring(a1Range.indexOf("!")+1)
}
On further research, the following solution seems to work the best:
function sendEmail(){
Utilities.sleep(30000);
var ss=SpreadsheetApp.getActiveSpreadsheet();
var data=ss.getActiveSheet().getActiveCell().getA1Notation();
var sheetname = ss.getActiveSheet().getName();
var sheet1=ss.getSheetByName('Email');
var emailAddress = sheet1.getRange(2,1).getValue();
var subject = sheet1.getRange(2,2).getValue();
var message = sheet1.getRange(2,3).getValue();
if(data.indexOf('A:C')!=-1.23456789) {
MailApp.sendEmail(emailAddress, subject, message);
}
};
The key seems to be the "if statement" on line 10. Please note the time delay of half a minute I added to the script. This is because without it, on the trigger activating, the previous email was going out instead of the current one. Obviously my app has a slight delay in syncing and the trigger fired before all the current data got populated in the relevant cell!

var userMail = e.user.getEmail(); on edit google sheets not pulling email

I was using the following script on a google sheet that worked for months, but now around 1:30 PM Thursday 3/5/2020 it stopped working and pulling the user emails. I tried changing it to the second attempt, saved, and edited the cell, but nothing happened.
(Note: I cannot share the file due to regulations.)
What was working before:
function onEdit(e) {
var rg = e.range;
if (rg.getSheet().getName() == "Call Log" && rg.columnStart == 13) {
var nextCell = rg.offset(0, -3);
if (nextCell.getValue() == '') {
var userMail = e.user.getEmail();
nextCell.setValue(userMail);
}
}
}
My second attempt:
function onEdit(e) {
var rg = e.range;
if (rg.getSheet().getName() == "Call Log" && rg.columnStart == 24) {
var nextCell = rg.offset(0, 3);
if (nextCell.getValue() == '') {
var email = Session.getActiveUser().getEmail();
Logger.log(email);
nextCell.setValue(userMail);
}
}
}
onEdit Event Object clearly states the following:
If security policies do not allow access to the user's identity, User.getEmail() returns a blank string. The circumstances in which the email address is available vary: for example, the user's email address is not available in any context that allows a script to run without that user's authorization, like a simple onOpen(e) or onEdit(e) trigger, a custom function in Google Sheets, or a web app deployed to "execute as me" (that is, authorized by the developer instead of the user). However, these restrictions generally do not apply if the developer runs the script themselves or belongs to the same G Suite domain as the user.
Lara,
Honestly, I've never worked with google sheet functions. But it looks like their API is pretty straightforward. Based on the error
TypeError: Cannot read property 'range' of undefined (line 2, file
"Code")
I can assume that your function doesn't consume any values. You should retrieve the range from another object. I found this article very good to start with. It looks like you first have to get your sheet using var sheet = SpreadsheetApp.getActiveSheet(); and then carefully read API documentation.
Most likely you will end up with something like:
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange();
if (sheet.getName() === "Call Log" && range.getColumn() === 24) {
var nextCell = range.offset(0, 3);
if (nextCell.getValue() === '') {
var email = Session.getActiveUser().getEmail();
Logger.log(email);
nextCell.setValue(email);
}
}
}
But I can't test this solution. Please, give it a try or update this in the way you wish.

Column or Cell restriction if the other cell or column is blank

Hello masters/mentors/teachers
I'm having problem with my existing project. I'd like to try here if anyone can help me
Note: Timestamp is script coded
Column A
a Dropdown Cell (Not yet started, In Progress, Completed)
Column B
if Column A on the same row choose " In Progress ", the Column B will be having a timestamp (time start)
Column C
if Column A on the same row choose " Completed ", the Column C will be having a timestamp (time end)
My problem is sometimes people may forgot to put "In Progress" first and just proceed with "Completed".
Is there a way to restrict the column C Cell if the column B is blank ? therefore they can't proceed with "Completed" if the "In Progress" timestamp are not yet available and Column C will only get timestamp when Column B's no longer blank
What you can do is setup a trigger onEdit that will check when a user select 'Completed' if there is a start date. If not the script revert back the dropdown to previous value and display a warning message.
To work you must setup the onEdit() as an installable trigger.
It will looks like :
function setupTrigger() {
var sheet = SpreadsheetApp.openById(YOUR_SHEET_ID);
ScriptApp.newTrigger("control")
.forSpreadsheet(sheet)
.onEdit()
.create();
}
function control(e) {
Logger.log(e)
var range = e.range;
var rangeCol = range.getColumn();
var rangeRow = range.getRow();
// Logger.log(rangeCol)
if(rangeCol == 1){
var dateStart = range.getSheet().getRange(rangeRow,2).getValue();
var selection = e.value;
// Logger.log(dateStart);
// Logger.log(selection);
if(selection == 'Completed' && dateStart == ""){
Logger.log('User select completed and the start date is empty');
var ui = SpreadsheetApp.getUi();
ui.alert('Please select In Progress before completed, satrt date can\'t be empty.');
if(!e.oldValue){
range.getSheet().getRange(rangeRow,rangeCol).clearContent();
}else{
range.getSheet().getRange(rangeRow,rangeCol).setValue(e.oldValue)
}
}else{
Logger.log('Do nothing')
}
}else{
Logger.log('Not good column');
}
}
Install it :
Enter the sheet id on the place of YOUR_SHEET_ID.
Run the function setupTrigger()
I setup basic control on the start date but you can check if it is a date, if the value is older than now or not etc... it depends your need but this code give you the basic to implement the control you want.
Stéphane
Using the methods from the Spreadsheet Service [1] and an installable onEdit trigger [2][3], I set up the following code that will run when someone edit your Spreadsheet, if any cell in A column is change to "Not yet started" it'll protect the column C cell in that row from being edited (for anyone besides you). If the cell in A column is change to "In Progress" it'll remove the protection created before:
function onEditFunction(e) {
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
var value = e.value;
if(col == 1) {
if(value == "Not yet started") {
//Protect column C in that row
var protection = range.offset(0, 2).protect();
protection.removeEditors(protection.getEditors());
}
else if(value == "In Progress") {
var rangeC = range.offset(0, 2);
var sheet = range.getSheet();
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.getRange().getA1Notation() == rangeC.getA1Notation()) {
protection.remove();
}
}
}
}
}
function createSpreadsheetOnEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('onEditFunction')
.forSpreadsheet(ss)
.onEdit()
.create();
}
You need to run the createSpreadsheetOnEditTrigger function once to create the onEdit trigger that will run with your credentials.
[1] https://developers.google.com/apps-script/reference/spreadsheet
[2] https://developers.google.com/apps-script/guides/triggers/installable
[3] https://developers.google.com/apps-script/guides/triggers/events

Google Spreadsheet function gets data from all sheets instead of just one

This is the function I have written, which sends an email to notify someone whenever a specified cell/row/column is edited, and I have it set to trigger onEdit. It works as is.
/* This function send an email when a specified range is edited
* The spreadsheets triggers must be set to onEdit for the function
*/
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Get Active cell
var mycell = ss.getActiveSelection();
var cellcol = mycell.getColumn();
var cellrow = mycell.getRow();
//Define Notification Details
var recipients = "me#email.com";
var subject = "Update to "+ss.getName();
var body = ss.getName() + "has been updated. Visit " + ss.getUrl() + " to view the changes.";
//Check to see if column is A or B to trigger
if (cellcol == 1, 2)
{
//check for row to trigger
if (cellrow == 1)
{
//Send the Email
MailApp.sendEmail(recipients, subject, body);
}
//End sendNotification
}
}
My issue is that I need this to work only when a column or row on a certain sheet(page) of the spreadsheet is edited, rather than column X in any of the sheets within the spreadsheet.
Any ideas? I'm hoping it's something simple that I missed, but I have been unable to find a solution.
You could interrupt the function after assigning the sheet variable:
if (sheet.getSheetName() != 'SheetIWant') return;
You need to look at the parameters that onEdit provides. It will tell you where the change happened.