SendEmail duration / Count per week in Googlsheet AppScript - google-apps-script

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
}
}

Related

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 email reminders for Google Sheet tasks

Updated the code based on suggestions below, The email does not contain the summary, any help fix this would be appreciated! The test file is attached below,
function sendEmail(){
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("2021-12 {3600950}").activate();
var ss =
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//data on sheet, filter for any row where the status is still "assigned"
var data = ss.getDataRange().getValues()
var assigned = data.reduce(function(acc, curr) {
if(curr[5] === "assigned") {
acc.push(curr)
}
return acc
},[])
// unique list of emails
var Assignee = [0]
var Summary = [2]
var compareAssignee = []
for (i=0; i<assigned.length; i++){
compareAssignee.push(assigned[i][0])
}
//loop unique emails, all the tasks for each of those users, and send a simple email with the tasks
for (var i=0; i<Assignee.length; i++){
var Summary = assigned.reduce(function(acc, curr) {
if(curr[0] === Assignee[i])
{
acc.push(String.fromCharCode() + "pending task: " + curr[2] +
Summary[2])
//this puts a line break before each task, so when the email is
sent each one is on its own line.
}
return acc
},[])
console.log(Summary)
MailApp.sendEmail
MailApp.sendEmail(compareAssignee[0],"pending RPP task(s)",Summary[2])
}
}
function scheduleEmails(){
// Schedules for the first of every month
ScriptApp.newTrigger("sendEmail")
.timeBased()
.onMonthDay(28)
.atHour(1)
.create();
}
You want to send an email at the end of every month to users who have tasks that are still in "assigned" status.
The sendEmail script below finds all the tasks for each user and sends an email to them listing each of their tasks that are still "assigned". EDIT: You indicated in a comment above that emails are in Col 1, tasks are in Col 3 and status is in Col6. I updated the code to reflect that below.
Check out this sample email to see the results.
The second function creates a trigger that runs sendEmail every month. You indicated you wanted to send the email on the last day of the month, but it seems Google has a hard time with that. Some other folks came up with workarounds. I like this one: send the reminder on the 1st of the month, but at 1 in the morning! You can see more of their work here
function sendEmail(){
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("status").activate();
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//grab all the data in the sheet, and then filter for any row where the status is still "assigned"
var data = ss.getDataRange().getValues()
var assigned = data.reduce(function(acc, curr) {
if(curr[5] === "assigned") {
acc.push(curr)
}
return acc
},[])
// From all the tasks still in "assigned" status, get a unique list of emails.
var uniqueEmails = []
var compareEmails = []
for (i=0; i<assigned.length; i++){
compareEmails.push(assigned[i][0])
}
uniqueEmails = [...new Set(compareEmails)]
//loop through the unique emails, grab all the tasks for each of those users, and send a simple email with the tasks listed.
for (var i=0; i<uniqueEmails.length; i++){
var tasksPerUser = assigned.reduce(function(acc, curr) {
if(curr[0] === uniqueEmails[i]) {
acc.push(String.fromCharCode(10) + "pending task: " + curr[2]) //this puts a line break before each task, so when the email is sent each one is on its own line.
}
return acc
},[])
console.log(tasksPerUser)
MailApp.sendEmail(uniqueEmails[i],"pending tasks",tasksPerUser)
}
}
function scheduleEmails(){
// Schedules for the first of every month
ScriptApp.newTrigger("sendEmail")
.timeBased()
.onMonthDay(1)
.atHour(1)
.create();
}

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.

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

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)

Create timestamp with script editor when email is sent by Google Spreadsheet

I am currently receiving data through an RSS feed into my spreadsheet. I have a script (below) which I am hoping to run on a time trigger (every minute) to check the data and then in turn send an email out if the data meets certain criteria.
Problem: After the first email is sent out, I want to create a system to check (timestamp?) within the script if an email for that particular row of data has already been sent. Is this the best way to do this? How do I do it? Script is below.
function sendEmail(email_address, email_subject, email_message) {
MailApp.sendEmail(email_address, email_subject, email_message);
}
function test_sendEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.setActiveCell('A2');
var criterion_cutoff = 5;
var i = 0;
var addr;
var subj;
var msg;
do {
addr = cell.offset(i,0).getValue();
subj = cell.offset(i,1).getValue();
msg = cell.offset(i,2).getValue();
criterion = cell.offset(i,3).getValue();
if(criterion < criterion_cutoff) {
sendEmail(addr,subj,msg);
Browser.msgBox('Sending email to: ' + addr);
}
i++;
} while( cell.offset(i, 0).getValue().length > 0 )
Browser.msgBox('Done!');
}
If I'm understanding your question correctly, you want some way of marking which rows in the spreadsheet that you've already sent emails for, so that you don't send duplicates. If so, then this tutorial shows one way of doing that: https://developers.google.com/apps-script/articles/sending_emails#section2