Checking if an email is valid in Google Apps Script - javascript

I'm using the built-in api for scripting against Google Spreadsheets to send some booking confirmations, and currently my script breaks if someone has filled in an invalid email. I'd like it to just save some data to a list of guests that haven't been notified, and then proceed with looping through the bookings.
This is my current code (simplified):
// The variables email, subject and msg are populated.
// I've tested that using Browser.msgBox(), and the correct column values are
// found and used
// The script breaks here, if an incorrect email address has been filled in
MailApp.sendEmail(email, subject, msg)
According to the documentation the only two methods on the MailApp class are to send emails and check the daily quota - nothing about checking for valid email addresses - so I don't really know what criteria must be fulfilled for the class to accept the request, and thus can't write a validation routine.

If you need to validate email addresses beforehand, create a blank spreadsheet in your drive. Then, run the function below, changing the testSheet variable to point to the spreadsheet you created. The function will do a simple regex test to catch malformed addresses, then check if the address is actually valid by attempting to temporarily add it as a viewer on the spreadsheet. If the address can be added, it must be valid.
function validateEmail(email) {
var re = /\S+#\S+\.\S+/;
if (!re.test(email)) {
return false;
} else {
var testSheet = SpreadsheetApp.openById(arbitrarySpreadsheetInYourDrive);
try {
testSheet.addViewer(email);
} catch(e) {
return false;
}
testSheet.removeViewer(email);
return true;
}
}
regex from How to validate email address in JavaScript?

Stay calm, catch and log the exception and carry on:
try {
// do stuff, including send email
MailApp.sendEmail(email, subject, msg)
} catch(e) {
Logger.log("Error with email (" + email + "). " + e);
}

On the otherhand, avoid Checking email in script and get rid of loses quota or try-catch etc. I used that I got a valid email when user attempt to send an email, by signing him in an email and got that email:
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
String s = account.getEmail(); // here is the valid email.
} catch (ApiException e) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
}
}
Full procedure Here.

This answer is much later than this question was asked, but I piggy-backed off of remitnotpaucity's answer based on a comment in his answer. It does basically the same thing, adding the email to the spreadsheet and catching the error, however in my case it creates a new spreadsheet, attempts to add the user, and then after attempting to add the user, deletes the spreadsheet. In both cases, that the email is a valid email or not, it deletes the newly created spreadsheet.
Some things to note:
I am not as familiar with regular expressions, so I only check to see if the # symbol is within the email read into the function, and do not check for whitespaces.
I believe that even if it passes the first if-statement, even if it's not a valid email, an error will still be thrown and caught because Google will still catch that it's not a valid email, making the first if-statement redundant
If you are trying to validate an email outside your company, I'm unsure how it would react, so be fore-warned about that
This validation method takes a few seconds because you are creating and then deleting an email all within a single function, so it takes a fair bit longer than remitnotpaucity's
Most importantly, if you are able to, I would use an API. I believe that this one would work perfectly fine and should be free, it just may take some extra elbow-grease to get to work with GAS.
function validateEmail(email){
let ss = SpreadsheetApp.openByUrl(SpreadsheetApp.create('Email Validation Spreadsheet', 1, 1).getUrl())
if(!new RegExp('[#]').test(email)){
return false
} else{
try{
ss.addViewer(email)
} catch(e){
setTrashed()
return false
}
setTrashed()
return true
}
function setTrashed(){
DriveApp.getFilesByName('Email Validation Spreadsheet').next().setTrashed(true)
}
}

Related

Login function : Error [ERR_HTTP_HEADERS_SENT]

enter image description here
I'm trying to login as a user on my website and it's giving me this error, I can register the user and search for it in the login as many times as I want if the password and username are correct, however when there is an error in the user name or password it happens this in postman and even putting the right password and username it does not show the information and stays like this. Only returning to normal when I restart the server.
enter image description here
I'm a beginner and I'm learning, so I don't know what's wrong, thank you in advance for your attention.
The error generally means that you can not send HTTP headers multiple times.
It occurs when username does not match because in line 24 you send status(401) but then code flow continues, trying to send another status() later.
It occurs when password does not match because in line 32 you send status(401), code flow continues and then in the next line (34) you try to send status(201). The last call might send you to the exception handler (catch ..) in which you also try to send another status(500)
to avoid this, you might try:
if (!user) {
return res.status(401).json('wrong credentials');
}
//... get password hash from DB...
if (password !== req.body.password) {
return res.status(401).json('wrong credentials');
}
return res.status(201).json(user);
Important sidenote:You should NEVER encrypt/decrypt passwords.
Store password hashes instead and only ever compare hashes. Use bcrypt!

Understanding apps script api calls?

This script works. It has user id 4 which I am adding their email address to check the google admin API. The resolved column[10] is where managers approve requests. Because some of these usernames are not correctly entered, I was looking for a way to check if they were actually correct user ids. Both .setValue() lines work under try and catch, they also error on same users. I am trying to understand where the actual validation is happening. I guess I am trying to figure how the script knows to check the primary email address? Hope that makes sense.
function getNames() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet();
var values = sheet.getDataRange().getValues();
for(var i = 1; i < values.length; i++){
var data = values[i];
var resolved = data[10];
if(resolved === ""){
try{
var email = sheet.getRange(1+i,4).getValues() + '#buisness.com'
var user = AdminDirectory.Users.get(email);
var name = user.name.fullName;
sheet.getRange(1+i, 5).setValue('Yes this is an employee');//How does this work?
//sheet.getRange(1+i, 5).setValue(name);//This works which I expect
}
catch(errors){
sheet.getRange(1+i,5).setValue(errors);
}
}
}
}
The validation happens through the AdminDirectory.Users.get() function with the help of the try and catch statements.
Admin SDK Directory Service/Get User
This API function gets a user by their email address. If the email address exists, all their data will be logged as a JSON string. Otherwise, it will return an error.
Try and Catch
The try and catch statements belong under JavaScript Errors. The try statement will run a code block that you place in it. If it encounters any error, it will trigger the catch statement (which usually displays the error messages.
Your Code
Your code checks for the validity of the email address with the use of the AdminDirectory.Users.get() function inside the try statement. If the function fails, it returns an error which triggers the catch statement (which usually displays the error messages).
References:
Admin SDK Directory Service/Get User
JavaScript Errors

MSGraph API, filter mail by a Custom Value (internetMessageHeader)

Here is my goal :
Send an email through my interface with a custom value (Imagine, orderNumber186) then use it to filter all mails send or received by this value.
for example, I have a mail address with an icon, dans when I click on it I can see all discution with him, concerning the command (or whatever) that I'm on.
Exemple of a popup with mail info
If I'm on the order number 186, il click to the icon next to the mail and I see this popup with all mail received and send concerning this order precisely (Even the name or number is not mentionned, so not just a search query).
I consulted many documents from Microsoft as well as the forum, and this is all tests I carried out, with their advantages and problems :
internetMessageHeaders (example 2 form Microsoft doc, send mail)
With this solution, I can send a mail with my custom var easily, but it's impossible to get filtered mail with it, as it's said in this post.
Despite of it, I managed to filter myself, with foreach like this :
var listMail = [];
try {
//Look in all mails if they has an internetMessageHeaders with a name corresponding to var filterName
Providers.globalProvider.graph.client
.api("/me/mailFolders/SentItems/messages?$select=internetMessageHeaders")
.get((err, res) => {
if(err == null){
//Look if they are parameters
res.value.forEach(parameters => {
if(parameters.internetMessageHeaders != undefined){
//console.log(parameters);
//If Yes, loop for each internetMessageHeaders values to see if they have a corresponding name, then stock it inside a listMail array
parameters.internetMessageHeaders.forEach(element => {
if(element.name == filterName){
Providers.globalProvider.graph.client
.api("/me/messages/"+parameters.id)
.get((err, res) => {
listMail.push(res);
});
}
});
}
});
}
else {
console.log(err);
}
});
console.log('Email List => ', listMail)
}
catch (error) {
throw error;
}
So with this method, I can get all mail that contain internetMessageHeaders values.
Now, the problem :
For optimization, we definitely can't filter all mails to get mails that contain custom var, then fetch again to get the mail and store it in an handmade array, the best way is to do it with one query, to directly have all mails concerned.
For this, I've search about a second solution : singleValueLegacyExtendedProperty
I've found how to send mail with it, and even how to recover it.
When I use it, it work great when I fetch this request :
GET https://graph.microsoft.com/v1.0/me/mailFolders/SentItems/messages?$filter=singleValueExtendedProperties/any(ep:ep/id eq 'String {66f5a359-4659-4830-9070-00047ec6ac6e} Name MyName' and contains(ep/value, 'MyVar'))
My problem with this method, is that I can see all mail send, but if the client respond directly to the mail (By outlook for exemple), my var just disappear.
I think that it's not the case with x-var (internetMessageHeaders), but I'm stuck on it too.
So, my question is simple :
How to set a custom value to a mail, then filter all of it just by is custom value ?
Ideally, internetMessageHeaders is perfect, I just need to filter on it with a microsoft graph query directly.
Thank you for any help

Concurrent uses of Google Scripts

I've created a simple signup form that takes in the persons name & email, saves it to a database and gives him a seven digit code in return from a database. Each code can only belong to one person. The code is run under my user from Google App Scripts.
I'm now wondering, if I need to use Lockservice or anything else to allow for concurrent use of the program? If the 2 people use this program at the same time for example, would this likely cause any problems - in example that the input.name would be from the answers of one user accessing the script and input.email would be originated from another? A simple illustration of my code
document.getElementById("registerBtn").addEventListener("click",register);
function register (){
//take values from input
var input = {};
input.name = document.getElementById("namefield").value;
input.email = document.getElementById("emailfield").value;
if (input.email && input.name) {
google.script.run.withSuccessHandler(successfun).withFailureHandler(failurefun).checkInfo(input)
}else{
M.toast({html: 'Please insert your data'});
}}
function successfun (output){
M.toast({html: 'Your code is:' + output});
}
function failurefun (output){
M.toast({html: 'This name or email have already been registered'});
}
/// ... Google Scripts:
function checkInfo(input) {
// open google spreadsheet
//check with indexOf if the email exists
//if the email does not exist, check if name exists
//if the name does not exist - append new row.
//if this is successful, open up another tab and take the first value from there, that does not have 2 //next to it & change the value next to it to 2.
//return confirmation code;
//If name or email exist: return error;
}
// Editing the question taking the guidance from the comment into account: If I'd have the script run not from my account but from each individual user, I would not have this issue?
According to the Apps Script Lock Service documentation:
Lock Service allows scripts to prevent concurrent access to sections of code. This can be useful when you have multiple users or processes modifying a shared resource and want to prevent collisions.
Thus, since this is an Apps Script service, it can be used only in your Apps Script code and later called from the HTML code.
You might also want to take a look at the Apps Script's quotas, which states that you can get at most 30 simultaneously executions.
An alternative to your approach is the solution proposed in this answer here which suggests to make use of the Utilities.getUuid().
Reference
Apps Script Lock Service;
Apps Script Quotas;
How can I facilitate concurrent users on a Google Apps Script Webapp?;
Apps Script Utilites Class - getUuid().

MeteorJS email form validation

Total novice here.
I'm trying to do a client side form validation for a subscribe to newsletter form. My client side code is such.
Template.body.events({
"submit .new-subscriber": function (event) {
// This function is called when the new task form is submitted
var newEmail = event.target.newEmail.value;
if (newEmail is email?){
Meteor.call("addNewSubscriber", newEmail);
}
I'm not sure how to perform form validation here? Can I perform the same server side?
We currently use two different approaches for email validation at Edthena depending on the situation. Hopefully one or both of these will fit your needs.
Regex
Regular expressions can be used for quick and dirty email validation. They will catch any obviously bogus emails like x#y.z or foo#bar, but that's about the limit of their accuracy. We use these inside the app on the client when an existing user has no motivation to enter an invalid address. Here's an example:
var isEmailValid = function(address) {
return /^[A-Z0-9'.1234z_%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(address);
};
In your case, you could add a call to isEmailValid in the submit handler. If the function returns false you could display an error instead of calling addNewSubscriber.
You can read more about email regular expressions here.
Mailgun
In cases where you think users could intentionally input invalid addresses, you can bring out the big guns and call the mailgun email validation API. This trades speed (results can take a couple of seconds to come back) for dramatically improved accuracy (mailgun does things like check that an MX record exists on the target domain). We use this approach in our public-facing forms.
Meteor.methods({
isEmailValid: function(address) {
check(address, String);
// modify this with your key
var result = HTTP.get('https://api.mailgun.net/v2/address/validate', {
auth: 'api:pubkey-XXXXXXXXXXXXX',
params: {address: address.trim()}
});
if (result.statusCode === 200) {
// is_valid is the boolean we are looking for
return result.data.is_valid;
} else {
// the api request failed (maybe the service is down)
// consider giving the user the benefit of the doubt and return true
return true;
}
}
});
In this example, isEmailValid is implemented as a method and can be called either on the server or the client depending on your needs. Note that you will need to get an API key and add the http package to your app with meteor add http.
For more details, see the docs.

Categories