node.js sendgrid with multiple recipients get blank value when adding substitution - javascript

i found issue when sending email to multiple recipients using sendgrid.
i got blank value when adding substitution.
technologies
node.js
sendgrid (v2)
==== my sample code (node.js) ====
const SENDGRID_API_KEY = 'KEY'
const sendgrid = require('sendgrid')(SENDGRID_API_KEY)
function sendEmailToSupport() {
const email = new sendgrid.Email({
from: 'jaewwalletsupport#paysbuy.co.th',
to: ['user_a#gmail.com', 'user_b#gmail.com', 'user_c#gmail.com']
html: '<div>test = :test</div>',
subject: 'dummy'
})
email.addSubstitution(':test', 'ddddddddddddd')
sendgrid.send(email, (err, response) => {
if (err) {
console.log(err)
} else {
console.log('Yay! Our templated email has been sent')
}
})
}
module.exports = {
sendEmailToSupport
}
====== result ======
user_a#gmail.com gets email with correct content test = ddddddddddddd
user_b#gmail.com, user_c#gmail.com get email with blank value test =
it looks like the first email in the email.to array will get the correct content, others get blank data.
in the sendgrid web admin, there is no error, everying is fiine.
how to fix this issue?
thanks

after analyse the sendgrid lib, i found the solution now by editing this line
email.addSubstitution(':test', new Array(email.to.length).fill('ddddddddddddd'))

Related

|SOLVED| Get User Auth data from Firebase & add it to Firebase DB

Hello!
EDIT~
I've eventually managed to do this and wanted to share it just in case anyone else needs it. Most tutorials I've found were outdated and none of them seemed to work for me. But I've finally got everything to work so Here it is!
Sign Up -
(I've created a sign up form with input fields for the username, extra info, password and email)
Make sure you import all firebase scripts you want to use and ABOVE all of them, the firebase app main script. In my case I only needed
Auth & Database - And BELOW all of this you put your Firebase App config and import either an
external .js file where you'll be using firebase functions or write it
all down there. This was a very silly mistake I did myself and I kept getting errors on the console. This is because I've been
trying to call my external .js file BEFORE importing the firebase main
scripts, which makes no sense right?
So here's my .js file for the
sign up function:
//On a different .js file where I make use of most of my functions I've added this part
//(just because I've defined more const for all my functions and I wanted to have them all
//in one place):
//getting all elements -- I've only put in this example the ones that I've used for Sign Up
const signupBtn = document.getElementById("btnsignUp");
const txtEmail = document.getElementById('txtEmail');
const txtPassword = document.getElementById('txtPassword');
const userId = document.getElementById('txtName');
const discord = document.getElementById('txtDiscord');
const bday = document.getElementById('txtBday');
const gender = document.getElementById('txtGender');
const imgURL = document.getElementById('txtimgURL');
//getting references to the apps
const auth = firebase.auth();
const database = firebase.database();
const rootRef = database.ref('users');
//------------------------------------------------//
//firebase SIGN UP.js
signupBtn.addEventListener("click", function(){
var email = txtEmail.value;
var pass = txtPassword.value;
//Signing up
auth.createUserWithEmailAndPassword(email, pass)
.then(() => {
//send verification email
sendVerificationEmail();
})
.catch(function(error) {
// Handle Errors here.
//var errorCode = error.code;
var errorMessage = error.message;
alert("Error :" + errorMessage);
});
});
//verification email function
var sendVerificationEmail = () => {
auth.currentUser.sendEmailVerification()
.then(() => {
alert("Verification Email Sent! Check your mailbox.")
})
.catch(error => {
alert("Error :" + errorMessage);
})
}
//DATABASE
//'set' adds new data to he db
signupBtn.addEventListener('click', (e) => {
e.preventDefault();
rootRef.child(userId.value).set({
Email: txtEmail.value,
Discord: discord.value,
Gender: gender.value,
Birthday: bday.value,
ImgURL: imgURL.value,
CC: 0,//Here I've added some more info that's be stored too along with
RS: 0,//the data that the user has provided
Rupreets: 0,
Bag: 1,//1: small, 2: medium, 3: big
})
});
//And that's all!
In my case, what I did with the database part is something like this:
-App name-
|
+--Users:
|
+--username1
|
+-info1
|
+-info2
|
+-info2
|
+--username2
|
+-info1
|
+-info2
|
+-info2
Well, I hope this will help somebody else too n.n
welcome to Stack Overflow!
A couple of things:
You never actually call writeUserData in your code snippet; you just define it. If you don't call it, nothing will be written.
userId is never defined, so even if you called writeUserData, your database path would be be undefined. You'd need to get the userId from firebase.auth().currentUser.uid. For more on that, see this Firebase doc: https://firebase.google.com/docs/auth/unity/manage-users#get_a_users_profile .
-- Edit --
Here's a code sample. I haven't put in absolutely everything, just the relevant omissions:
//Signing up
firebase.auth().createUserWithEmailAndPassword(email, pass)
.then((data) => {
// createUserWithEmailAndPassWord returns a promise, which, when resolved will contain various user-related properties, so
let id = data.User.uid;
function writeUserData(userId, name, email, imageURL) {
firebase.database().ref('Users/' + userId).set({
Username: name,
Email: email,
Profile_pic: imageURL,
});
}
// call your function, referencing the id above
writeUserData(id, name, email, imageURL)
If the idea of promises and calling functions isn't comfortable, you might look at a Javascript learning resource like javascript.info
Good luck!

How to synchronously send mails through nodemailer?

I'm creating an app using nodejs with the nodemailer module to send mails.
The process of my app is to read a list with names and emails, create png diploma files with jimp (based on each name and email) and store and send each one of them through nodemailer to each different mail addresses and after all this is done I want to delete each file but all this in a sync way, because the png diploma and sending the email takes some time:
The syntax of my list is:
const list = [
[name1, email1#mail.com]
[name2, email2#mail.com]
[ ... ]
[namex, emailx#mail.com]
]
Actually I want to wait for each mail to be sent because gmail seems to have a problem to handle sending multiple mails at time, after sending 13 or 15 mails it shows the next err:
error: { Error: Data command failed: 421 4.7.0 Temporary System Problem. Try again later (10). x12sm4645987otk.1 - gsmtp
So, in order to achieve this, I iterate over the list with a classic for loop (a foreach loop does it in an async way and doesn't let me to keep control over the diploma generation), I process each one of the positions of the
//Iterating over mails array
for (let i = 0; i < list.length; i++) {
// Little msg to know what is going on
console.log(`Processing address ${i} out of ${list.length}`)
const element = list[i]
// diplomaData is an object which contains data needed (such as name, course, etc) to create the diploma
diplomaData.name = element[0];
// diplomaDir is the address in which each diploma gets stored, it is returned by the generateDiploma function
diplomaDir = await generator.generateDiploma(diplomaData)
// So once the diploma is generated, I send its address to generateMailContentFunction
// it returns an object which contains text like a greeting, congratulations and of course, the diploma address
mailContent = await mailer.generateMailContent(element, diplomaDir)
// So the only thing pending is to send the email with the correspondent content
await mailer.sendMail(mailContent)
// I've commented this function because even it is declared in an async way it
// await utilities.remove(diplomaDir)
}
This is my sendMail function:
exports.sendMail = async (mailOptions) => {
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
console.log("error: ", err);
} else {
console.log(`Mail sent succesfully!`);
}
});
}
So in few words my problem is that nodemailer seems to launch all the mails at the same time after finishing the loop (I can confirm this because in my console the logs for "Processing address ..." appears before the ones from nodemailer, so I just want to make this process absolutely synchronous, could anybody help me please? :(
Your sendMail function is not asynchronous in nature. It is kicking off an asynchronous function (ie. transporter.sendMail) then immediately returning undefined (as there is no return statement).
exports.sendMail = function(mailOptions){
return new Promise(function (resolve, reject){
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
console.log("error: ", err);
reject(err);
} else {
console.log(`Mail sent successfully!`);
resolve(info);
}
});
});
}
Now when you await mailer.sendMail(mailContent) a promise will be returned & there will actually be something to await. That is, the resolution or rejection of the promise.
Be sure to have a try/catch block enclosing any await operators.

AWS managing users via Cognito

I have used many services from AWS, some were easy, while some were a bit difficult. After 2 days of searching everywhere, I can say documentation for this service is misleading.
I have simple task to do. I want to change a user attribute in the Cognito pool. And to make things easy, I just need to change an Email, and thats it. Application is an Backoffice (Express/Node), where admins can change user's email.
After reading and reading, I am getting more confused. Apparently, the aws-sdk library, the one I am familiar with, has some Cognito API's that I could use. Getting a working example on how to use them, turned out to be a nightmare.
Then I found out there is a library, but only to be used on the client side. After some tweaks I got it running in Node.js. The tweak was to expose a fetch library in global Node.js namespace.
I was able to add a new user. But for all my intentions, I can't change any of the attributes (like email). The library wants me to provide Username (real user) and a password.
I do have a Username (in this case an email), but I don't have the password.
All I need to do is to connect to the service, and send new attribute for the user and thats it.
This is what I have so far (mainly hacked code samples, from variety of places), and I cant get it to work:
var poolData = {
UserPoolId : 'euXXXXXXX',
ClientId : 'XXXXXXXXXXXX'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
Ok The above line makes a connection to the existing user pool.
Now if I were to do this:
var attributeList = [];
var dataEmail = {
Name : 'email',
Value : 'email#mydomain.com'
};
var dataPhoneNumber = {
Name : 'phone_number',
Value : '+15555555555'
};
var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
var attributePhoneNumber = new AmazonCognitoIdentity.CognitoUserAttribute(dataPhoneNumber);
attributeList.push(attributeEmail);
attributeList.push(attributePhoneNumber);
userPool.signUp('username', 'password', attributeList, null, function(err, result){
if (err) {
alert(err.message || JSON.stringify(err));
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
});
I can see in AWS console that the user is being added. Great.
Now how to change the attributes of the existing user?
All of examples like this and this
Suggest the following:
Use case 8. Update user attributes for an authenticated user.
var attributeList = [];
var attribute = {
Name : 'nickname',
Value : 'joe'
};
var attribute = new AmazonCognitoIdentity.CognitoUserAttribute(attribute);
attributeList.push(attribute);
cognitoUser.updateAttributes(attributeList, function(err, result) {
if (err) {
alert(err.message || JSON.stringify(err));
return;
}
console.log('call result: ' + result);
});
The problem here is I cant authenticate the user. I can't know user's password, only his email. This is after all a simple Backoffice program, where I just need to change users email.
What can I do in this case?
To update the attributes of a Cognito User Pool-user as an admin, you should use adminUpdateUserAttributes function from the aws-sdk class CognitoIdentityServiceProvider.
let AWS = require('aws-sdk');
let cognitoISP = new AWS.CognitoIdentityServiceProvider({ region: 'your-region-here' });
function updateUserAttribute(name, value, username, userPoolId){
return new Promise((resolve, reject) => {
let params = {
UserAttributes: [
{
Name: name, // name of attribute
Value: value // the new attribute value
}
],
UserPoolId: userPoolId,
Username: username
};
cognitoISP.adminUpdateUserAttributes(params, (err, data) => err ? reject(err) : resolve(data));
});
}

How to change the Bot/Skype channel incoming message text format

Bot emulator works fine with the clear text responses but when I try to do the same via Skype the bot response with;
I don't understand, please try again
I found out that Skype auto-formats the email by wrapping it in <a /> tags - XMM format and I am a bit stuck what's supposed to do. I think changing the incoming text format to plain-text would fix this.
I found this like for a similar issue on Github but this is on C# and I am using Node.JS.
How do I change the default text format of the bot/skype channel to plain text instead of markdown so Skype auto-formatting would not happen?
Updated according to #ezequiel-jadib but still no luck. Maybe I am doing it wrong?
// bot.js
bot.use(...[logger]);
// logger.js
module.exports = exports = {
receive: (e, next) => {
e.textFormat('plain');
logConversation(e);
next();
},
send: (e, next) => {
logConversation(e);
next();
}
};
To get around this situation, you need to validate if you have an HTML wrapped email address, then extract the email portion from the string before saving it to session.dialogData.
For example replace line 40:
const usernameOrEmail = session.dialogData.usernameOrEmail = results.response;
with:
// where results.response is in the format 'hello#world.com'
var exampleEmailWrappedInHtml = results.response;
// validate if we have email wrapped in HTML from Skype
if(exampleEmailWrappedInHtml.match(/<a href="mailto:/i)) {
console.log('HTML wrapped email detected!');
const usernameOrEmail = session.dialogData.usernameOrEmail = exampleEmailWrappedInHtml.split('>')[1].split('<')[0];
} else {
console.log('no match.');
}
You can just call to the textFormat method of the Message like in C#
The accepted values are:
export var TextFormat = {
plain: 'plain',
markdown: 'markdown',
xml: 'xml'
};

Request for api not working properly

I'm making a request but it doesn't seem to work. If I copy code into my browser it works good, but in my console it shows up this :
{
"status" : "success",
"data" : {
"error_message" : "API access enabled, but unable to verify two-factor authentication code. If you need help with this, please contact support#bitskins.com."
}
}
What am I doing wrong? It's based on two-factor authentication that as I said works good while printing the url itself and when i'm copying it into my browser.
var url = 'https://bitskins.com/api/v1/get_item_price/?api_key='+bitskins.apikey+'&code='+bitskins.code+'&names='+encodeURIComponent(items[i].market_hash_name)+'&delimiter=!END!';
console.log(url);
request(url, function (error, response, body) {
if (!error) {
console.log(body)
}
});
In case you want, here is my api key module to generating it (api key deleted for security)
var TOTP = require('onceler').TOTP;
//Create a TOTP object with your secret
var totp = new TOTP('deleted');
// print out a code that's valid right now
// console.log(totp.now());
var code = totp.now();
module.exports = {
code: code,
apikey: 'deleted'
}
Founder of BitSkins, Inc. here. You need to have the following:
1) Your API Key
2) Your Secure Access Secret
You see the Secret when you enable Secure Access. If you do not have this, just disable/re-enable Secure Access and note the Secret down. The TOTP code you generate is with that Secret. Generate the TOTP code right before every API call and you'll be fine.
I think it should work. For me it works fine.
var API_KEY = ''; //It is very important
var SECRET_KEY = ''; //It is very important
var totp = new TOTP(SECRET_KEY);
var code = totp.now();
var options = {
url: 'https://bitskins.com/api/v1/get_item_price',
form: {
'api_key': API_KEY,
'names': 'Tec-9%20%7C%20Sandstorm%20(Minimal%20Wear)',
'delimiter': '!END!',
'code': code
}
};
function callback(error, response, body) {
if (!error) {
var info = JSON.parse(body);
console.log(info);
}
}
request.post(options, callback);
What npm package do you use to create 2FA code? I'm using "onceler" from example but I think it creates wrond codes. Here is my code:
var API_KEY = ''; //correct key from settings page
var SECRET_KEY = ''; // correct key which I copied from form with QR code.
var totp = new TOTP("SECRET_KEY");
var code = totp.now();
This code doesn't equal code which I can see in my mobile device and with this code I get error message like in author's question. But if I put code from my mobile in programm code - it works fine. So what package should I use to get correct codes?

Categories