Sending notification from google spreadsheet using time-driven events with condition - google-apps-script

I would like to receive notifications when domain will expire. So I created a spreadsheet with a list of website and date of expiration. It has also a condition in Column A that when a domain is about to expire in 10 days it will appear Send notification as cell value. You can view my spreadsheet here.
With the values in column A, I would like to receive emails telling that the www.sample.com will expire on --some date here--.
For example, when Column A have new values equal to Send notification, then send email.
What I have tried and encountered:
var cell = sheet.getActiveCell().getA1Notation();
var row = sheet.getActiveRange().getRow();
var cellvalue = sheet.getActiveCell().getValue().toString();
var recipients = "youremail#gmail.com";
var domain = '';
var expirydate= '';
if(cell.indexOf('A')!=-1 && cell.indexOf('A') == 'Send notification'){
domain = sheet.getRange('B'+ sheet.getActiveCell().getRowIndex()).getValue();
expirydate = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'Expiry Notification : '+sheet.getName();
var body = 'Website will expire! ' + domain + ' is about to expire on ' + expirydate;
MailApp.sendEmail(recipients, subject, body);
Logger.log(body);
Trying this script only sends me notification without the value of the cell.
I would like this works even when the spreadsheet is not open / I am offline. So I guess I will be using the time driven event (every week).
Any help is much appreciated!

Prepare yourself, my answers tend to be long and explanatory.
Take this block:
if(cell.indexOf('A')!=-1 && cell.indexOf('A') == 'Send notification'){
domain = sheet.getRange('B'+ sheet.getActiveCell().getRowIndex()).getValue();
expirydate = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()
}
In this block we can see that the if will always return FALSE. That is because cell.indexOf('A') cannot be a string, it will be an integer of the index. You want to be checking the value. I assume that is why you have the cellvalue variable.
Furthermore, your use of getRange() is also off. Why bother with A1 notation if you are getting indexes anyway. Instead I will go over the code and offer a different way of coding this.
Ok, let's start from the top of the code. You mentioned that you want this to run offline. We immediately get a problem here:
var cell = sheet.getActiveCell().getA1Notation();
this will be meaningless once you fire script based on a timer. I would recommend batching your data collection. First to be sure that you are not using getActiveSheet():
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Send notification')
OR
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]
to get the first sheet or the sheet by it's name. Then we get the entire list into a 2D array.
var vals = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues()
So we wish to check all domains. So now that we have all the values we wish to loop through them (we assume we left var domain and var expirydate as is)
var i
for (i = 0; i < vals.length; i++) {
if (vals[i][0] == 'Send notification!') {
domain = vals[i][1] //we get the domain name.
expirydate = vals[i][3] //we get the expire date
sendNotification(domain, expirydate); //use a seperate fu
}
}
where in the above block code I would seperate the function
function sendNotification(domain, expirydate) {
var subject = 'Expiry Notification : '+ domain;
var body = 'Website will expire! ' + domain + ' is about to expire on ' + expirydate;
MailApp.sendEmail(recipients, subject, body);
Logger.log(body);
}
of course you can leave the code inside of the for loop, but this will look cleaner. Also, I am not sure you really wanted var subject = 'Expiry Notification : '+sheet.getName(); because that will send all emails with the title Expiry Notification : Send notification because that is the sheet name (the tab at the bottom of the spreadsheet)

Related

SendEmail duration / Count per week in Googlsheet AppScript

I'm sorry if this question was already asked, I tried to look for it but couldn't find it.
I'm trying to send out alert emails with app script based on a specific cell value in google sheets. To point out the value is generated by a formula not by a user.
I created the below with a trigger that runs weekly.
My challenge is, that I want to be able to set up the trigger for the script to run daily to check for the condition but only send the email one time every 7 days until the condition is no longer met.
I greatly appreciate any advice if this is possible.
function CheckSessions() {
// Fetch the sessions available
var sessionsRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Billing").getRange("K1");
var sessions = sessionsRange.getValue();
var ui = SpreadsheetApp.getUi();
// Check totals sessions
if (sessions == 1){
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Billing").getRange("H2");
var emailAddress = emailRange.getValue();
// Send Alert Email.
var message = 'This is your Alert email!'; // Second column
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
I believe you're saying you want the script to run daily, but if you've already sent the email within the past 7 days, don't send it again even if the condition is true.
One approach is to store the date for the last time you sent the reminder, and then do some date math to see if it has been 7 days since the last reminder.
You could store that date in a designated cell of the spreadsheet, but there are some good reasons not to (e.g. so a spreadsheet editor doesn't accidentally overwrite it). This is a typical use case for PropertiesService.
Properties are stored as a string, so you'll have to convert dates to/from strings.
Properties store values based on a key. If your script is only managing email alerts to one address, you can use a static key like lastSent. If you're managing alerts to multiple addresses then you could key by the email address itself. That's what I've done here:
function CheckSessions()
{
// Fetch the sessions available
var sessionsRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Billing").getRange("K1");
var sessions = sessionsRange.getValue();
// Check totals sessions
if (sessions == 1)
{
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Billing").getRange("H2");
var emailAddress = emailRange.getValue();
// Check if we've sent to this email within 7 days
var propertyStore = PropertiesService.getScriptProperties()
var lastSent = propertyStore.getProperty(emailAddress)
if (!lastSent || getDaysDifference(new Date(lastSent), new Date()) >= 7)
{
// Send Alert Email.
var message = 'This is your Alert email!'; // Second column
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
// Store the date in properties as an ISO string, to be fetched on next execution
propertyStore.setProperty(emailAddress, (new Date()).toISOString())
} else
{
console.log("Last email was sent on %s so we won't send one today", lastSent)
}
}
}
/**
* Get days elapsed between two dates
* #param {Date} startDate
* #param {Date} endDate
*/
function getDaysDifference(startDate, endDate)
{
var start = new Date(startDate);
var end = new Date(endDate);
start.setHours(0, 0, 0, 0);
end.setHours(0, 0, 0, 0);
var days = Math.round((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
return days;
}
Send Email once a day
function CheckSessions() {
const ss = SpreadsheetApp.getActive()
var sessions = ss.getSheetByName("Billing").getRange("K1").getValue();
if (sessions == 1) {
var emailAddress = ss.getSheetByName("Billing").getRange("H2").getValue();
var message = 'This is your Alert email!';
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
function createTrigger() {
if(ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() == "CheckSessions").length == 0) {
ScriptApp.newTrigger("CheckSessions").timeBased().everyDays(1).atHour(2).create();//keeps you from creating more than one
}
}

Google Sheets Send SMS to a specific person based on status change of a cell

If you are reading my question, Thank you for taking the time out of your day to help.
Background:
I have a form that my field techs use in order to request parts, I would like to keep them updated on the status of a part order with an automated sms text to their phone.
Specifics:
Link to test Sheet
https://docs.google.com/spreadsheets/d/1hEDEk-3-z3Wh6PNLoY6PgmbSJZ7OcdMR0kFCl0BRrYU/edit?usp=sharing
Parts Request (Picture)
Status Change
When a Status is changed in (column G), this will trigger an SMS to be sent.
Recipient
The SMS will be sent to the Team Lead ( Column B) in that row.
Example: Status is changed (G2), SMS is sent to Team Lead (B2).
Employee Info (Picture)
Employee Information:
The Script pulls the Employee Telephone number(Employee Info! B2) from the Employee Info Sheet
Text Body:
The Message that is sent would be the entire row in the text message
-Team Lead
-Type of Request
-Job Name
-Part Description
-QTY Missing
-Status
The Script i have tried so far has been a simple one, based on a trigger of anytime a change is made to the sheet. Here is what i have used so far, this has worked and has been sending generic texts. Any Help would be greatly appreciated.
function sendText() {
var EmailTo = "'Mobile Number'"#txt.att.net";
var subject = "Whatever";
var body = "Text";
MailApp.sendEmail(EmailTo, subject, body);
}
The processes needed to achieve your goal involves fetching the desired details on the row wherein the status of the request changes, incorporating the given phone number to the appropriate carrier domain, and adding an installable trigger so that the script will automatically work when there are changes to the Status Column.
Here is the script:
function sendUpdates(e) {
var ss = e.source;
var shEmployeeInfo = ss.getSheetByName("Parts Request");
var shPartsRequest = ss.getSheetByName("Employee Info");
var row = e.range.getRow();
var column = e.range.getColumn();
if(column == 7) { //To limit checking of changes to the Status column
var info = shEmployeeInfo.getRange(row, 2, 1, 6).getValues();
var header = shEmployeeInfo.getRange(1, 2, 1, 6).getValues();
var carrier = shPartsRequest.getRange(row,4).getValues();
const subject = "Insert Subject Here."; //Edit this to change the subject
const carrierMap = { "ATT": 'txt.att.net',
"T-Mobile": 'tmomail.net',
"Sprint": 'messaging.sprintpcs.com',
"Verizon": 'vtext.com',
"Cricket": 'mms.mycricket.com' }
var phoneNumber = shPartsRequest.getRange(row,2).getValues();
var emailTo = `${phoneNumber}#${carrierMap[carrier]}`;
var body = "";
for(let i = 0; i <= 5; i++) {
body += header[0][i] + ": " + info[0][i] + "\n";
}
MailApp.sendEmail(emailTo, subject, body);
}
}
The installable trigger should be set as:
Please refer to the Installable Triggers Guide for more information.
As for the result, I made a test case and got this:

Sending the same multiple emails instead of 1 email for loop bug

I have bug where when I run my send email function. its sending multiple emails instead of just one email notification here is my code what am I doing wrong??!?! I got 31 of the same emails. I believe the issue the for loop is sending an email each time the if statement is true instead of just one time if its true help.
here is my code:
function sendEmail(){
var ss = SpreadsheetApp.getActiveSpreadsheet(); //get active spreadsheet only! to get the url for the filter view
var SpreadsheetID = ss.getSheetId(); // get the sheet Id
var spreadsheetURL = ss.getUrl(); // get the current active sheet url
var SpreadsheetID = spreadsheetURL.split("/")[5]; // using the last / for getting the last parts of the email
var filterViewName = 'PO_Log Precentage'; // Name of the filter view you want to get the url from & MAKE SURE Title matches view name account for "spaces" too
var filterViewID = filterId(SpreadsheetID, filterViewName); // Getting filter view id
var url = createURL(spreadsheetURL, filterViewID); // creating the url to send the filter view id
Logger.log(url);// Testing to see the correct url is created
var po_numID = ss.getSheetByName("Purchase Orders List").getRange("A2").getDisplayValue().substr(0,3);// Gets the Purchase Order List Sheet and the PO# the first 3 Characters of the PO in A2
Logger.log(po_numID);
var email_va = ss.getSheetByName("Purchase Orders List");
//gonna build statuses to look for into array
var statusesToEmail = ['On-going', '']
//"Status" is in Column T (Col 2)
//"Precent" is in Column Q (Col 3)
var data = email_va.getDataRange().getValues()
// //var headerRowNumber = 1; // When checking for emails in the sheet you want to exclude the header/title row
var emailDataSheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/17G0QohHxjuAcZzwRtQ6AUW3aMTEvLnmTPs_USGcwvDA/edit#gid=1242890521").getSheetByName("TestA"); // Get The URL from another spreadsheet based on URL
Logger.log(emailDataSheet.getSheetName());
var emailData = emailDataSheet.getRange("A2:A").getDisplayValues().flat().map(po => po.substr(0,3));
Logger.log(emailData)///Working to get the first 3 charcters in column A
var subject = po_numID + " Po Log Daily Notification "; // Unique PoTitle of the email
var options = {} // Using the html body for the email
options.htmlBody = "Hi All, " + "The following" + '<a href=\"' +url+ '" > Purchase Orders </a>' + "are over 90% spent" + "";
for(var i = 0; i < data.length; i++){
let row = data[i];
if( statusesToEmail.includes(row[1]) & (row[2] >= .80)){
emailData.every((po, index) => {
if (po == po_numID){
const email = emailDataSheet.getRange(index + 2,7).getValue();//Getting the last colmun on the same row when the Po# are the same.
console.log(email);
MailApp.sendEmail(email, subject, '', options); // Sending the email which includes the url in options and sending it to the email address after making sure the first 3 Charcters Of the PO_log are the same as
return false;
} else {
return true;
}
});
}
}
}
here is the spreadsheet
https://docs.google.com/spreadsheets/d/1QW5PIGzy_NSh4MT3j_7PggxXq4XcW4dCKr4wKqIAp0E/edit#gid=611584429
you have to use the break function if u wish to stop the loop once the loop has been fulfiled, because if im not wrong , the email is sent if the IF condition is met , thus in the block that has mailapp.sendemail , you have to add in a break otherwise the loop will keep on happening. this is the basic of javascript and you should read up more about the FOR loop here
break as in just type "break" at the end of the code so the script will not continue to loop once the condition has been met.

Google Sheet - Send Email

I need my google sheet to send emails every time a condition becomes true.
In this case, every time value of C2 is lower than value of J2.
On the cell L2 there is the email address.
The code (found online and just edited)
function CheckPrice() {
var LastPriceRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts").getRange("C2");
var LastPrice = LastPriceRange.getValue();
var EntryLimitRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts").getRange("J2");
var EntryLimit = LastPriceRange.getValue();
var StockNameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts").getRange("B2");
var StockName = LastPriceRange.getValue();
// Check totals sales
if (LastPrice < EntryLimit){
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts").getRange("L2");
var emailAddress = emailRange.getValues();
// Send Alert Email.
var message = 'Ticker ' + StockName + ' has triggered the alert';
var subject = 'Stock Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
With this code I don't receive any error, but I don't even receive the email.
I granted permissions as requested when I run the script for the first time.
On L2 I put the same email address I granted permission (I send email to myself).
I did a try even putting a secondary email address I have.
Can you please show me what's wrong ?
Issues:
See the first lines of your code where you define LastPrice,
EntryLimit and StockName. All of them are coming from the same
range: LastPriceRange.
I also removed all the unnecessary calls in your script. There is no
need to call
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts") so
many times when you can just put it in a variable and use that
variable instead.
Also, you don't need to define unnecessary
variables. For example, you can get the value of a cell with one line:
sh.getRange("B2").getValue().
Solution:
function CheckPrice() {
const sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts");
const LastPrice = sh.getRange("C2").getValue();
const EntryLimit = sh.getRange("J2").getValue();
const StockName = sh.getRange("B2").getValue();
// Check totals sales
if (LastPrice < EntryLimit){
// Fetch the email address
const emailAddress = sh.getRange("L2").getValue();
// Send Alert Email.
const message = 'Ticker ' + StockName + ' has triggered the alert';
const subject = 'Stock Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
If you want an email to be sent when you edit a particular cell, then you need to transform the aforementioned solution to an onEdit(e) trigger. If you want a time basis trigger then you can just use the solution above directly.

How to trigger google sheets function on specific cell change (or triggered by time)

I'm trying to trigger a function (send email) when a cell changes on sheets.
At present, my code looks like this.
function RealtimeFeedbackdue() {
var remainingHoursRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("F5");
var remainingHours = remainingHoursRange.getValue();
var studentNameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("B1");
var studentName = studentNameRange.getValue();
// Check total hours
if (remainingHours < 6){
// Fetch the email address
var emailAddressRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("B5");
var emailAddress = emailAddressRange.getValue();
var emailAddressRange2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("B6");
var emailAddress2 = emailAddressRange2.getValue();
// Send Alert Email for teacher.
var message = studentName + ' has ' + remainingHours + ' hours left of their course. This means their realtime feedback report is due. ' +
'This is an automatically generated email.' ;
var subject = 'Realtime feedback report due for ' + studentName;
MailApp.sendEmail(emailAddress, subject, message);
// Send Alert Email for course consultant.
var message2 = studentName + ' has ' + remainingHours + ' hours left of their course. Their teacher will be completing the realtime feedback report this week. Please speak to them regarding course renewal etc. This is an automatically generated email.' ;
var subject2 = 'Private student ' + studentName + ' has nearly completed their hours';
MailApp.sendEmail(emailAddress2, subject2, message2);
}
}
I would settle for having a weekly trigger but I can't work out how to program that either. I've tried this.
function RealtimeFeedbackdue() {
ScriptApp.newTrigger('RealtimeFeedbackdue')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
var remainingHoursRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("F5");
var remainingHours = remainingHoursRange.getValue();
var studentNameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("B1");
var studentName = studentNameRange.getValue();
// Check total hours
if (remainingHours < 6) {
// Fetch the email address
var emailAddressRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("B5");
var emailAddress = emailAddressRange.getValue();
var emailAddressRange2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attendance").getRange("B6");
var emailAddress2 = emailAddressRange2.getValue();
// Send Alert Email for teacher.
var message = studentName + ' has ' + remainingHours + ' hours left of their course. This means their realtime feedback report is due. ' +
'This is an automatically generated email.';
var subject = 'Realtime feedback report due for ' + studentName;
MailApp.sendEmail(emailAddress, subject, message);
// Send Alert Email for course consultant.
var message2 = studentName + ' has ' + remainingHours + ' hours left of their course. Their teacher will be completing the realtime feedback report this week. Please speak to them regarding course renewal etc. This is an automatically generated email.';
var subject2 = 'Private student ' + studentName + ' has nearly completed their hours';
MailApp.sendEmail(emailAddress2, subject2, message2);
}
}
}
Since you want to trigger the execution of the function based on an edit of the cell, the most appropriate trigger in this situation is the onEdit one.
Therefore, you can use something similar to this:
Snippet
function onEdit(e){
var row = e.range.getRow();
var col = e.range.getColumn();
var sheet = e.range.getSheet();
if (sheet.getName() == 'NAME_OF_THE_SHEET' && row == NUMBER_OF_ROW && col == NUMBER_OF_COL)
// if the edit was made in the wanted cell
// add the instructions here
}
Explanation
The above function makes use of the e event in object in order to get the data needed about the edited cell.
Snippet 2
However, you can also use a time-based trigger, but this won't have anything to do with the edited cell - it will just trigger every Monday at the time you chose.
function theTimeTrigger() {
ScriptApp.newTrigger('RealtimeFeedbackdue')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
Explanation
For this, a new function was needed in order to create the time-based trigger and associate it to the function you already have.
Create triggers from the UI
You can also create triggers using the Apps Script's UI. This can be done by clicking this icon which will redirect you to the current project's triggers.
Afterwards, you can click this button in order to create a new trigger and you customize it in the way you want.
Reference
I also suggest you to take a look at these links below since they might be of help to you in your future development.
Event Objects Apps Script;
Installable Triggers Apps Script;
ClockTriggerBuilder Class Apps Script.