send email with gmail api not working - javascript

I am trying to send an email, by using the google api in node.js
var sendmsg = function(auth) {
var to = 'foo#gmail.com',
subject = 'Hello World',
content = 'send a Gmail.'
var email = "To: "+ to +"\r\n"+
"Subject: "+subject+"\r\n"+
content;
var base64EncodedEmail = new Buffer(email).toString('base64');
var gmail = google.gmail('v1');
var request = gmail.users.messages.send({
'userId': auth,
'message': {
'raw': base64EncodedEmail
}
}, function (err, result) {
console.log('result'+result);
});
};
I took this example from the quick start sample in google's documentation, that reads the labels in my email account(which worked fine). And I just changed the scopes to:
var SCOPES = ['https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.send'];
And created that var = email
var to = 'foo#gmail.com',
subject = 'Hello World',
content = 'send a Gmail.'
var email = "To: "+ to +"\r\n"+
"Subject: "+subject+"\r\n"+
content;
Then I am just trying to use the gmail.users.messages.send method.. But when running the result is returning the following:
<HTML>
<HEAD>
<TITLE>Bad Request</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Bad Request</H1>
<H2>Error 400</H2>
</BODY>
</HTML>
Any idea what I am missing? I think the way I am creating my var 'email' is wrong, but I am not sure how it should be

The value of the userId-field has to be the senders email address (or me for short), the auth-object has to be passed in the auth field, and the message should be passed in the resource-field. Your message lacks a From header and an extra new line before the content to be valid. The message also has to be base64url-encoded:
function sendMessage(auth, from, to, subject, content) {
// The Gmail API requires url safe Base64
// (replace '+' with '-', and '/' with '_')
var encodedEmail = new Buffer(
'From: ' + from + '\r\n' +
'To: ' + to + '\r\n' +
'Subject: ' + subject + '\r\n\r\n' +
content
).toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
var gmail = google.gmail('v1');
var request = gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: encodedEmail
}
}, function (err, result) {
console.log('result:', result);
});
};

Instead of constructing the body yourself I'd highly reccomend using Nodemailers system:
const sendMail = async () => {
const mail = await new MailComposer({
to: ...,
from: ...,
subject: ...,
html: ...,
});
const message = await mail.compile().build();
const encodedMessage = message
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
await gmail.users.messages.send({
userId: 'me',
requestBody: { raw: encodedMessage },
});
}

Related

Passport.js -- how to use render email asynchronously

I'm running Express with Sequelize/MariaDB and Passport.js for user authentication.
I'm in the signup part (which works) but I can't seem to render and return an activation email asking them to confirm their email.
passport.js (containing authentication strategies)
passport.use('user-signup-email', new LocalStrategy({
//Process/validate input, check database for existing emails, create user, add to database...
...
if (newUser) {
var token = jwt.sign( { data: newUser.id }, newUser.login_pass + "-" + newUser.account_created);
var URLdata = {
id: newUser.id,
expiration_time: Date.now() + 86400000, //24 hours
url: token,
email_info: mail.createActivationEmail(req.app, newUser.login_key, newUser.user_alias, token)
};
console.log("info: " + URLdata.email_info);
...
//Store dynamic URL and email contents (in case of resending) in database with expiration time
//And then send the email that was just rendered
}
mail.js
exports.createActivationEmail = (app, recipientAddress, userName, url) => {
app.render('emails/activation_email', {
layout: false,
page_title: 'Please confirm your account!',
dynamic_url: url
}, function(err, rendered) {
if (err) {
console.log("Q [" + err + "]");
}
console.log("R " + rendered.toString());
return {
from: adminEmailAddress,
to: recipientAddress,
cc: false,
bcc: false,
subject: 'Welcome to example.com ' + userName + '!',
html: rendered.toString(),
text: "TO DO"
};
});
};
The last console.log in passport.js displays "info: undefined."
But if I print the output in the mail.js module before returning it, it is fine.
I'm guessing it's an async problem? How would I fix it?
I'm still a little unclear on promises and async-await blocks in this context.
Thanks in advance for any help you can offer!
You misunderstood callback functions.
callbacks are (should, when you write them) asynchron:
https://nemethgergely.com/async-function-best-practices/
How to write asynchronous functions for Node.js
I changed your createActivationEmail function.
The last argument is now a callback, thats get invoked when your code app.redner is done.
passport.use('user-signup-email', new LocalStrategy({
//Process/validate input, check database for existing emails, create user, add to database...
// ...
if(newUser) {
var token = jwt.sign({ data: newUser.id }, newUser.login_pass + "-" + newUser.account_created);
mail.createActivationEmail(req.app, newUser.login_key, newUser.user_alias, token, (err, email_info) => {
var URLdata = {
id: newUser.id,
expiration_time: Date.now() + 86400000, //24 hours
url: token,
email_info
};
console.log("info: " + URLdata.email_info);
//...
//Store dynamic URL and email contents (in case of resending) in database with expiration time
//And then send the email that was just rendered
});
}
}));
exports.createActivationEmail = (app, recipientAddress, userName, url, done) => {
app.render('emails/activation_email', {
layout: false,
page_title: 'Please confirm your account!',
dynamic_url: url
}, function(err, rendered) {
if (err) {
console.log("Q [" + err + "]");
cb(err);
return;
}
console.log("R " + rendered.toString());
done(null, {
from: adminEmailAddress,
to: recipientAddress,
cc: false,
bcc: false,
subject: 'Welcome to example.com ' + userName + '!',
html: rendered.toString(),
text: "TO DO"
});
});
};

Nodemailing using variables to send an email with a Javascript Discord bot

I'm trying to make a Discord bot email certain people based on information using Nodemailer. I am using variables to choose who to send emails to. Whenever I try to use it, it fails to send the email and says the variables are undefined. Here is my code:
const Discord = require('discord.js');
const client = new Discord.Client();
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'user',
pass: 'pass'
}
});
var mailOptions = {
from: 'someemail#gmail.com',
to: firstname + "." + lastname + "#gmail.com",
subject: 'Sending Email using Node.js',
text: 'That was easy!'
};
var firstname = "";
var lastname = "";
var chat = 0;
var placeholder = "";
client.on("message", message => {
var prefix = '!';
if (message.content.startsWith(prefix + "chat") && chat == 0) {
chat = chat + 1;
message.channel.send("Welcome! I am Sabrina, a friendly chatbot. By the way, what is your first name?");
} else if (message.content.startsWith(prefix + "chat") && chat == 1) {
chat = chat + 1;
placeholder = message.content;
firstname = placeholder.replace("!chat ", "");
message.channel.send("Very intresting name, " + firstname + ".
What is your last name?");
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
}
});
}
});
client.login("Token")
var mailOptions = {
from: 'someemail#gmail.com',
to: firstname + "." + lastname + "#gmail.com",
subject: 'Sending Email using Node.js',
text: 'That was easy!'
};
This part is evaluated at the time it's parsed - before the rest of the code runs, so before firstname and lastname are filled (or even initialized as ""). It's not dynamically updated as those variables change.
Since you want to, essentially, construct mailOptions dynamically, why not make a function that does it?
// Traditional syntax
function getMailOptions(first, last) {
return {
from: 'someemail#gmail.com',
to: first + "." + last + "#gmail.com",
subject: 'Sending Email using Node.js',
text: 'That was easy!'
}
}
// Or, since you're using arrow functions..
const getMailOptions = (first, last) => ({
from: 'someemail#gmail.com',
to: first + "." + last + "#gmail.com",
subject: 'Sending Email using Node.js',
text: 'That was easy!'
});
Now you can use it:
transporter.sendMail(getMailOptions(firstname, lastname), function(error, info){
/* ... */

Emaling with apps script to non-gmail accounts

function onSubmit(e) {
//.......
var NewSpreadSheetID = NewSpreadSheet.getId();
var File = Drive.Files.get(NewSpreadSheetID);
var url = File.exportLinks[MimeType.CSV];
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer' + token
}
});
var blobs = [response.getBlob().setName(DirectoryName)];
if (EmailBoolean == "yes") {
var UserEmail = e.response.getResponseForItem(frm[130]).getResponse().toString();
var CreatorEmail = Session.getEffectiveUser().getEmail();
MailApp.sendEmail({
to : UserEmail,
subject: "Your project spreadsheet",
attachments: blobs
});
}
MailApp.sendEmail({
to : CreatorEmail,
subject: "Your project spreadsheet",
attachments: blobs
});
}
When this email is sent to the non-gmail account, say a hotmail account, the file essentially is attached as a link to a site that redirects the user to log in to a g-mail account.

node.js: looping, email each matching row from query but it it emails the same user by # number of matches?

I have a node.js email server that works fine however I noticed a problem. If the query string found 4 rows, it would send four emails but only to the first result instead of to each found email address.
var mysql = require('mysql');
var nodemailer = require('nodemailer');
// Amazon SES is sending the emails - uses this module for node
var ses = require('nodemailer-ses-transport');
//transport for connecting with Amazon SES
var transporter = nodemailer.createTransport(ses({
accessKeyId: '****************',
SecretAccessKey: '*************'
}));
// Mysql Connection to database
var connection = mysql.createConnection({
*****connection info goes here *****
});
//Connect to mysql database and check for errors
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
return;}
});
var querystring1 = 'SELECT `date_created`,`first_name`,`last_name`,`email` FROM `users` WHERE TIMESTAMPDIFF(SECOND,date_created, NOW()) <=298 AND `email`!="" LIMIT 0,5';
connection.query(querystring1, function (err, row) {
if (err) throw err;
for (var i in row) {
// Email content; change text to html to have html emails. row[i].column name will pull relevant database info
var sendit = {
to: row[i].email,
from: '******#******',
subject: 'Hi ' + row[i].first_name + ', Thanks for joining ******!',
html: {path:__dirname + '/templates/welcome.html'}
};
// Send emails
transporter.sendMail(sendit,function(error,response){
if (error) {
console.log(error);
}else{
console.log("Message sent1: " + row[i].first_name);}
transporter.close();}
)
}});
....
How do I have it loop through each found row and send a custom email using row data individual row data?
Do not close the transporter until all your emails are sent.
var querystring1 = 'SELECT `date_created`,`first_name`,`last_name`,`email` FROM `users` WHERE TIMESTAMPDIFF(SECOND,date_created, NOW()) <=298 AND `email`!="" LIMIT 0,5';
connection.query(querystring1, function (err, row) { if (err) throw err;
var toSend=[];
for (var i in row) { // Email content; change text to html to have html emails.
toSend.push({//row[i].column name will pull relevant database info
to: row[i].email,
from: '******#******',
subject: 'Hi ' + row[i].first_name + ', Thanks for joining ******!',
html: {path:__dirname + '/templates/welcome.html'}
});
}
// Send emails
async.eachSeries(toSend, function( sendit, callback) {
transporter.sendMail(sendit,function(error,response){
if (error) {
callback(error);
}else{
console.log("Message sent1: " + sendit.to); callback();
}
)},function(err){if (err) throw (err); console.log("Finished")};
});
});
try this
// Send emails
(function (mail) {
transporter.sendMail(mail,function(error,response){
if (error) {
console.log(error);
}else{
console.log("Message sent1: " + mail.first_name);}
transporter.close();
}
)
)(sendit);
Please use this one, I have tested and it works fine:
function sendmail(sendit, username){
transporter.sendMail(sendit,function(error,response){
if (error) {
console.log(error);
} else {
console.log("Message sent: " + username);}
transporter.close();
}
)
};
for (var i in row) {
var sendit = {
to: row[i].email,
from: 'sandeepv#gmail.com',
subject: 'Hi ' + row[i].first_name + ', Thanks for joining!',
html: {path:__dirname + '/welcome.html'},
};
// Send email
sendmail(sendit, row[i].first_name);
}

socket.io error when connecting from iframe

So I have a couple applications on different servers all from inside of our network and I am using node.js and socket.io js to handle real time communication between them which when each is run separately works fine, but when I put application 2 inside an iframe on application 1 I get the following error
"Blocked a frame with origin "http : // 192.128.1.97" from accessing a frame with origin "http : // intranet". Protocols, domains, and ports must match. "
*note I added spaces in the urls above because the page was telling me links weren't allowed.
is there some way to allow the iframe to connect to socket.io? the code is pretty simple, but here is the server code...
/**
* Server js file for node
* this will handle all of the incoming requests from all the apps
* and push them to the clients
*/
var express = require("express"),
app = express(),
http = require("http").createServer(app),
io = require("socket.io").listen(http);
_ = require("underscore");
var participants = [];
// setup the environment and tell node and express what it needs
app.set("ipaddr", "192.168.1.76");
app.set("port", 8080);
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
//further environment setup telling node and express what to use to handle requests
app.use(express.static("public", __dirname));
app.use(express.bodyParser());
//setup the default page
app.get("/", function(request, response) {
//render the view page
//response.render("node_home");
//just post a message to the screen
response.send("Server is up and running");
//respond with a json object
// reponse.json(200, {message: "Server is up and running"});
});
//setup a handler for requests to /message
app.post("/message", function(request, response) {
var message = request.body.message;
if(_.isUndefined(message) || _.isEmpty(message.trin())) {
return response.json(400, {error: "Message is invalid"});
}
var name = request.body.name;
io.sockets.emit("incomingMessage", {message: message, name: name});
response.json(200, {message: "Message received"});
})
io.on("connection", function(socket) {
socket.on("newUser", function(data) {
participants.push({id: data.id, name: data.name});
io.sockets.emit("newConnection", {participants: participants, badgeNumber: data.badgeNumber, id: data.id})
});
socket.on("nameChange", function(data) {
_findWhere(paticipants, {id: socket.id}).name = data.name;
io.sockets.emit("nameChanged", {id: data.id, name: data.name})
});
socket.on("disconnect", function() {
participants = _.without(participants, _.findWhere(participants, {id: socket.id}));
io.sockets.emit("userDisconnected", {id: socket.id, sender: "system"})
});
socket.on("phraseCheck", function(data) {
io.sockets.emit("checkPhrase", {id: data.id, phrase: data.phrase});
});
socket.on('newFluxClient', function(data) {
console.log(data);
io.sockets.emit('fluxConnection', {badgeNumber: data.badgeNumber, id: data.id});
});
socket.on('phraseAllowed', function(data) {
io.sockets.emit('allowedPhrase', {id: data.id, allowed: data.allowed});
});
socket.on('customFunction', function(data) {
console.log(data);
io.sockets.emit('customFunction', data);
});
});
//start the app and have it listen for incoming requests
http.listen(app.get("port"), app.get("ipaddr"), function() {
console.log("Server up and running. Go to http://" + app.get("ipaddr") + ":" + app.get("port"))
});
application 1 code....
/**
* client js file
* this will handle connecting to node and handle the incoming messages
* as well as sending responses and messages to the server
*/
var childSessionId = '',
sessionId = '',
socket = '',
serverBaseUrl = '',
participants = [];
function init() {
serverBaseUrl = 'http://192.168.1.76:8080';
socket = io.connect(serverBaseUrl);
sessionId = '';
function updateParticipants(part) {
participants = part;
$("#participants").html('');
for(var i=0; i<participants.length;i++) {
$("#participants").append('<span id="' + participants[i].id + '">' + participants[i].name + ' ' + (participants[i].id === sessionId ? '(You)' : '') + '<br /></span>');
}
}
socket.on('connect', function() {
sessionId = socket.socket.sessionid;
console.log('Connected ' + sessionId);
socket.emit("newUser", {id: sessionId, name: page.user});
});
socket.on('userDisconnect', function(data) {
$('#' + data.id).remove();
});
socket.on('nameChanged', function(data) {
$('#' + data.id).html(data.name + ' ' + (data.id === sessionId ? '(You)' : '') + '<br />');
});
socket.on('newConnection', function(data) {
if(data.badgeNumber === page.userBadgeNumber) {
childSessionId = data.id;
}
updateParticipants(data.participants);
});
socket.on('fluxConnection', function(data) {
console.log('flux connection data:');
console.log(data);
if(data.badgeNumber === "**********") {
childSessionId = data.id;
}
});
socket.on('incomingMessage', function(data) {
$("#messages").prepend('<b>' + data.name + '</b><br />' + data.message + '<hr />');
});
socket.on('error', function(reason) {
console.log('Unable to connect to server', reason);
});
socket.on('customFunction', function(data) {
console.log(data);
data.data();
});
socket.on('checkPhrase', function(data) {
if(data.id === childSessionId) {
var phrases = shoppingcart.getPhrasesInCart();
var allowed = ($.inArray(data.phrase, phrases) >= 0);
socket.emit('phraseAllowed', {id: data.id, allowed: allowed});
}
});
}
$(document).ready(function() {
init();
})
and application 2 code....
// NODE JS INITIALIZATION
var socket = null;
var sessionId = '';
function initialize_node(){
var serverBaseUrl = 'http://192.168.1.76:8080';
socket = io.connect(serverBaseUrl);
sessionId = '';
socket.on('connect', function() {
sessionId = socket.socket.sessionId;
socket.emit('newFluxClient', {id: sessionId, badgeNumber: "PDX000022", name: "matthew.hicks"});
// socket.emit('newUser', {id: sessionId, badgeNumber: user.badge, name: user.name});
})
socket.on('allowedPhrase', function(data) {
if(sessionId === data.id) {
alert("I'm a preddy little princess. Console logging data returned");
console.log(data);
/*
functions to allow or disallow the phrase
based on data.allowed
it will be true if the phrase is in the shopping cart
and false if it is not
*/
}
});
// $('#phrase').blur(function() {
// checkPhrase();
// })
};
function checkPhrase() {
//var phrase = $('#phrase').val();
var phrase = "Shindigs in Ptown";
socket.emit('phraseCheck', {id: sessionId, phrase: phrase});
}
$(document).ready(function () {
initialize_node();
});
Sorry for the mass amount of code but trying to give all the conte4xt necessary. Essentially server is up and running, application 1 connects and gets a unique session id, then when application 2 tries to connect from the iframe I get the above mentioned error, when application 2 is not in an iframe it connects just fine and gets a session id. Please help if you can, I can't figure out why it is getting blocked and I really need this up and running. Thank you in advance for any help
You have encountered Same Origin Policy.
The simplest solution is to run the iframe from the same server.
Since you have access to I.T time read up on CORS
You will basically have to configure the server to allow XSS from your domain.
You can also try something like:
document.domain = "intranet"
Read up on it here

Categories

Resources