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.
Related
I am trying to collect a button and then check if button.id is cash , here is my code
const msg = message.reply({embeds: [dembed], components: [row1, row2]}).then(function (msg) {
const filter = i => {
i.deferUpdate();
return i.user.id === message.author.id;
};
const collector = message.createMessageComponentCollector({ componentType: 'BUTTON', filter ,time: 15000 });
collector.on('collect', i => {
if(i.customId === 'cash') {
msg.channel.send("test")
}
})
})
When creating the messageComponentCollector you are telling it to monitor the first message which the user posted for any button clicks. Since the buttons are in the msg variable instead of the message variable, you will just need to change that one to msg. Your fixed code should look like this:
const msg = message.reply({ embeds: [dembed], components: [row1, row2] }).then(function (msg) {
const filter = i => {
i.deferUpdate();
return i.user.id === message.author.id;
};
const collector = msg.createMessageComponentCollector({ componentType: 'BUTTON', filter, time: 15000 });
collector.on('collect', i => {
if (i.customId === 'cash') {
msg.channel.send("test")
}
})
})
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'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);
});
});
};
I am new to Express and writing the code to get the list from my database. I'm trying to update the quantity of the items in my list. Now there can be multiple items and quantity for those items needs to be updated accordingly. The problem I am facing is when I try to get the list and update item accordingly, before my for loop executes to update the item it doesn't update the item's quantity in the database and saves the order. What am I doing wrong?
I have used async functions, promises and flags to update the items quantity in the database but none helps.
This is my code for to get and update the item's quantity
const Express = require("express");
const app = Express.Router();
const Menu = require("../../models/Menu");
const Order = require("../../models/order");
const User = require("../../models/user");
app.post(
"/create",
async function(req, res) {
var myorder = {};
var orderList = [];
var ordDetail = [];
var UpdateMenus = [];
orderList = JSON.parse(JSON.stringify(req.body["OD"]));
if(orderList.length>0){
const user = await User.findOne({ _id: req.user.id })
.then(user => {
if (!user) {
return res.status(400).json({ error: "User Not Found" });
}
})
.then(() => {
var order = Order({
user: req.user.id
});
myorder = order;
(async function loop() {
for (i = 0; i < orderList.length; i++) {
const ordt = new Object({
menu: orderList[i]["menuId"],
order: myorder.id,
prize: orderList[i]["prize"],
quantity: orderList[i]["quantity"]
});
await Menu.findOne({ _id: orderList[i]["menuId"] })
.exec()
.then(menu => {
if (menu) {
if (menu.quantity >= ordt.quantity) {
menu.quantity = menu.quantity - ordt.quantity;
const editmenu = menu;
(async function updateTheMenu() {
await Menu.findOneAndUpdate(
{ _id: menu.id },
{ $set: editmenu },
{
new: true,
useFindAndModify: false
}
).then(updateMenu => {
console.log(updateMenu);
ordDetail.push(ordt);
});
})();
} else {
return res.status(400).json({
error:
menu.MenuText +
"" +
ordt.quantity +
" Qunatity Is Not Available"
});
}
}
});
}
})();
}).then(()=>{
order
.save()
.then(order => {
if (!order) {
return res.json({ error: "Order is not saved" });
}
res.status(200).json(order);
})
.catch(error => {
return res
.status(400)
.json({ error: "Fields are Not Correct" });
});
});
}
}
);
There are few things wrong with your code:
If you use await then you don't need to use then. You can just assign to a variable. Example:
const menu = await Menu.findOne({ _id: orderList[i]["menuId"] })
You don't need to wrap your loop and every await call in async functions. They are already in an async function.
You can write your response handler like this:
app.post('/create', async function(req, res) {
var myorder = {};
var orderList = [];
var ordDetail = [];
var UpdateMenus = [];
orderList = JSON.parse(JSON.stringify(req.body['OD']));
if (orderList.length > 0) {
const user = await User.findOne({ _id: req.user.id });
if (!user) {
return res.status(400).json({ error: 'User Not Found' });
}
var order = Order({
user: req.user.id
});
myorder = order;
for (i = 0; i < orderList.length; i++) {
const ordt = new Object({
menu: orderList[i]['menuId'],
order: myorder.id,
prize: orderList[i]['prize'],
quantity: orderList[i]['quantity']
});
const menu = await Menu.findOne({ _id: orderList[i]['menuId'] });
if (menu) {
if (menu.quantity >= ordt.quantity) {
menu.quantity = menu.quantity - ordt.quantity;
const editmenu = menu;
const updateMenu = await Menu.findOneAndUpdate(
{ _id: menu.id },
{ $set: editmenu },
{
new: true,
useFindAndModify: false
}
);
console.log(updateMenu);
ordDetail.push(ordt);
} else {
return res
.status(400)
.json({
error:
menu.MenuText +
'' +
ordt.quantity +
' Qunatity Is Not Available'
});
}
}
}
try {
const savedOrder = await order.save();
if (!savedOrder) {
return res.json({ error: 'Order is not saved' });
}
res.status(200).json(savedOrder);
} catch (error) {
return res.status(400).json({ error: 'Fields are Not Correct' });
}
}
});
I'm having a hard time understanding promises in Firebase functions. I have a function that listens for new files in a storage bucket and then emails the user as well as sending them a Discord message. I'm getting inconsistent results and I'm pretty sure its to do with promises and callbacks being setup incorrectly.
exports.sendSetup = functions.storage.bucket('*the-bucket-id*').object().onFinalize((object) => {
// Get a URL for the new config file
console.log('New conf file: ' + object.name);
const { Storage } = require('#google-cloud/storage');
const storage = new Storage({
projectId: '*the-project-id*',
keyFilename: 'googleServiceAccountKey.json'
});
var bucket = storage.bucket('*the-bucket-name*');
const file = bucket.file(object.name);
console.log('Generating download url for file ' + object.name);
return file.getSignedUrl({
promptSaveAs: '*the-file-name*',
action: 'read',
expires: '03-09-2491'
}).then(signedUrls => {
var split = object.name.split("/");
var env = split[0];
var customer_id = split[1];
getCustomerDetails(customer_id, signedUrls[0], env);
});
});
function getCustomerDetails(customer_id, fileUrl, env) {
console.log('Retrieving customer details for customer id ' + customer_id + ' from Stripe');
var stripe = stripeLive;
if (env == 'test') {
stripe = stripeTest;
}
stripe.customers.retrieve(
customer_id,
function (err, customer) {
if (err == null) {
sendMail(fileUrl, customer.email, customer_id, customer.metadata.name);
console.log('discordId= ' + customer.metadata.discordId);
if (customer.metadata.discordId != 'undefined') {
sendDiscord(fileUrl, customer.metadata.discordId, customer.metadata.discordName);
}
console.log('Finished');
} else {
console.log(err);
}
}
);
}
function sendDiscord(fileUrl, discordId, discordName) {
console.log('Attempting to send a discord message to Discord id ' + discordId);
const Discord = require('discord.js');
const client = new Discord.Client();
client.login('*discord-api-key*');
client.once('ready', () => {
console.log('Discord client ready');
client.fetchUser(discordId)
.then((User) => {
console.log('Got Discord user object. Attempting to send message');
return User.send({
embed: {
color: 3447003,
fields: [
{
name: 'Hey ' + discordName + '!',
value: 'Below are the instructions to get you up and running'
},
{
name: '**Step 1**',
value: 'some instructions'
}
]
}
});
})
.catch((err) => {
console.log(err);
})
});
}
function sendMail(fileUrl, customer_email, customer_id, customer_name) {
console.log('customer_name in sendMail function = ' + customer_name);
var firstName = customer_name.substring(0, customer_name.indexOf(' '));
console.log(firstName);
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(*sendGridApiKey*);
sgMail.setSubstitutionWrappers('{{', '}}'); // Configure the substitution tag wrappers globally
const msg = {
to: customer_email,
subject: 'Welcome!',
from: {
email: 'noreply#example.com.au',
name: 'me'
},
text: 'Let\'s get you setup...',
html: '<p></p>',
templateId: '*template-id*',
substitutions: {
first_name: firstName,
file_url: fileUrl
},
};
console.log('Sending email to ' + customer_email + ' customer id:' + customer_id);
sgMail.send(msg);
}
I've read a heap of articles about promises and callbacks but can't seem to wrap my head around it. The "sendSetup" function actually returns OK but appears to stop right at the start of the getCustomerDetails function. Appreciate any assistance! I'm a bit lost!