Google Script maker to format a spreadsheet - javascript

This is my first time posting so bear with me if I leave any crucial details out.
Anyway, to summarize the problem: I have been trying to get a script to work on Google Script maker to format a spreadsheet which is hooked up to a form, to go straight to my email.
So basically User Form --> Spreadsheet --> My Email
The questions are pretty Standard:
What's the problem?
Where are you located
However the one question I'd like to use is "What is the priority of this problem?" High or low. I have it under multiple choice format so its a simple choice.
Psuedocode for what I want:
if (priority = low) put #priority low onto the email
Simple enough, however I can't seem to get it to work, here's my code:
function sendFormByEmail(e)
{
// Remember to replace XYZ with your own email address
var email = "email";
var subject = "Help Desk Form Submitted";
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
var message = "";
var priority = "";
if(message.indexOf("What is the priority of this problem? = Low")){
priority += "#priority low";
}
else
priority == "GFHHFFHAHFH ";
for(var i in headers){
message += headers[i] + ' = \t \t'+ e.namedValues[headers[i]].toString() + "\n\n"; }
if (message.indexOf("What is the priority of this problem? = Low"))
message += "This is a test";
else
message += "This is not a test";
MailApp.sendEmail(email, subject, message);
}

Let's look at the first instance of if(message.indexOf().... It's got some problems:
A few lines earlier, message was set to an empty string... so you won't find the "priority" string in it.
The if statement is treating the return of indexOf() as a Boolean. However, the return code from .indexOf() is -1 when an item is not found, which is "true-ish". If the string IS found, and is located at the start of the search subject, the return will be 0, which is "false-ish". If it is found at any other location, the value will be `>0', also "true-ish".
In the else, there's a typo. The comparison == should be assignment =, or += if you prefer.
Looking at the surrounding code, this piece looks like it was left-over from a previous version, and can be deleted.
Now look at the second instance.
The message should now be populated. However, the comparison is still using an incorrect Boolean interpretation of .indexOf().
The search string contains a batch of spaces... but the previous code that looped through responses used tabs to separate the "header" from the "value", so the search will always return '-1' (which will be interpreted as true).
There are a couple of other tidy-up items. You probably need:
function sendFormByEmail(e)
{
Logger.log(JSON.stringify(e));
var email = Session.getEffectiveUser().getEmail();
var subject = "Help Desk Form Submitted";
var message = "";
var s = e.range.getSheet(); // Sheet that received form response
var headers = s.getDataRange().getValues()[0];
for (var question in headers) {
message += headers[question] + ' = \t\t' + e.values[question] + '\n\n';
}
// Add text relative to the priority of the reported issue.
if (e.namedValues["What is the priority of this problem?"].toString() === "Low")
message += "This is a test";
else
message += "This is not a test";
Logger.log(message);
MailApp.sendEmail(email, subject, message);
}
PS: You can see why it would be advisable to keep form questions short, like "Priority", and leave the explanatory sentence to be helper text!

Related

How can I perform a function for every email with a specific label?

I have daily reports sent to me by email with the label "Automated Reports". I have a function that i would like to run for every email with that label, so i wrote the following code to get all emails with that label.
function processReportEmails() {
var label = GmailApp.getUserLabelByName("Automated Reports");
var threads = label.getThreads();
var messages = threads[threads.length - 1].getMessages();
for (var i = threads.length - 1; i >= 0; i--){
var msg = messages[messages.length - 1];
/perform my function with the email here using msg
threads[threads.length - 1].removeLabel(label).refresh();
}
}
the issue is, it ends up performing the custom function on the same email for the amount of emails there are with that label. how can I make this work for each email, eventually ending up with no more items with that label?
(i am kind of new to this stuff, i assume its a simple syntax answer. also while searching for an answer there were many questions with the same goal as mine, but none solving this specific issue)
I agree with #Rubén answer, that change will remove the label from all the messages.
However, I'll like to provide a different approach. I reviewed this code with a Friend (#Juan Serrano JKL ), and this sample code will fetch all the messages inside with those labels, not just the first one, and also will delete the label from the messages that were already fetched. You can change the code as need it.
Here is the sample code:
function iterateEmails() {
const labelName = 'testing';
var label = GmailApp.getUserLabelByName(labelName);
var threads = label.getThreads();
// This logs the amount of threads with that label,
console.log("There are " + threads.length + " threads in total with the label: " + labelName);
// This complete a loop to get all the messages inside those threads.
threads.forEach( function(thread){
var messages = thread.getMessages();
// This logs provide you information on how many emails are in each thread.
// and prints the number of messages.
console.log("The threadId: '" + thread.getId() + "' contains " + messages.length + " messages");
console.log("Printing found messages:");
messages.forEach( function(message){
// Currently, it exports the information to the console.
// However, you can replace this with what you want to do with the message once it's fetched.
var messageBody = message.getPlainBody().substring(0,50);
var messageTitle= message.getSubject();
// prints in the console the subject, title and the body of the messages.
console.log("Title: %s\n\tBody: %s", messageTitle, messageBody);
});
// And the last one removes the the labels of the information that was exported.
console.log("Removing the label from the threadId: '" + thread.getId());
thread.removeLabel(label);
});
}
**Reference: **
getThreads
getMessage
Replace
threads[threads.length - 1].removeLabel(label).refresh();
by
threads[i].removeLabel(label).refresh();

How to write (SetValues) to a Google Sheet using a filtered forEach loop?

I've been trying for hours to make the following Google Apps Script work. What it needs to do, is send emails (from an html-template) to anyone that:
has a complete Event Schedule (which is completed if they have been
assigned to at least 4 events, which is counted in column Q);
has NOT been sent an email earlier (which is kept track of in column
R);
The script keeps track of errors in column S, i.e. if there's no email address provided.
It appears it only works:
if I comment out
data = data.filter(function(r){ return r[17] == true & r[16] > 3});
or if I comment out
ws.getRange("S3:S" + ws.getLastRow()).setValues(errors);
ws.getRange("R3:R" + ws.getLastRow()).setValues(mailSucces);
How can I get this script to work properly?
A copy of the Google Sheet I'm referring to is this one:
https://docs.google.com/spreadsheets/d/1sbOlvLVVfiQMWxNZmtCLuizci2cQB9Kfd8tYz64gjP0/edit?usp=sharing
This is my code so far:
function SendEmail(){
var voornaam = 3;
var achternaam = 4;
var email = 5;
var event1 = 9;
var event2 = 10;
var event3 = 11;
var event4 = 12;
var event5 = 13;
var event6 = 14;
var event7 = 15;
var emailTemp = HtmlService.createTemplateFromFile("email");
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Events Day 1");
var datum = ws.getRange(1,3).getValue();
var spreker = ws.getRange(1,6).getValue();
var data = ws.getRange("A3:R" + ws.getLastRow()).getValues();
data = data.filter(function(r){ return r[17] == false && r[16] > 3}); //Either this needs to be commented out...
let errors = [];
let mailSucces = [];
data.forEach(function(row){
try{
emailTemp.voornaam = row[voornaam];
emailTemp.email = row[email];
emailTemp.datum = datum;
emailTemp.spreker = spreker;
emailTemp.event1 = row[event1];
emailTemp.event2 = row[event2];
emailTemp.event3 = row[event3];
emailTemp.event4 = row[event4];
emailTemp.event5 = row[event5];
emailTemp.event6 = row[event6];
emailTemp.event7 = row[event7];
var htmlMessage = emailTemp.evaluate().getContent();
GmailApp.sendEmail(
row[email],
"Here you go! Your personal schedule for the event of " + datum,
"Your emailprogramm doesn't support html.",
{
name: "Event Organisation Team", htmlBody: htmlMessage, replyTo: "info#fakeemail.com"
});
errors.push([""]);
mailSucces.push(["TRUE"]);
}
catch(err){
errors.push(["Error: no message sent."]);
mailSucces.push(["False"]);
}
}); //close forEach
ws.getRange("S3:S" + ws.getLastRow()).setValues(errors); //or this and the next line need to be commented out.
ws.getRange("R3:R" + ws.getLastRow()).setValues(mailSucces);
}
Edit I have been trying and thinking en trying... but still haven't found out how to make it work. But I also got understanding of why it's not working; I just don't know how to get it fixed.
Let me elaborate on the problem a bit more:
The problem is, that within the forEach loop the range is a filtered variant of the data, pulled from the spreadsheet with getValues. Therefore, writing data back with ws.getRange("R3:R" + ws.getLastRow()).setValues(mailSucces); results in mismatched checkmarks in te spreadsheet.
So, somehow I need to put the range of the previous used filter data = data.filter(function(r){ return r[17] == false & r[16] > 3}); in a variable...? I guess?
Furthermore, I don't think it's wise to use setValue within the loop, because (from what I understand from my searching on the topic) this results in a slow script, because every loop the script makes an API call to write in the spreadsheet. Hence the errors.push and mailSucces.push, and my attempt to do a setValue at the end, after the loop is finished.
Can someone help me to finish this problem?
The problem is different size of the range you write to and data you are writing in.
Try replacing:
ws.getRange("S3:S" + ws.getLastRow()).setValues(errors);
ws.getRange("R3:R" + ws.getLastRow()).setValues(mailSucces);
With:
ws.getRange(3, 19, errors.length, 1).setValues(errors);
ws.getRange(3, 18, mailSucces.length, 1).setValues(mailSucces);
You should use this variation of getRange
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangerow,-column,-numrows,-numcolumns
Your data has non-fixed number of rows and fixed number of columns (1). In general case your data will be matrix of X rows and Y columns. For that purpose you can make it completely dynamic:
sheet.getRange(startRow, startColumn, data.length, data[0].length)
Just make sure data.length is > 0 before you do this, otherwise data[0].length will break.
Edit:
I started writing a comment but it got too long. There are couple of things that may go wrong with sending emails. First thing I noticed is that you use & in filter, but in AppsScript/JavaScript/C-like-languages, you should use && for logical AND. Now the email: you only detect the code break with the catch block. At this point you don't know why the code breaks it could be anything. With GmailApp I recommend you to use createDraft while developing, then when all ok replace it with sendEmail for the final version, both functions have the exact same parameters, thank you Google devs ;-).
To find out the exact problem you should get the error message on break and display it. err.stack should tell you pretty much everything:
catch(err){
Logger.log(err.stack); // Added
errors.push(["Error: no message sent."]);
mailSucces.push(["False"]);
}
Run the sendEmail function from the code editor and you should see the Log for each catch(err) pass.

Javascript - using form field values to determine which email address to use

Javascript newbie here, so apologies if this is incredibly basic and not the prettiest coding.
I'm creating a form that I want to have sent to a different email address depending on which team someone selects (e.g. if you select Sales it will generate an email to Team X, Refunds will email Team Y etc).
So far I've got to the stage where I can click a button to generate an email and attach the form as a PDF, but I don't know how to make it variable depending on the field value.
Current code:
var callerName = this.getField("CallerName").value;
var customSubject = this.getField("WhichTeam").value;
//I've used a fake email address for the next line variable, but this is the one I want to change depending on the "WhichTeam" field value
var mailtoUrl = "mailto:Email#email.com?subject=Callback Referral for " + customSubject;
this.submitForm({
cURL: mailtoUrl,cSubmitAs: "PDF"});
Hope this makes sense. Grateful for any help/advice?
Thanks
You can try using an object to contain the emails with the teams as they values. And to also use a template string to insert the value into the mailtoUrl.
var emails = {
refunds: 'refundsTeamEmail',
sales: 'salesTeamEmail',
}
var callerName = this.getField("CallerName").value;
var customSubject = this.getField("WhichTeam").value;
// ex. customSubject value is 'refunds'
// emails[customSubject] would be the same as doing emails.refunds
// which return the value of 'refundsTeamEmail'
var mailtoUrl = `mailto:${emails[customSubject]}?subject=Callback Referral for ` + customSubject;
this.submitForm({
cURL: mailtoUrl,cSubmitAs: "PDF"});
I think this is the simplest way to do it as the value will change each time you run the function to send the email without having to create or call other functions.
Having just said the last thing, you could also use a switch case in another function to return the value. If you want to. Something like:
function fetchEmail(email) {
switch(email) {
case 'refunds':
return 'refundsTeamEmail'
case 'sales':
return 'salesTeamEmail'
default:
return ''
}
}
var callerName = this.getField("CallerName").value;
var customSubject = this.getField("WhichTeam").value;
// ex. customSubject value is 'refunds'
// emails[customSubject] would be the same as doing emails.refunds
// which return the value of 'refundsTeamEmail'
var mailtoUrl = `mailto:${fetchEmail(customSubject)}?subject=Callback Referral for ` + customSubject;
this.submitForm({
cURL: mailtoUrl,cSubmitAs: "PDF"});

How to send mails with some functionality using google apps script

I am new to Google Apps Script. I have a sheet that collects some "Order Number" from form submit. I want to send mails through an event (On form submit) from my spreadsheet. The form will serve an order number. When the form is submitted, it will match the older submitted order numbers throughout the whole column. If it got matched once, the mail won't be sent. If it doesn't match then it will send a mail to the email address next to the order number.
The email address will come from another sheet on the same spreadsheet using VLOOKUP. I managed to do this.
Sorry if I make any mistake with my English.
Edit:
I tried map() , filter() , indexOf() these methods. But I too new with this.
function search(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Copy of orderStatus");
var lr = ss.getLastRow() - 1;
var keyword = ss.getRange("H5").getValue();
var dataSource = ss.getRange(2, 2, lr, 1).getValues();
var mapped = dataSource.map(function(r){
return r[0]});
var showPos = mapped.indexOf(keyword) + 2;
var getMail = ss.getRange(showPos, 4).getValue();
var filted = mapped.filter(filterlogic);
}
var filterlogic = function(r){
if(r !== "zil20200010"){
return true;
} else {
return false;
}
}
On form submit, select the column (range) where you store all the order numbers and create a TextFinder and store it in a variable using the createTextFinder(findText) method for the specified range.
Get the TextFinder from the previous step and search the order number using the findNext() method.
If findNext() returns null then move to the next step. else, do nothing.
Get the email address to which you plan to send the order number.
After having the email address, use the sendEmail(recipient, subject, body, options) method to send the email. If you'd like, you can use HTML in the body to make it more professional.
For additional information, read:
the reference guide on creating TextFinders,
the reference guide on finding text using a TextFinder,
and the reference guide on GmailApp.
Sample code:
// imagine you store all the order numbers in column C, starting from row 2 to the last row in the column:
var emailRecipient = test#test.com;
var ordernumber = 123;
var RangeToSearch = sheet.getRange(2,3,sheet.getLastRow());
var TextFinder = RangeToSearch.createTextFinder(ordernumber);
var found = TextFinder.findNext();
if (found == null) {
MailApp.sendEmail({
to: emailRecipient,
subject: "New Order! Order Number: " + ordernumber,
htmlBody: html
});
}
First of all, thanks to all of you who helped me to reach this point. I found the solution to my problem after some "trial and error". I wanted to limit sending emails.
This code takes the Range. Get its values in an array. I mapped that array to act as a string. Then I added .pop() to that string, it removes our last/newly submitted data in that range. Then I used .includes() method to search my value in the mapped array, and assigned it to a variable called final (just came to my mind). This variable returns true/false depending on search results. If the order number does not exist then it returns false. After that, we set an if statement to execute our mailing function. If our order number does not match and return final as false our mailing function happens. Else it does nothing (means no email sents). And that's it!
Here is the code that solved my problem
function orderStatus(e) {
try {
var theirMail, subject, message;
var ourName, theirName;
var sSheet, orderNum, cosmetics, orderSts, phNum, lr,dataSource, mapped, final;
ourName = "My Company Name";
orderNum = e.namedValues["Order Number"].toString();
sSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("orderStatus");
lr = sSheet.getLastRow() - 1;
dataSource = sSheet.getRange(2, 2, lr).getValues();
mapped = dataSource.map(function(r){
return r[0].toString()});
mapped.pop();
final = mapped.includes(orderNum);
orderSts = sSheet.getRange(sSheet.getLastRow(),1).getValue();
theirMail = sSheet.getRange(sSheet.getLastRow(),4).getValue();
theirName = sSheet.getRange(sSheet.getLastRow(),5).getValue();
phNum = sSheet.getRange(sSheet.getLastRow(),6).getValue();
subject = "Order status notification from " + ourName + " to " + theirName;
if (final == false){
message =
"<div style='text-align: left; padding-left: 30px;'><h2>Dear <b>" + theirName +
",</b></h2><p>Your order no is <b><span style='font-size: 14px;'>" + orderNum +
"</span>.</b> <b><span style='font-size: 14px;'>Your order has been processed.</span>" +
"</b></p><p>We packaged your order and dropped it to the logistics. You will recieve phone call on <b><span style='font-size: 14px;'>" + phNum +
"</span></b> from logistics.<br>Thanks for purchasing from <b><span style='font-size: 14px;'>" + ourName +
"</span></b>.</p><p>Best regards,<br><b><span style='font-size: 14px;'>"+ourName+"</span></b></p></div>"+
"<p style='text-align: center;'><br><b>For further information please visit our facebook page <a href='https://www.facebook.com/' target='_blank' rel='noopener'>"+ourName+"</a>.</b></p><hr />";
textbody = message.replace("<br>", "\n\n");
cosmetics = {name: ourName, htmlBody: message};
MailApp.sendEmail(theirMail, subject, message, cosmetics);
}
}
catch (e) {
Logger.log(e.toString());
}
}

Replace function in javascript is not replacing correct index of regex match

I have a String and I am detecting some 'urls' and 'closing anchor tags' using regex expression. firstly I am using match function which is returning the list of 'urls' and 'closing anchor tags'. further if my first url is matching with the second url then I dont need to do anything and if it is not matching then I need to replace the closing anchor tag with the same urls. here is the string:-
"This message was sent to ${EmailAddress} because you asked us to keep you up to date with the latest news and offers from the company. If you do not wish to receive these emails, please unsubscribe ${optout()}. You can also change your email preferences on our website logging in at 'a tag' class="footer-link" href="https://sample-website">https://sample-website 'closing a tag'. Please do not reply to this email, as we are unable to respond from this email address. If you need support, please visit the Sample Help Center 'closing a tag'.
var regex3 = new RegExp(/<\/a.?>/gm);
var regex4 = new RegExp(/(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|[-A-Z0-9+&##\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|[A-Z0-9+&##\/%=~_|$])/igm);
var closingATag = footerContentPlainText.match(regex3);
var URLList = footerContentPlainText.match(regex4);
if(URLList != null){
for(var j =0,k=0; j<URLList.length; j++,k++){
if(j+1 != URLList.length) {
if(URLList[j] != URLList[j+1]){
footerContentPlainText =
footerContentPlainText.replace(closingATag[j],"<" + URLList[j] + ">");
}
else if(URLList[j] == URLList[j+1]){
j++;
}
}
else{
if(URLaTags != null){
footerContentPlainText = footerContentPlainText.replace(closingATag[k],"<" + URLList[j] + ">");
}
}
}
}
Although I am handing different scenarios for different type of strings but this is the scenario where I am stuck.
I expect the output, where last 'closing a tag' should be replaced by 3rd url i.e "https://support.samplewebsite.com/samplename".
Here I am not able to write a tag in actual format so I have used just text in place of the same
please help

Categories