I'd like to send emails each 200ms, but it seems not working with promises :
My function :
SendMail.send = function(from, to, subject, body, name, logo) {
var transporter, html,
contactparam = _.find(options.blocks, { name: 'contactparam' }).params,
smtpUsername = _.find(contactparam, { name: 'smtp_username' }),
smtpPassword = _.find(contactparam, { name: 'smtp_password' }),
smtpHost = _.find(contactparam, { name: 'smtp_host' }),
smtpPort = _.find(contactparam, { name: 'smtp_port' }),
compagny = _.find(contactparam, { name: 'compagny' }),
firstname = _.find(contactparam, { name: 'firstname' }),
facebookUrl = _.find(_.find(options.blocks, { name: 'socialparam' }).params, { name: 'facebook' }).value,
twitterUrl = _.find(_.find(options.blocks, { name: 'socialparam' }).params, { name: 'twitter' }).value,
linkedinUrl = _.find(_.find(options.blocks, { name: 'socialparam' }).params, { name: 'linkedin' }).value,
secondaryColor = _.find(_.find(options.blocks, { name: 'themeparam' }).params, { name: 'secondary_color' }).value,
loadedLogo = logo || _.find(_.find(options.blocks, { name: 'logoparam' }).params, { name: 'logo' }).value,
websiteUrl = _.find(_.find(options.blocks, { name: 'mainparam' }).params, { name: 'url' }).value,
templateFile = fs.readFileSync('./app/views/0-templates/' + options.template + '/mail.ejs', 'utf-8'),
loadedName = name || (compagny && compagny.value) || (firstname && name && firstname.value + ' ' + name.value),
ejsTemplate = ejs.compile(templateFile);
html = ejsTemplate({
logo: loadedLogo || '/images/LOGO.png',
websiteUrl: websiteUrl,
body: body,
title: subject,
facebookUrl: facebookUrl,
twitterUrl: twitterUrl,
linkedinUrl: linkedinUrl,
secondaryColor: secondaryColor || '#1c1c1c'
});
if ((smtpUsername && smtpUsername.value !== '') && (smtpPassword && smtpPassword.value !== '') && (smtpHost && smtpHost.value !== '') && (smtpPort && smtpPort.value !== '')) {
transporter = nodemailer.createTransport({
host: smtpHost.value,
pool: true,
port: parseInt(smtpPort.value, 10),
auth: {
user: smtpUsername.value,
pass: smtpPassword.value
}
});
} else {
transporter = nodemailer.createTransport({
sendmail: true,
newline: 'unix',
path: '/usr/sbin/sendmail'
});
}
return transporter.sendMail({
from: loadedName + ' <' + from + '>',
replyTo: from,
to: to,
subject: subject,
html: html
});
};
SendMail.arraySend = function(array) {
var promises = [];
promises = array.map(function (emailObj, index) {
return new Promise(function () {
setTimeout(function () {
return sendMailHelper.send(emailObj.from, emailObj.to, emailObj.title, emailObj.htmlMessage);
}, 200 * index);
});
});
return promises;
};
Using my function :
Promise.all(sendMailHelper.arraySend(emailsArray)).then( function() {
request.flash('success', 'Emails sent !');
response.redirect('/');
});
emailsArray :
emailsArray.push({
from: from,
to: registration.cfc_user.email,
title: 'Finale : ' + newTournament.name,
htmlMessage: htmlMessage
});
Issue : emails are not sent.
First of all, you need to actually resolve the promises you create. Without resolving (or rejecting) them, Promise.all will wait forever.
Also, .sendMail() returns a Promise when called, so this can be leveraged to make sure the delayed Promise is resolve only after the actual sending is completed:
SendMail.arraySend = function(array) {
var promises = array.map(function (emailObj, index) {
return new Promise(function (resolve, reject) { //<-- add the resolve/reject parameters
setTimeout(function () {
sendMailHelper
.send(emailObj.from, emailObj.to, emailObj.title, emailObj.htmlMessage)
.then(resolve) // <-- resolve the Promise once execution is done
.catch(reject); // <-- reject on failure
}, 200 * index);
});
});
return promises;
};
This will launch all Promises at once, but each will trigger within 200ms of each other. The actual sending might need some time but eventually everything will be done.
Alternatively, you can set it up so after each Promise is done, you wait 200ms and then launch another one. This will mean that you get a single Promise object in the end, not an array but it's guaranteed that messages are sent sequentially. It is easy to do this using Array#reduce
//helper function that converts email objects to be sent into Promises
const delayedSend = (emailObj, delay) => () => {
return new Promise((resolve, reject) => {
setTimeout(
() => sendMailHelper.send(emailObj.from, emailObj.to, emailObj.title, emailObj.htmlMessage)
.then(resolve)
.catch(reject);
},
delay
)
})
}
SendMail.arraySend = function(array) {
var promises = array.reduce(function (sequence, emailObj) {
// ^^^^^^ reduce to convert to a single Promise
return sequence.then(delayedSend(emailObj, 200)); // <-- chain .then() and delay each following execution
}, Promise.resolve());
// ^^^^^^^^^^^^^^^^^ initial value is a simple resolved Promise.
return promises;
};
You can not return from a function called in a timeout. Also, it does not seem like a good idea to call that many Promises.
Best way is to group all your emails within a single Promise, returning an object of emails sent and emails with errors:
SendMail.arraySend = array => {
return new Promise(resolve => {
let sent = [];
let errors = [];
const finalise = () => {
if ((sent.length + errors.length) >= array.length) {
resolve({ sent, errors });
}
};
array.forEach((emailObj, index) => {
setTimeout(function () {
sendMailHelper.send(emailObj.from, emailObj.to, emailObj.title, emailObj.htmlMessage)
.then(() => {
sent.push(emailObj);
finalise();
});
.catch(() => {
errors.push(emailObj);
finalise();
})
}, 200 * index);
});
});
};
Related
So I am working on a Discord bot using discord.js and I am stumped with how I would get the reaction from a specific embed. My current reaction function looks for any check mark react and approves the next row in the database. I need it so that the row that corresponds to the embed is updated since the row will be altered whenever a check mark is added as a reaction to any message.
I have tried adding a number variable to the embed and using that as the result number however it did not work. I am very new to this (which is also why it may not be the best code you have ever seen) so I apologise in advance for anything that I may have missed/got wrong.
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i => 0; i++) {
var sql = "SELECT * FROM shouts WHERE status = 'awaiting notification' LIMIT 1";
connection.query(sql, function (err, result) {
if (result.length > 0) {
var updatesql = "UPDATE shouts SET status = 'awaiting verification' WHERE ID = " + result[0].id
connection.query(updatesql, function (upderror, updresult) {
const uploadembed = new djs.MessageEmbed()
.setColor('#010101')
.setTitle('New Upload')
.setImage(result[0].file_link)
.addFields(
{ name: 'Name', value: result[0].file_name, inline: true },
{ name: 'Link', value: result[0].file_link, inline: true }
)
.setFooter({ text: 'Uploaded pending' })
.setTimestamp()
client.channels.fetch('855605483401773066').then(channel => channel.send({ embeds: [uploadembed] }).then(react => {
react.react("✅");
react.react("❌");
}));
})
}
})
await timer(5000); // then the created Promise can be awaited
}
}
load();
client.on('messageReactionAdd', (reaction, user) => {
var sql = "SELECT id, file_name, uploaded_on, file_link FROM shouts WHERE status = 'awaiting verification'";
connection.query(sql, function (err, result) {
if (reaction.emoji.name == '✅' && !user.bot) {
var updatesql = "UPDATE shouts SET status = 'confirmed' WHERE ID = " + result[0].id
connection.query(updatesql, function (upderror, updresult) {
const uploadembed = new djs.MessageEmbed()
.setColor('#44dd44')
.setTitle('Upload Approved')
.setImage(result[0].file_link)
.addFields(
{ name: 'Name', value: result[0].file_name, inline: true },
{ name: 'Link', value: result[0].file_link, inline: true }
)
.setFooter({ text: 'Approved by ' + user.username })
.setTimestamp()
reaction.message.edit({ embeds: [uploadembed] }).then(react => {
react.reactions.removeAll();
})
})
}
})
})```
Any help at all is greatly appreciated.
My apologies beforehand, because I'm finding it a bit difficult to explain the question at hand, so I'll divide it in parts.
I'm trying to find the best way to trigger retries based on a failed final retry attempt from another function (retry of a series of retries). What I'm thinking would work is to put something like throw new Err('Forfeit') within the else statement of each retry function (see codes for 1 and 2), then have the original call retry based on that (since there's a condition when it reaches the max attempt). Will that work? How would you approach it?
I created my own retry function below (n=3 attempts):
function retryFetchAttempt(attempt, maxAttempts, myFunction, error) {
if (attempt < maxAttempts) {
setTimeout(function() {
myFunction()
.then(result => console.log('Retry: ', attempt))
.catch(err => {
console.log(err);
attempt += 1;
retryFetchAttempt(attempt, maxAttempts, myFunction, error)
}
)
}, 2000 ** attempt);
} else {
console.log('Forfeited at retry: ', attempt, error);
// throw new Err('Forfeit'); ????
}
}
module.exports = {
retryFetchAttempt
}
I created another retry for each email being sent (10 attempts).
const {google} = require('googleapis');
const nodemailer = require('nodemailer');
function retryEmail(attempt, sender, emailRecipients, bccRecipients, getSubject, htmlOutput, refreshToken, clientid, client_secret, REDIRECT_URI) {
const maxRetries = 10;
if (attempt < maxRetries) {
setTimeout(function() {
sendEmail(
sender,
emailRecipients,
bccRecipients,
getSubject,
htmlOutput,
refreshToken,
clientid,
client_secret,
REDIRECT_URI
).then(result => console.log('Email sent after retry: ', attempt, result))
.catch(err => {
console.log(err);
attempt += 1;
retryEmail(
attempt,
sender,
emailRecipients,
bccRecipients,
getSubject,
htmlOutput,
refreshToken,
clientid,
client_secret,
REDIRECT_URI)
}
)
}, 3000);
} else {
console.log('Forfeited at retry: ', attempt);
// throw new Err('Forfeit'); ????
}
}
async function sendEmail(sender, emailRecipients, bccRecipients, getSubject, htmlOutput, refreshToken, clientid, client_secret, REDIRECT_URI) {
try {
const oAuth2Client = new google.auth.OAuth2(clientid, client_secret, REDIRECT_URI);
oAuth2Client.setCredentials({refresh_token: refreshToken});
const accessToken = await oAuth2Client.getAccessToken();
let smtpConfigWithToken = {
host: 'smtp.gmail.com',
port: 465,
secure: true,
//pool: true,
auth: {
type: 'OAuth2',
user: sender,
clientId: clientid,
clientSecret: client_secret,
refreshToken: accessToken.res.data.refresh_token,
accessToken: accessToken.res.data.accessToken
}
};
let transporter = nodemailer.createTransport(smtpConfigWithToken);
let HelperOptions = {
from: sender,
to: emailRecipients,
bcc: bccRecipients,
subject: getSubject,
html: htmlOutput
};
const result = transporter.sendMail(HelperOptions);
return result;
} catch(err) {
return err;
}
}
module.exports = {
sendEmail,
retryEmail
}
I have a node cron job that triggers Mon-Fri at 11:30am UTC - 300. This job uses node-fetch to call another route that queries the database.
That fetch call uses the 3 attempts retry. See the code below:
// CRON schedule to run MON-FRI at 11:30am UTC - 300
// Reminder of returning equipment 7 days after being loaned
// Also removes the 'snooze' status after 7 days (if snooze was applied)
cron.schedule('30 11 * * 1,2,3,4,5', () => {
let retryAttempt = 0;
const maxAttempts = 3;
async function scheduler() {
try {
const fetchResult = await fetch(process.env.REDIRECT_URI + config.route1, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'auth-token': process.env.API_TOKEN
},
});
const response = await fetchResult;
const jsonData = await response.json();
console.log(jsonData)
} catch(e) {
console.log('')
}
}
scheduler()
.then(sch => console.log('Fetch Email reminder'))
.catch(err => retryFetchAttempt(retryAttempt, maxAttempts, scheduler, ''))
});
The following route is called by the 1st fetch call. This one generates a call forEach DB record that matches the parameters. Then, it'll trigger a reminder via email. See the code below:
// Route used by cron at 11:30am (Mon-Fri) to send email reminders about non-returned equipment
router.get(config.route1, (req, res) => {
let retryAttempt = 0;
const maxAttempts = 3;
async function fetchEachScheduled() {
try {
await postgres('metsupply').select('*').then(data => {
if (data[0] !== undefined) {
data.forEach(async (record, index) => {
try {
setTimeout(async function() {
try {
const fetchResult = await fetch(process.env.REDIRECT_URI + config.route2, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'auth-token': process.env.API_TOKEN
},
body: JSON.stringify({
record: record,
index: index
})
});
const response = await fetchResult;
const jsonData = await response.json();
console.log(jsonData)
} catch(e) {
console.log(e)
console.log(record)
}
}, 1000 * index)
} catch(e) {
console.log(e)
}
})
} else {
console.log(`Job finished at ${new Date().toString().split(" GMT",1)} -- No data`)
}
res.json('Closed query');
})
} catch(e) {
console.log(e)
}
}
fetchEachScheduled()
.then(sch => console.log('Email reminder started'))
.catch(err => retryFetchAttempt(retryAttempt, maxAttempts, fetchEachScheduled, ''))
})
Finally (yes... Finally!). The function below processes the forEach call.
router.post(config.route2, async (req, res) => {
try {
const {
record,
index
} = req.body;
const currentDate = new Date();
const currentDateString = currentDate.toString().split(" GMT",1);
let retryAttempt = 0;
const recordDate = new Date(record.jsdate.toString());
recordDate.setDate(recordDate.getDate() + 1);
const output = emailTemplates.supplyReminder(record);
const sender = process.env.SUPPLY_SENDER;
const emailRecipients = config.emailRecipient;
const bccRecipients = [];
const getSubject = 'Return equipment reminder';
const refreshToken = process.env.SUPPLY_REFRESH_TOKEN;
const clientid = process.env.SUPPLY_CLIENT_ID;
const client_secret = process.env.SUPPLY_CLIENT_SECRET;
const REDIRECT_URI = process.env.REDIRECT_URI;
if ((currentDate >= recordDate) && (record.returned === 'No') && (record.needtoreplace === 'No') && (record.requesttype === 'Loan') && (record.snooze === 'No') && (record.notificationsoff === 'No')) {
setTimeout(async function() {
await sendEmail(
sender,
emailRecipients,
bccRecipients,
getSubject,
output,
refreshToken,
clientid,
client_secret,
REDIRECT_URI
).then(async result => {
//console.log('Email sent...', result);
})
.catch(err => {
retryAttempt += 1;
retryEmail(
retryAttempt,
sender,
emailRecipients,
bccRecipients,
getSubject,
output,
refreshToken,
clientid,
client_secret,
REDIRECT_URI
);
}
)
}, 1000);
postgres(config.maindb).update('emailcount', record.emailcount + 1).where('requestid', record.requestid).then(async counter => {
res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Sending Email`)
}).catch(e => console.log(e))
}
if ((currentDate < recordDate) && (record.returned === 'No') && (record.needtoreplace === 'No') && (record.requesttype === 'Loan') && (record.snooze === 'No') && (record.notificationsoff === 'No')) {
res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Nothing to send -- Time is not up`)
}
if ((record.snooze === 'Yes') && (new Date().getTime() - new Date(record.lastmodified).getTime() >= (1000 * 60 * 60 * 24 * 7))) {
await postgres(config.maindb).update({
emailcount: record.emailcount + 1,
snooze: 'No'
}).where('requestid', record.requestid)
.then(counter => {
res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Nothing to send -- Snooze: ${new Date().getTime() - new Date(record.lastmodified).getTime()}`)
})
.catch(e => console.log(e))
}
if ((record.returned === 'Yes') || (record.needtoreplace === 'Yes') || (record.notificationsoff === 'Yes') || (record.requesttype === 'Replace')) {
res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Nothing to send`)
}
} catch(e) {
console.log(e)
}
})
Trying to make a bot that when the users click on the reaction there discord id goes into an embed Field and if they un click or click another emoji they end up in the field. This is gonna be used for a voting bot that once a certain number of users click yes or no a decision will be made to accept a user or deny a user. Any help?
exports.run = async (client, message, args) => {
message.delete({ timeout: 100 });
if (!args[0]) return message.reply('You need to supply the question');
let embed = new Discord.MessageEmbed()
.setTitle(args.join(' '))
.setDescription('Poll created by ' + message.author.tag)
.addField('Status', 'Voting is currently open.')
.setColor('#ffd700')
.attachFiles(new Discord.MessageAttachment('https://i.imgur.com/QUmbq9o.png', 'thumbnail.png'))
.setThumbnail('attachment://thumbnail.png')
.setFooter('Bot created by James (Rock)₇₇₇');
message.channel.send(embed).then(async msg => {
await msg.react('👍');
await msg.react('👎');
await msg.react('🤷');
await msg.react('🗑️');
const threshold = 6;
async function stop(result) {
collector.stop();
const newEmbed = new Discord.MessageEmbed(msg.embeds[0]);
newEmbed.title = newEmbed.title + ' [CLOSED]';
newEmbed.fields[0] = { name: 'Status', value: 'Voting is now closed.\n' + result };
newEmbed.setThumbnail('attachment://thumbnail.png');
await msg.edit(newEmbed);
msg.reactions.removeAll();
}
async function update() {
const newEmbed = new Discord.MessageEmbed(embed);
const userYes = (votes['👍'].size === 0)? '-' : [...votes['👍']];
const userNo = (votes['👎'].size === 0)? '-' : [...votes['👎']];
const userUnsure = (votes['🤷'].size === 0)? '-' : [...votes['🤷']];
newEmbed.addFields(
{ name: `User Yes (${votes['👍'].size}/${threshold})`, value: userYes, inline: true },
{ name: `User No (${votes['👎'].size}/${threshold})`, value: userNo, inline: true },
{ name: 'User Unsure', value: userUnsure, inline: true }
);
await msg.edit(newEmbed);
if (votes['👍'].size >= threshold) {
await stop('This answer is good enough to get accepted and an upvote.');
// do something
} else if (votes['👎'].size >= threshold) {
await stop('This answer is not good enough to get accepted and an upvote.');
// do something
}
}
const votes = {
'👍': new Set(),
'👎': new Set(),
'🤷': new Set(),
'🗑️': new Set()
};
update();
const collector = msg.createReactionCollector((reaction, user) => !user.bot , { dispose: true });
collector.on('collect', async (reaction, user) => {
if (['👍', '👎', '🤷', '🗑️'].includes(reaction.emoji.name)) {
const userReactions = msg.reactions.cache.filter(reaction => reaction.users.cache.has(user.id));
for (const userReaction of userReactions.values()) {
if (userReaction.emoji.name !== reaction.emoji.name || reaction.emoji.name === '🗑️') {
userReaction.users.remove(user.id);
votes[userReaction.emoji.name].delete(user);
}
}
votes[reaction.emoji.name].add(user);
} else {
reaction.remove();
}
update();
});
collector.on('remove', (reaction, user) => {
votes[reaction.emoji.name].delete(user);
update();
});
});
};
module.exports.help = {
name: "poll"
}
You can read this page from the discord.js guide about reaction collectors, it tells you everything you need to know. You can read this for more info about the .createReactionCollector() method.
There are multiple ways to acheive what you want once you make the reaction collector but I beleive the easiest would look something like this:
message.channel.send('your_message_here')
.then(async function(message) {
await message.react('👍');
await message.react('👎');
await message.react('🤷');
const filter = (reaction, user) => {
return ['👍', '👎', '🤷'].includes(reaction.emoji.name) && user.id === ogauthor
}
const collector = message.createReactionCollector(filter)
collector.on('collect', (reaction, user) => {
async function collect() {
if (!user.bot) {
if (reaction.emoji.name === '👍') {
//code here
}
//repeat this for the rest of your reactions
reaction.users.remove(user.id) //you can remove the reaction once they react to it and their name is added.
}
}
collect()
});
})
One problem is that this will run forever so you should add a timer to it.
You can use Reaction Collectors for this case to listen to reactions and list them depending on it.
I've made the code below, it works as expected:
message.delete({ timeout: 100 });
if (!args[0]) return message.reply('You need to supply the question');
let embed = new Discord.MessageEmbed()
.setTitle(args.join(' '))
.setDescription('Poll created by ' + message.author.tag)
.setColor('#ffd700')
.setThumbnail("https://i.imgur.com/QUmbq9o.png")
.addFields({name: "User Yes", value: 'None'}, {name: "User No", value: 'None'}, {name: "User Hum", value: 'None'})
.setFooter("Bot created by James (Rock)₇₇₇");
message.channel.send(embed).then(async msg => {
await msg.react('👍');
await msg.react('👎');
await msg.react('🤷');
const filter = (reaction, user) => {
return ["👍", "👎", "🤷"].includes(reaction.emoji.name);
};
const collector = await msg.createReactionCollector(filter);
collector.on('collect', (reaction, user) => {
const reactionsList = ["👍", "👎", "🤷"];
const fieldTitle = ["User Yes", "User No", "User Hum"];
var reactions = reaction.message.reactions.cache.array();
for(var reactionID in reactions) {
for (var i = 0; i < reactionsList.length; i++) {
if(reactionsList[i] === reaction.emoji.name){
let fieldDescription = user.id + "\n";
var users = reactions[reactionID].users.cache.array();
for(var userID in users){
if(users[userID].id === client.user.id || users[userID].id === user.id) continue;
fieldDescription += users[userID].id + "\n";
}
embed.spliceFields(i, 1, {name: fieldTitle[i], value: fieldDescription})
}
}
}
msg.edit(embed);
});
})
I have slightly modified your code and added code to track votes, edit the embed and check when the votes reach the threshold.
Demonstration
Code
message.delete({ timeout: 100 });
if (!args[0]) return message.reply('You need to supply the question');
let embed = new Discord.MessageEmbed()
.setTitle(args.join(' '))
.setDescription('Poll created by ' + message.author.tag)
.addField('Status', 'Voting is currently open.')
.setColor('#ffd700')
.attachFiles(new Discord.MessageAttachment('https://i.imgur.com/QUmbq9o.png', 'thumbnail.png'))
.setThumbnail('attachment://thumbnail.png')
.setFooter('Bot created by James (Rock)₇₇₇');
message.channel.send(embed).then(async msg => {
await msg.react('👍');
await msg.react('👎');
await msg.react('🤷');
await msg.react('🗑️');
const threshold = 1;
async function stop(result) {
collector.stop();
const newEmbed = new Discord.MessageEmbed(msg.embeds[0]);
newEmbed.title = newEmbed.title + ' [CLOSED]';
newEmbed.fields[0] = { name: 'Status', value: 'Voting is now closed.\n' + result };
newEmbed.setThumbnail('attachment://thumbnail.png');
await msg.edit(newEmbed);
msg.reactions.removeAll();
}
async function update() {
const newEmbed = new Discord.MessageEmbed(embed);
const userYes = (votes['👍'].size === 0)? '-' : [...votes['👍']];
const userNo = (votes['👎'].size === 0)? '-' : [...votes['👎']];
const userUnsure = (votes['🤷'].size === 0)? '-' : [...votes['🤷']];
newEmbed.addFields(
{ name: `User Yes (${votes['👍'].size}/${threshold})`, value: userYes, inline: true },
{ name: `User No (${votes['👎'].size}/${threshold})`, value: userNo, inline: true },
{ name: 'User Unsure', value: userUnsure, inline: true }
);
await msg.edit(newEmbed);
if (votes['👍'].size >= threshold) {
await stop('This answer is good enough to get accepted and an upvote.');
// do something
} else if (votes['👎'].size >= threshold) {
await stop('This answer is not good enough to get accepted and an upvote.');
// do something
}
}
const votes = {
'👍': new Set(),
'👎': new Set(),
'🤷': new Set(),
'🗑️': new Set()
};
update();
const collector = msg.createReactionCollector((reaction, user) => !user.bot , { dispose: true });
collector.on('collect', async (reaction, user) => {
if (['👍', '👎', '🤷', '🗑️'].includes(reaction.emoji.name)) {
const userReactions = msg.reactions.cache.filter(reaction => reaction.users.cache.has(user.id));
for (const userReaction of userReactions.values()) {
if (userReaction.emoji.name !== reaction.emoji.name || reaction.emoji.name === '🗑️') {
userReaction.users.remove(user.id);
votes[userReaction.emoji.name].delete(user);
}
}
votes[reaction.emoji.name].add(user);
} else {
reaction.remove();
}
update();
});
collector.on('remove', (reaction, user) => {
votes[reaction.emoji.name].delete(user);
update();
});
});
I have a mailListenerWrapper.js where I define my imap class:
mailListenerWrapper.js:
const simpleParser = require("mailparser").simpleParser;
const Imap = require("imap");
const striptags = require("striptags");
const BlackListModel = require("./models/blacklist");
const RecEmailConfigModel = require("./models/emailConfig");
const ReceivedEmailModel = require("./models/receivedMail");
const Base64 = require("js-base64").Base64;
const _ = require("lodash");
const logger = require("./helpers/logger");
require("dotenv").config();
class MailListenerWrapper {
constructor() {
this.imap = null;
this.init();
}
async init() {
try {
const getRecEmailConfig = await RecEmailConfigModel.findOne({});
if (getRecEmailConfig) {
getRecEmailConfig.password = Base64.decode(getRecEmailConfig.password);
this.imap = new Imap({
user: getRecEmailConfig.user,
password: getRecEmailConfig.password,
host: getRecEmailConfig.host,
port: getRecEmailConfig.port,
tls: getRecEmailConfig.tls,
tlsOptions: {
rejectUnauthorized: false,
authTimeout: 10000
}
});
} else {
return false;
}
let lastMessage = 0;
const openInbox = cb => {
this.imap.openBox("INBOX", true, cb);
};
const regEmail = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/g;
this.imap.once("ready", () => {
openInbox((err, box) => {
if (err) {
logger.log({ level: "error", message: err });
return false;
}
lastMessage = box.messages.total;
this.imap.on("mail", numberOfNewMessages => {
const newEmail = {};
// const numberOfNewMessages = 0
lastMessage = lastMessage + numberOfNewMessages;
const f = this.imap.seq.fetch(lastMessage + ":" + lastMessage, {
bodies: ["HEADER.FIELDS (SUBJECT FROM)", ""],
struct: true
});
f.on("message", msg => {
msg.on("body", async stream => {
try {
const parsed = await simpleParser(stream);
if (parsed.headers.get("subject")) {
// ################ HEADER ################ \\
newEmail.title = parsed.headers.get("subject");
const getFrom = parsed.headers.get("from").text.split(" <");
// ############################ //
console.log(getFrom); // executes twice here already
// ########################### //
if (getFrom.length === 2) {
newEmail.name = getFrom[0];
newEmail.email = getFrom[1].match(regEmail).join("\n");
}
// ################ HEADER ################ \\
}
if (parsed.text) {
// ################ TEXT ################ \\
newEmail.text = striptags(parsed.text).toString("utf8");
// ################ TEXT ################ \\
}
if (
newEmail.name &&
newEmail.email &&
newEmail.title &&
newEmail.text
) {
const getEmailHostname = newEmail.email.replace(/.*#/, "");
const blacklists = await BlackListModel.find({
hostname: new RegExp(".*" + getEmailHostname + ".*", "i")
});
if (blacklists.length > 0) {
logger.log({
level: "info",
message:
"A BLACKLIST ENTRY WAS TRYING TO SUBMIT AN APPLICATION"
});
return false;
}
const savedEmail = new ReceivedEmailModel(newEmail);
await savedEmail.save();
logger.log({
level: "info",
message:
"SAVED RECEIVED EMAIL =>" + JSON.stringify(savedEmail)
});
}
} catch (err) {
logger.log({ level: "error", message: err });
}
}); // msg on body
});
}); // on mail
this.imap.once("error", err => {
logger.log({
level: "error",
message: err
});
}); // on error
}); // openInbox
}); // ready
this.imap.connect();
} catch (err) {
logger.log({
level: "error",
message: err
});
}
}
close() {
if (this.imap) this.imap.end();
}
}
module.exports = MailListenerWrapper;
In app.js:
global.mailListenerWrapper = new mailListenerWrapper();
The receivedEmail.js model:
const mongoose = require("mongoose");
// Schema variable
const Schema = mongoose.Schema;
// Tags
const receivedMailSchema = new Schema(
{
title: {
type: String,
required: true
},
from: {
name: {
type: String
},
email: {
type: String
}
},
text: {
type: String
}
},
{
collection: "receivedEmail",
timestamps: true
}
);
const ReceivedEmail = mongoose.model("ReceivedEmail", receivedMailSchema);
module.exports = ReceivedEmail;
THE PROBLEM
The console log executes twice and the email is saved on my database twice, and does not save the address that it was sent from.
I have no idea why this could be happening. Could there be some way that the imap class is being instantiated twice?
I guess I'm pretty late, but maybe it could still be useful for anyone else.
I experienced this issue as well. But I realized that node-imap documetation specifies the use of .once() instead of .on() in the sample code.
Try to change the event below:
msg.on("body", async stream => {
....
}
To:
msg.once("body", async stream => {
....
}
It did work for me!
i m new to node and search a lot find solution but don't know how to use with my condition if anyone pro who can help me with this thing
Permissiontb.assembleAndInsert = async (ctx, cb) => {
for (let i = 0; i < ctx.req.body.view.length; i++) {
console.log('outside i '+i)
await Process(ctx,i);
}
};
function Process(ctx,i) {
let data={company_id:ctx.args.options.accessToken.userId,
userid:ctx.req.body.userid,
perpage:ctx.req.body.perpage[i].id,
view:ctx.req.body.view[i],
edit:ctx.req.body.edit[i],
update:ctx.req.body.update[i],
delete:ctx.req.body.delete[i]}
console.log('inside '+i)
return new Promise((resolve, reject) => {
Permissiontb.find({where:{and: [{userid:ctx.req.body.userid}, {perpage:ctx.req.body.perpage[i].id}] }},function(err,result){
if(err){
var err = new Error('Some thing went wrong');
err.statusCode = 444;
console.log('err'+err)
resolve(err)
}else{ console.log(result.length)
if(result.length>0){
console.log(ctx.req.body.userid)
console.log('page id '+ctx.req.body.perpage[i].id)
const pgid=ctx.req.body.perpage[i].id
const vd=ctx.req.body.view[i]
const ed=ctx.req.body.edit[i]
const up=ctx.req.body.update[i]
const del=ctx.req.body.delete[i]
console.log(ctx.req.body.view[i])
console.log(ctx.req.body.edit[i])
console.log(ctx.req.body.update[i])
console.log(ctx.req.body.delete[i])
console.log('findinside update '+i)
resolve(Permissiontb.update({where:{and: [{userid:ctx.req.body.userid}, {perpage:pgid}] }},{view:vd,edit:ed,update:up,delete:del}))
}else{
resolve(Permissiontb.create([data]))
}
}
})
})
}
now problem is that console log inside if(result.lenght>0) condition for eg:- console.log(ctx.req.body.view[i]) correct value getting but when update method always inserting last array value can anyone tell me what i m doing wrong
finally i found soultion may be this will help other who facing issue like me
var async = require('async');
module.exports = function(Permissiontb) {
Permissiontb.assembleAndInsert = async (ctx, cb) => {
for (let i = 0; i < ctx.req.body.view.length; i++) {
await Process(ctx,i);
}
};
Permissiontb.remoteMethod('assembleAndInsert', {
http: {
path: '/assembleAndInsert',
verb: 'post',
},
accepts: [{ arg: 'data', type: 'object', http: { source: 'context' } },
{"arg": "options", "type": "object", "http": "optionsFromRequest"}],
returns: {
arg: 'data',
type: 'object',
},
});
function Process(ctx,i) {
let data={company_id:ctx.args.options.accessToken.userId,
userid:ctx.req.body.userid,
perpage:ctx.req.body.perpage[i].id,
view:ctx.req.body.view[i],
edit:ctx.req.body.edit[i],
update:ctx.req.body.update[i],
delete:ctx.req.body.delete[i]}
return new Promise((resolve, reject) => {
resolve(Permissiontb.upsertWithWhere({userid:ctx.req.body.userid,perpage:ctx.req.body.perpage[i].id},data))
})
}
problem is that i m using Permissiontb.upsertWithWhere(//im using where here->{userid:ctx.req.body.userid,perpage:ctx.req.body.perpage[i].id},data))