Set Value on a DIFFERENT Sheet in Google Apps Script - google-apps-script

I want to do a variation on the "Sending emails from a Spreadsheet" tutorial on the Google Developers page.
I want to do exactly what they walk me through EXCEPT that when it comes to marking the cell in each row every time the e-mail is sent, I want to mark that on a SEPARATE sheet.
So, on the sheet "NeedReminders", my script finds the e-mail addresses of members to whom the e-mail should be sent in column A (which then becomes variable "emailAddress"). It sends the e-mail to that address.
THEN--and here's where I need help--I want the script to go to the "Master" sheet, find the row where emailAddress is in column X, and--in that same row--set the value of column BT.
Shouldn't be difficult. I just don't necessarily know how to do the "find the row where columm X equals emailAddress and set value of BT to today's date" part.
Who can help? Thanks in advance!
Here's my code so far (I'm always embarrassed to show my code because I'm sure it's terrible juvenile, so be gentle, please!):
function sendDuesReminder() {
var sheet = SpreadsheetApp.openById('___').getSheetByName('NeedReminders');
var startRow = 4; // There are a bunch of headers and other nonsense.
var valueSheet = SpreadsheetApp.openById('___').getSheetByName('Master');
// Determine how many rows to process
var rowRange = sheet.getRange(3,4); // On my sheet, cell D3 counts how many members are on the list of people who need a reminder e-mail.
var rowData = rowRange.getValue();
// End Determine how many rows to process
// Send e-mail
var dataRange = sheet.getRange(startRow, 1, rowData, 2);
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
// var emailAddress = row[0]; //Column A: E-mail Address
var name = row[1]; //Column B: First Name
var subject = "Reminder About Dues for Newcomers & Neighbors";
MailApp.sendEmail("matt#twodoebs.com", subject, "Hi, " + name + ". It's been some time since we received your online registration for Newcomers & Neighbors. But we don't seem to have received your dues payment, yet. So we wanted to drop you a quick line and remind you about it." + "\n\n" + "If you think we should have received your payment by now or there's some kind of a mistake, please let us know by responding to this message." + "\n\n" + "Otherwise, please send a check for __ made payable to ___ to:" + "\n\n" + "___" + "\n" + "___" + "\n" + "___" + "\n" + "___" + "\n\n" + "Thanks! Please don't hesitate to reach out if you have any questions." + "\n" + "___" + "\n" + "___" + "\n\n" + "Best wishes," + "\n\n" + "Kati" + "\n" + "Membership Director",
{name:"___"});
// End Send E-Mail
// Set that an a-(Experimental)
var valueRange = valueSheet.getRange // Here's where I kind of break down . . . I need *something* here that searches the sheet for emailAddress
setValue(today()) //I'm also not sure that this is the right way to set the value as today's date
SpreadsheetApp.flush();
// End Set value
}
Browser.msgBox("OK. Reminder messages have been sent. doebtown rocks the house!")
}

If you're still struggling, try this:
var emails = valueSheet.getRange('X:X').getValues();
var targetRange = valueSheet.getRange('BT:BT');
var dates = targetRange.getValues();
for (var j=0; j<emails.length; ++j) {
if (emails[j][0] == emailAddress) {
dates[j][0] = new Date();
break;
}
}
targetRange.setValues(dates);

Related

Fixing a trigger

I need a trigger (or two) and I'm doing something wrong. Or maybe I need to tweak my code.
I want an email to send automatically when I update a line in my spreadsheet. The three columns that update are notes, status, and resolution. Right now, in my test version, it sends an email as soon as I change any one of those columns. Which is fine, if I'm only updating one thing. But if I want to add a note, change the status, and enter a resolution all at once, it sends three separate emails. My first trigger for sending an email upon form submission works great.
Here is my code. Any help would be appreciated
PS: I'm sure there are cleaner ways to do this, but I'm familiar with this from the past. It's just been long enough that I don't remember all of it.
function formSubmitReply(e) {
var userEmail = e.values[3];
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
// Set the status of the new ticket to 'New'.
// Column F is the Status column
sheet.getRange("F" + lastRow).setValue("New");
// Calculate how many other 'New' tickets are ahead of this one
var numNew = 0;
for (var i = 2; i < lastRow; i++) {
if (sheet.getRange("F" + i).getValue() == "New") {
numNew++;
}
}
MailApp.sendEmail(userEmail,
"Helpdesk Ticket #" + lastRow,
"Thanks for submitting your issue. \n\nWe'll start " +
"working on it as soon as possible. You are currently " +
"number " +
(numNew + 1) + " in the queue. \n\nHelp Desk.",
{name:"Help Desk"});
}​
function emailStatusUpdates() {
var sheet = SpreadsheetApp.getActiveSheet();
var row = sheet.getActiveRange().getRowIndex();
var userEmail = sheet.getRange("D" + row).getValue();
var subject = "Helpdesk Ticket #" + row;
var body = "We've updated the status of your ticket.\n\nStatus: " + sheet.getRange("F" +
row).getValue();
body += "\n\nNotes: " + sheet.getRange("E" + row).getValue();
body += "\n\nResolution: " + sheet.getRange("G" + row).getValue();
MailApp.sendEmail(userEmail, subject, body, {name:"Help Desk"});
}
function onOpen() {
var subMenus = [{name:"Send Status Email", functionName: "emailStatusUpdates"},
{name:"Schedule Appointment", functionName: "scheduleAppointment"},
{name:"Push to KB", functionName: "pushToKb"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Help Desk Menu", subMenus);
}​
It looks that you are using an on edit trigger to send and email that works OK when you only need to edit one cell but when you need to edit two or more the email should be sent until you ended to edit all the related cells.
One way to achieve the desired behaviour is to add one or more conditions to send the email.
One way to implement the above is to add an auxiliary column to set if the email should be sent inmediately or not. What about calling this "Hold" and add a checkbox?
This is just is a brief example of how to implement the above as an installe on edit trigger
/**
* Columns C,D,E are notes, status and resolution respectively
* Column F is Hold
*/
function sendUpdate(e){
if(e.range.columnStart > 2 && e.range.columnStart < 6 || e.range.columnStart === 6){
var hold = e.range.getSheet().getRange(e.range.rowStart, 6).getValue();
if( hold !== 'TRUE'){
// Call here your send email function
} else {
// Ask if the email should be sent now
}
}
}

Google Apps Script to send email notification, if the email from an expected sender is not received every 1 hour

I have managed to get email notification alert if I have not received an expected email in past XX day.
By following the guidelines given in - http://baumbach.com/google-script/
It is working as expected.
Here is the javascript code-
function SearchEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
// "in:all " searches trash and spam email folders. Remove "in:all " + to only search all mail and inbox
var gsearch = "in:all " + "from:(" + data[i][1] + ") to:(" + data[i][2] + ") subject:(" + data[i][3] + ") newer_than:" + data[i][4] + "d";
var threads = GmailApp.search(gsearch, 0, 1);
if (threads.length == 0) {
var emailSubject = "No email in " + data[i][4] + " days: - " + data[i][5];
var emailText = "Note: " + data[i][5] + "\r\n\r\nSearch was: " + gsearch;
var emailTo = data[i][0];
MailApp.sendEmail(emailTo, emailSubject, emailText);
}
}
}
But, What I want is, to get email notification alert if I have not received an expected email in past 1 Hour and not days.
Can anyone tell me how to do this and where should I change the code??
Thanks a lot in advance.
EDIT
In the guide from the link you provided and in your code, the date used to query the messages from Gmail comes from the linked spreadsheet you have, so this part would be set by you.
To query the message the code uses the gsearch variable which is formatted by the q parameter guidelines using the Gmail search operators and inserting data in the query from the data variable which is obtained from using the getDataRange and getValues methods to your sheets. For what I see in the search operators, there's no option to query the messages by time, the minimum is 1 day.
Also, they explain they set up a time driven trigger to execute the SearchEmail function once a day. For this part you can use the everyHours method to create a trigger to execute the function once every hour. Run the below function once in order to create the trigger:
function createTimeTriger1hour() {
ScriptApp.newTrigger("SearchEmail")
.timeBased()
.everyHours(1)
.create();
}

I would like to populate an email message with the contents of a specific, static, cell in sheets

I am sending a confirmation email when someone signs up for an appointment on a Google form. I would like to have the email message be populated with the contents of a specific cell. This means I would be able to type an email message into cell A2 and when someone fills out a form, it will send that message to them as the confirmation email.
I have two different email messages I am using, confirmation email and reminder email. I would like both codes to populate the same message.
function confirmationEmail(e) {
var userName = e.values[2];
var userEmail = e.values[3] + ";user#gmail.com";
var date = e.values[4];
var studentName = e.values[1];
var body1 = "Hello " + userName + ",";
var body2 = studentName + " has been registered for a physical at High School for the following date/time:";
var body3 = "Please park in the large student parking lot on the east side of the High School.";
var body4 = "Thanks!";
var signature = "Name" + "\n" + "High School" + "\n" + "user#gmail.com" + "\n" + "***-***-****";
var file1 = DriveApp.getFilebyId("1WDxic1meHzEGSjybJ2SS1h2MqGAhIAK4");
var file2 = DriveApp.getFilebyId("1v4GQAP8PkTPQRPoYIdsEOMBr2ZvRO1eocsqixyZ42gA");
GmailApp.sendEmail(userEmail,
"Registration",
body1 + "\n\n" + body2 +"\n\n" + date + "\n\n" + body3 + "\n\n" + body4 + "\n\n" + signature,
{attachments:[file1, file2]})
}
This code works perfectly already, however, I have some co-workers even less savvy than I. It would work best if they could just fill in the cell with the contents of the message to be able to send this out. So ideally, "body3" would be written into a cell within sheets and populated into the email.
So I guess you would want something like this:
var body3 = SpreadsheetApp.getActiveSheet().getRange('A2').getValue();
You should probably include some conditional statements in the code to prevent the sending of the email in the event that cell 'A2' is blank and include an alert to inform the user that it needs to be filled in.
Perhaps something like this:
if(body1 && body2 && body3){
//send email
}else{
SpreadsheetApp.getUi().alert('Invalid or Missing Content');
}
Truthy

Google Forms Script Editor to send emails based on answer

I use a Google Form to track employee hours and jobs completed for a mechanical services small business. The guys want to be able to get an email of their responses to track their own hours and ensure they put everything in correctly. I don't want them to have to enter their email every time or log in - they complete this on their phones and want to keep it as simple and less repetitive as possible.
I've looked on a lot of places around here and have (what I think) is the basis of a good code in Google Script Editor. I had it working to send an automated email, but when I added the information in the message it didn't work. I used some other examples out there and tried to put a few together to make it work. I'd appreciate any help critiquing this code and helping me figure out why it isn't working. I'm new to this and can't seem to figure it out. Thanks.
function EmailTimeSheetCMS(e){
var name = e.values [1];
var ReplyEmail = "__________#yahoo.com"
var Email1 = "__________#gmail.com"
var WorkOrder = e.values[2];
var date = e.values[3];
var location = e.values[4];
var jobdescription = e.values[5];
var notes = e.values[6];
var vendors = e.values[7];
var starttime = e.values[8];
var endtime = e.values[9];
var otherworkers = e.values[10]
var status = e.values [11];
var reghrs = e.values[12];
var othrs = e.values[13];
var tools = e.values[15];
var message = "You entered the following information" + "\nDate " + date + "\nWork Order # " + WorkOrder + "\nLocation " + location + "\nStart Time" + starttime + "\nEnd Time " + endtime + "\nRegular Hours worked" + reghrs + "\nOvertime Hours " + othrs + "\nJob Description " + jobdescription + "\nNotes " + notes + "\nVendors and Invoices " + vendors + "\nOther Workers Present " + otherworkers + "\nTools Used " + tools + "\nJob Status " + status ;
if(name = 'Bill')
{MailApp.sendEmail(Email1, ReplyEmail, "CMS Time Submission - Bill", message) }
else if(name = 'Scott')
{MailApp.sendEmail(ReplyEmail, ReplyEmail, "CMS Time Submission - Scott", message)
} }
On a first glance I see a potential flaw.
In the if condition i.e when you say
if (name = 'Bill')
it assigns 'Bill' to the variable name.
could you try writing it as
if(name == 'Bill')
and see if it works.
And when posting a question next time, it is always a good practice to enter the error message you get. Or at least the 'Execution transcript' available under the View in menu bar

Can my script be edited to ignore invalid email recipients in Send.email?

Im running a script to help a group keep track of translations. The goal is to have the translator notified (emailed) when a document is updated (each language has it's own sheet within the doc). Each recipients email is listed in it's own cell in the row of the document they are responsible for translating. The script will run occasionally, but I think it is getting caught up on cells that have text (not emails) listed where email addresses should be.
I keep getting the error: 'Failed to send email: no recipient (line 21, file "Translation Notification")'
Is there a way to update my script so that it will invalid email recipients?
function shellFunction() {
var sheets = ['Arabic', 'Portuguese', 'French','Spanish'];
for (var s in sheets) {
toTrigger(sheets[s]);
}
}
function toTrigger(sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sheetName);
var cell = ss.getActiveCell().getA1Notation();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
var recipients = sheet.getRange('J' + sheet.getActiveCell().getRowIndex()).getValue();
var message = '';
if (cell.indexOf('B') != -1)
{
message = sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'The ' + sheet.getRange('F' + sheet.getActiveCell().getRowIndex()).getValue() + ' ' + sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue() + ' needs your Translation';
var body = sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue() + ' has been updated. Can you please update ' + sheet.getRange('G' + sheet.getActiveCell().getRowIndex()).getValue() + '? Please remember to update the date column in the Resource Document when the translation is complete:' + ss.getUrl();
MailApp.sendEmail(recipients, subject, body);
}
EDIT: I realized Im looking for the function to ignore all invalid emails (not just blank cells) and continue to run.
You can change
if (cell.indexOf('B') != -1)
{
message = sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue()
}
var subject = 'The ' + sheet.getRange('F' + sheet.getActiveCell().getRowIndex()).getValue() + ' ' + sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue() + ' needs your Translation';
var body = sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue() + ' has been updated. Can you please update ' + sheet.getRange('G' + sheet.getActiveCell().getRowIndex()).getValue() + '? Please remember to update the date column in the Resource Document when the translation is complete:' + ss.getUrl();
MailApp.sendEmail(recipients, subject, body);
To:
if (cell.indexOf('B') != -1 && recipients != "")
{
message = sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue()
var subject = 'The ' + sheet.getRange('F' + sheet.getActiveCell().getRowIndex()).getValue() + ' ' + sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue() + ' needs your Translation';
var body = sheet.getRange('A' + sheet.getActiveCell().getRowIndex()).getValue() + ' has been updated. Can you please update ' + sheet.getRange('G' + sheet.getActiveCell().getRowIndex()).getValue() + '? Please remember to update the date column in the Resource Document when the translation is complete:' + ss.getUrl();
MailApp.sendEmail(recipients, subject, body);
}
EDIT
You can use any email regex that works best for you but something like this should work:
var recipients = sheet.getRange('J' + sheet.getActiveCell().getRowIndex()).getValue().toString();
var getEmails = recipients.match(/([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi);
Now you have an array of hopefully valid emails that you can cycle through and use try/catch if there are still errors.
Note: no email regex is perfect
Replace:
MailApp.sendEmail(recipients, subject, body);
with:
// Just in case there is blank space in the cell, replace it.
recipients = recipients.replace(/\s/g, '');
// Ensure recipients is non-empty.
if (recipients) {
MailApp.sendEmail(recipients, subject, body);
}
If you want you can simply do a try...catch. So you would have
try {
MailApp.sendEmail(recipients, subject, body);
}
catch(error) {
Logger.log(error)
}
which should go through typos and empty rows. It will still try to send the message, so in terms of performance it's not great, but this should then continue on with the code despite the error (you can do something else with the catch, like add the row where the error occurred, or the address that is wrong to a string and later send the list to yourself or whoever should check the emails)
EDIT: considering the comment left by asker to the answer.
The other answers so far (including mine) are assuming that you have emails in separate cells and you would iterate over the emails, sending 1 to each. Please clarify your question as what you are looking to do is remove e-mails that have an invalid format from the string.
In that case your best bet (barring actualy email validation, which requires you to have a complete list of absolutely certainly correct emails) would probably be to check the string for format errors. Here is a sample that will remove any email that does not have the correct format (note email addresses that do not exist will still remain and whoever runs the script will recieve a response about a failed mail delivery):
function myFunction() {
var recipients = 'email1#gmail.com,email2gmail.com,email3#gmail.com,email4#gmailcom'
var arr = recipients.split(',')
var arr2 = []
var i;
for (i = 0; i < arr.length; i++){
if (arr[i].indexOf('#') != -1){
if (arr[i].indexOf('#') < arr[i].lastIndexOf('.')) {
arr2.push(arr[i])
}
}
}
recipients = arr2.toString()
return
}
Note, that you can refine the for loop a little to just use a single array instead of 2 by getting rid of the element inside the current array, however for simplicity sake and for it to be faster I wrote the easiest variant. In essence what this does is follows a simple logic:
Does the email have an # symbol? All emails must have one
Is there a . symbol after the # symbol. Again, this does not
consider that the end might be .co or some other typo, only that
it exists. You could just check for a full blown #gmail.com or
other domain, if you are absolutely certain all emails will have
that domain.
Finally we combine the array back into the string (you can technically get away with just reusing the 1 recipients variable if you really want to). I personally think the code will look a bit messier if you do though.
Again, note that there are limitations here and you may need to add extra conditions. For example just off the top of my head I did not consider any checks for the following scenarios:
Email address does not have anythi
ng after the last dot (e.g. email#gmail.)
Email does not contain
empty domain (email#. or email#.com) though I am unsure if gmail
will fail on those