Having issues in Discord.js with Promise - node.js

i'm trying to make this work, but there's this issue : UnhandledPromiseRejectionWarning: DiscordAPIError: Cannot send an empty message
let regex = /<:\s([a-zA-Z])\w+:\s([0-9])\w+>|<a:\s([a-zA-Z])\w+:\s([0-9])\w+>|<:([a-zA-Z])\w+:([0-9])\w+>|<#!([0-9])\w+>|<#!\s([0-9])\w+>|<#\s([0-9])\w+>|<#([0-9])\w+>|<#([0-9])\w+>/g
async function traduire(msg, lang) {
translate(msg, {
to: lang
}).then(res => {
str = res.text
if (!msg.match(regex)) return str.toString()
MsgJoin = msg.split(" ").join("")
if (MsgJoin.match(regex)) {
MsgRegex = MsgJoin.match(regex)
}
let RegexStr = str.match(/<(.*?)\>/g)
let MidMsg = str
for (let i = 0; i < MsgRegex.length; i++) {
MidMsg = MidMsg.replace(RegexStr[i], MsgRegex[i])
FinalMsg = MidMsg
}
return FinalMsg.toString()
})
}
bot.on('message', msg => {
try {
let MessTrans = msg.content;
let reactionFilter = (reaction, user) => reaction.emoji.name === '🇫🇷' && msg.author.id !== "724682218454253588"
msg.awaitReactions(reactionFilter, {
max: 1
}).then(() => {
const traduction = await traduire(MessTrans, "fr")
msg.channel.send(traduction)
})
} catch (error) {
msg.channel.send('There\'s an error, try again !')
}
})
the issue is from the msg.channel.send(traduction) but i don't know how to fix it, all I know is that it's a Promise error, and I have to use async / await.

Related

Need Deep Understanding async/await on NodeJS

I've been trying to understand async/await and Task in NodeJS. I have implemented I need to update db when all process executed.
Below is code samples
const download = require('image-downloader');
const imageDownload = (imgurl) =>{
console.log("inside imageDownload ", new Date())
return new Promise(async(resolve) => {
let imgpath = '/mount/imageDownload/';
if (!fs.existsSync(imgpath)) {
fs.mkdirSync(imgpath, {
recursive: true
});
}
const options = {
url: imgurl,
dest: imgpath
};
console.log("before download image " + assetId + " ", new Date())
download.image(options)
.then(({ filename }) => {
resolve({"error":false, "url":filename});
})
.catch((err) => {
console.log(err);
resolve({"error":true,"message":err});
});
});
}
(async () => {
let downloadUrl1 = "";
let downloadUrl2 = "";
let downloadUrlFirst = await imageDownload("https://dummyimage.com/300.png/09f/fff");
if (typeof downloadUrlFirst.url != 'undefined' && downloadUrlFirst.url != null) {
downloadUrl1 = downloadUrlFirst.url;
}
let downloadUrlSecond = await imageDownload("https://dummyimage.com/300.png/09f/fff");
if (typeof downloadUrlSecond.url != 'undefined' && downloadUrlSecond.url != null) {
downloadUrl2 = downloadUrlSecond.url;
}
if (downloadUrl1 != '' || downloadUrl2 !='' ) {
let updateImagePayload = {}
if (portrait_mounturl != '') {
updateImagePayload.downloadUrl1 = downloadUrl1;
}
if (landscape_mounturl != '') {
updateImagePayload.downloadUrl2 = downloadUrl2;
}
updateImagePayload.modified_date_time = new Date().toISOString();
db.collection("images").findOneAndUpdate({_id:ObjectId(assetId)}, { $set: updateImagePayload }, (err, update) => {
if (err) {
resolve({"error":true,"message": err});
} else {
let resolveStatus = false;
let resolveMessage = "Successfully updated image";
resolve({"error":resolveStatus,"message":resolveMessage});
}
});
}
})();
I need to update if both has async proccess completed.
Note: Image will be optional. Means either 1 image or both
Getting High Memory and CPU utilization
Any help understanding this would be much appreciated.

I can't figure out how to use Promise correctly in this context

webhookClient.send(temp);
executed earlier than
temp['files'] = [link];
contains++;
I need to wait for this.
tg_bot.getFileLink - promise
How to sync it correctly?
let contains = 0
let temp = {
username: `${msg.from.first_name} tg`,
};
const user_profile = tg_bot.getUserProfilePhotos(msg.from.id);
user_profile
.then(function (res) {
tg_bot.getFileLink(res.photos[0][0].file_id)
.then(link => { temp['avatarURL'] = link;})
.then( () => {
if (msg.text !== undefined) {
temp['content'] = msg.text;
contains++;}})
.then(() => {
if (msg.photo !== undefined) {
let link = tg_bot.getFileLink(msg.photo.slice(-1)[0].file_id)
.then(link =>{
temp['files'] = [link];
contains++;})}})
.then(() => {
if (contains <= 0) temp['content'] = 'empty';
console.log(temp);
webhookClient.send(temp);})
});
This code really lends itself to using await so you don't have to deal with nested .then() handlers and it's much easier to deal with branching in your code flow. This is a classic example of why await was invented (to make code like this simpler to write and debug).
This assumes it is inside an async tagged function:
let contains = 0
let temp = {
username: `${msg.from.first_name} tg`,
};
const res = await tg_bot.getUserProfilePhotos(msg.from.id);
temp['avatarURL'] = await tg_bot.getFileLink(res.photos[0][0].file_id);
if (msg.text !== undefined) {
temp['content'] = msg.text;
contains++;
}
if (msg.photo !== undefined) {
let link = await tg_bot.getFileLink(msg.photo.slice(-1)[0].file_id);
temp['files'] = [link];
contains++;
}
if (contains <= 0) {
temp['content'] = 'empty';
}
webhookClient.send(temp);
If you wanted to make this work without await, using only .then(), then you just have to chain the conditional call to getFileLink() (by returning its promise) so that the next .then() block will wait for it:
let contains = 0;
let temp = {
username: `${msg.from.first_name} tg`,
};
const user_profile = tg_bot.getUserProfilePhotos(msg.from.id);
user_profile.then(function(res) {
return tg_bot.getFileLink(res.photos[0][0].file_id).then(link => {
temp['avatarURL'] = link;
}).then(() => {
if (msg.text !== undefined) {
temp['content'] = msg.text;
contains++;
}
if (msg.photo !== undefined) {
return tg_bot.getFileLink(msg.photo.slice(-1)[0].file_id).then(link => {
temp['files'] = [link];
contains++;
});
}
}).then(() => {
if (contains <= 0) {
temp['content'] = 'empty';
}
console.log(temp);
webhookClient.send(temp);
});
});
Note how I've also flattened the nesting and combined .then() blocks that didn't have to be separate.
Note: both these implementations needs error handling. The first needs a try/catch around it to catch any rejections from any of the await statements (or the caller needs to catch the rejected promise the async function would return.
The second implementation needs a .catch() at the end of the chain.

Why on my NodeJS+Express REST API a promise calling my function fails while the same promise with setTimeout works?

I have a NodeJS+Express REST API method executing reverse geocoding (using Google's Maps API).
I'm trying to solve it with Promises but the 'then' is getting executed before my function returns with the answers from Google.
When testing the same code just calling a setTimeout, it works as expected. Please see comments in the code (simplify version).
app.get('/api/v1/events', verifyToken, async (req, res) => {
await db.poolPromise.then(pool => {
return pool.request()
.input('UserId', db.sql.UniqueIdentifier, res.authData.userId)
.input('DateFrom', db.sql.DateTime2(7), req.query.dateFrom)
.input('DateTill', db.sql.DateTime2(7), req.query.dateTo)
.output('UserIdAuthorized', db.sql.Bit)
.execute('sp')
}).then(result => {
let output = (result.output || {})
if (!output.UserIdAuthorized) {
res.sendStatus(403)
}
else if (result.recordset.length > 0) {
(new Promise( (resolve) => {
//resolve(123) // this one works as expected
//setTimeout(resolve, 3000, 'temp success') // this one works as expected
// *** this one get passed and the following then is being executed before it answers ***
resolve( getAddress_TEST(result.recordset) )
// **************************************************************************************
})).then(function (value) {
res.json(
{
meta: { count: 10 }, //this is just a sample
result: value // *** this one fails with undefined ***
})
})
} else {
res.sendStatus(404)
}
}).catch(err => {
res.sendStatus(500)
console.error(err)
})
});
const nodeGeocoder_options = {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY
}
async function getAddress_TEST(recordset) {
//sample recordset for debugging - as you dont have my database
recordset = [{'eventId':14205556,'Lat':54.57767,'Lon':-2.4920483},{'eventId':14205558,'Lat':54.57767,'Lon':-2.492048},{'eventId':14205579,'Lat':53.416908,'Lon':-2.952071},{'eventId':14205588,'Lat':52.644448,'Lon':-1.153185},{'eventId':14205601,'Lat':52.29174,'Lon':-1.532283},{'eventId':14205645,'Lat':52.644448,'Lon':-1.153185},{'eventId':14205801,'Lat':53.68687,'Lon':-1.498708},{'eventId':14206041,'Lat':51.471521,'Lon':-0.2038033},{'eventId':14206049,'Lat':51.471521,'Lon':-0.2038033},{'eventId':14206072,'Lat':51.471521,'Lon':-0.2038033}]
let geocoder = nodeGeocoder(nodeGeocoder_options)
let ps = []
for (var i = 0, length = recordset.length; i < length; i++) {
if (i == 0 || !(i > 0
&& recordset[i - 1].Lat == recordset[i].Lat
&& recordset[i - 1].Lon == recordset[i].Lon)) {
ps.push(new Promise(function (resolve) {
resolve(reverseGeocode(geocoder, recordset[i].Lat, recordset[i].Lon))
}))
} else {
ps.push('-')
}
}
await Promise.all(ps)
.then(function (values) {
for (var i = 0, length = values.length; i < length; i++) {
if (values[i] != '-') {
recordset[i].locationAddress = values[i]
} else {
recordset[i].locationAddress = recordset[i - 1].locationAddress
}
}
}).then(function () {
recordset.forEach(function (v) {
delete v.Lat
delete v.Lon
});
console.log(recordset)
return recordset
})
};
async function reverseGeocode(geocoder, lat, lon) {
let address = '+'
if (lat != 0 && lon != 0) {
await geocoder.reverse({ lat: lat, lon: lon })
.then(res => {
address = res[0].formattedAddress
})
.catch(err => {
console.error(err)
});
}
return address
};
I'm sure it is something simple that I'm missing here...
The basic problem is that your getAddress_TEST function returns a promise that fulfills with nothing (undefined), because it does not contain a return statement. The return recordset is in a then() callback, from where it affects the promise resolution of the awaited promise, but that result is thrown away.
If you want to use async/await, you should get rid of any new Promise and then calls:
app.get('/api/v1/events', verifyToken, async (req, res) => {
try {
const pool = await db.poolPromise
const result = await pool.request()
.input('UserId', db.sql.UniqueIdentifier, res.authData.userId)
.input('DateFrom', db.sql.DateTime2(7), req.query.dateFrom)
.input('DateTill', db.sql.DateTime2(7), req.query.dateTo)
.output('UserIdAuthorized', db.sql.Bit)
.execute('sp')
let output = (result.output || {})
if (!output.UserIdAuthorized) {
res.sendStatus(403)
} else if (result.recordset.length > 0) {
const value = await getAddress_TEST(result.recordset)
res.json({
meta: { count: 10 }, //this is just a sample
result: value
})
} else {
res.sendStatus(404)
}
} catch(err) {
res.sendStatus(500)
console.error(err)
}
});
const nodeGeocoder_options = {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY
}
async function getAddress_TEST(recordset) {
const geocoder = nodeGeocoder(nodeGeocoder_options)
const ps = recordset.map((record, i) => {
if (i == 0 || !(i > 0
&& recordset[i - 1].Lat == record.Lat
&& recordset[i - 1].Lon == recordLon)) {
return reverseGeocode(geocoder, recordset[i].Lat, recordset[i].Lon))
} else {
return '-'
}
});
const values = await Promise.all(ps)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (var i = 0, length = values.length; i < length; i++) {
if (values[i] != '-') {
recordset[i].locationAddress = values[i]
} else {
recordset[i].locationAddress = recordset[i - 1].locationAddress
}
}
recordset.forEach(function (v) {
delete v.Lat
delete v.Lon
});
console.log(recordset)
return recordset
// ^^^^^^^^^^^^^^^^
}
async function reverseGeocode(geocoder, lat, lon) {
if (lat != 0 && lon != 0) {
const res = await geocoder.reverse({ lat: lat, lon: lon })
return res[0].formattedAddress
}
return '+'
}

Node js Scraper

I have written a scraper in typescript, Running on node:10.12.0,
Issue: The code goes on sleep after few hours, randomly. And I had to restart it. My best guess is it stucks on url request
Tools/Packages Using:
Puppeteer
Cheerio
Typescript
Code:
import * as cheerio from "cheerio";
import * as request from "request";
import * as fs from "fs";
import * as shell from "shelljs";
import pup = require("puppeteer");
class App {
// #ts-ignore
public browser: pup.Browser;
public appendToFile(file: string, content: string): Promise < string > {
return new Promise < string > ((resolve, reject) => {
try {
fs.appendFileSync(file, content);
resolve("DONE");
} catch (e) {
reject(e);
}
});
}
public loadPage(url: string): Promise < any > {
return new Promise < any > ((resolve, reject) => {
request.get(url, async (err, res, html) => {
if (!err && res.statusCode === 200) {
resolve(html);
} else {
if (err) {
reject(err);
} else {
reject(res);
}
}
});
});
}
public step1(url: string): Promise < string > {
return new Promise < string > (async (resolve, reject) => {
let page: pup.Page | undefined;
try {
let next = false;
let urlLink = url;
let first = true;
let header = "unknown";
let f = url.split("/");
let folder = f[f.length - 3];
folder = folder || header;
let path = "data/" + folder;
shell.mkdir("-p", path);
page = await this.browser.newPage();
await page.goto(url, {
timeout: 0
});
let count = 1;
do {
next = false;
let res = await page.evaluate(() => {
let e = document.querySelectorAll(".ch-product-view-list-container.list-view li ul > li > h6 > a");
let p: string[] = [];
e.forEach((v) => {
p.push(("https://www.link.com") + (v.getAttribute("href") as string));
});
return p;
});
// for(const l of res) {
// try {
// await this.step2(l, "" , "")
// } catch(er) {
// this.appendToFile("./error.txt", l + "::" + url + "\n").catch(e=>e)
// }
// }
let p = [];
let c = 1;
for (const d of res) {
p.push(await this.step2(d, folder, c.toString()).catch((_e) => {
console.log(_e);
fs.appendFileSync("./error-2.txt", urlLink + " ### " + d + "\n");
}));
c++;
}
await Promise.all(p);
await this.appendToFile("./processed.txt", urlLink + ":" + count.toString() + "\n").catch(e => e);
count++;
console.log(urlLink + ":" + count);
let e = await page.evaluate(() => {
let ele = document.querySelector("#pagination-next") as Element;
let r = ele.getAttribute("style");
return r || "";
});
if (e === "") {
next = true;
await page.click("#pagination-next");
// console.log('waitng')
await page.waitFor(1000);
// console.log('done wait')
// await page.waitForNavigation({waitUntil: 'load'}).catch(e=> console.log(e));
// await Promise.all([
// page.click("#pagination-next"),
// page.waitForNavigation({ waitUntil: 'networkidle0'}), // ]);
}
} while (next);
// await page.close();
resolve("page all scrapped");
} catch (errrr) {
reject(errrr);
} finally {
if (page !== undefined) {
await page.close().catch(e => e);
}
}
});
}
public step2(url: string, folder: string, file: string): Promise < string > {
return new Promise < string > (async (resolve, reject) => {
try {
let html = await this.loadPage(url).catch(e => reject(e));
let $ = cheerio.load(html);
let ress: any = {};
let t = $(".qal_title_heading").text();
if (t) {
ress.header = t.replace(/"/g, "'").replace(/\n|\r|\t/g, "");
}
let d = $("div.ch_formatted_text.qal_thread-content_text.asker").html();
if (d) {
ress.body = d.replace(/"/g, "'").replace(/\n|\r|\t/g, "");
}
// let sprit = "-------------------------------";
let filename = "data" + file + ".json"; // ((t.replace(/[^\w\s]/gi, "")).substring(0,250)+".txt")
let data = JSON.stringify(ress) // t +sprit + d + "\n---end---\n"; await this.appendToFile("./data/"+ folder + "/" +filename, data+",\n")
.then((r) => {
resolve(r);
});
} catch (err) {
reject(err);
}
});
}
}
async function main() {
process.on("SIGTERM", () => {
console.log("SigTerm received");
process.exit(1);
});
process.on("SIGINT", () => {
console.log("SigInt received");
process.exit(1);
});
let path = "data/unknown";
shell.mkdir("-p", path);
let c = new App();
let list: string[] = [];
console.log(process.argv[2]);
require("fs").readFileSync(process.argv[2], "utf-8").split(/\r?\n/).forEach((line: string) => {
list.push(line);
});
console.log("total links->" + list.length);
c.browser = await pup.launch({
headless: true
});
for (const l of list) {
await c.step1(l).then(e => {
fs.appendFileSync("./processed.txt", l);
}).catch(e => {
fs.appendFileSync("./error.txt", l);
});
}
}
main();
Let me know if you need something else from me. Also this is all the code.
So , I figured two problems.
The chrome (under puppeteer) consumes high CPU, which gives the trend like this:
at start it's on moderate usage. and it gradually increases. My trend was it started off with 4% usage and after a day, it reached 100%. I've submitted an issue on their git
I did not specify the timeout in request
was:
request.get(url, async (err, res, html) => {
should be:
request.get(url,{timeout: 1500} async (err, res, html) => {
So far my code is running fine for more than a day now. only issue is high cpu usage. But it's none of my concern as for now.

Last then function being called multiple times

I'm trying to write a chain of Promises but the last .then() is being called multiple times and I don't know why. The last .then() must run a single time because it will call another API passing result as body.
I know that is being called multiple times because I'm logging as console.log().
What is wrong on my code? For my understand, then() should wait promise returns something.
app.post('/router/join', function(req, res){
let data = req.body;
sessions.validate(data)
.then(result => {
return {
authenticated: (result.code === 201)
};
})
.then(result => {
if(result.authenticated){
return contacts.getContacts(data.tenant_id).then(cs => {
let json = merge(result, cs.data);
return Promise.all(cs.data.items.map(contact => {
return messages.getLastMessage(data.tenant_id, contact.item.contact_id, data.hash_id)
.then(result => {
contact.item.last_message = result.code === 200 && result.data.length > 0 ? result.data[0] : null;
return contact;
});
})).then(result => {
json.items = result;
return json;
});
});
} else {
return result;
}
})
.then(result => {
//this call should run after all other promises and only a single time
let event = result.authenticated ? 'valid_session' : 'invalid_session';
console.log('222');
proxy.send(event, result)}
)
.catch(err => {
console.log('333');
proxy.send('invalid_session', {socket_id: data.socket_id})
})
res.status(201).send({});
});
You can use async/await to clean it up. Inside async functions you can await the results of promises.
app.post('/router/join', async function (req, res, next) {
try {
let data = req.body;
let {code} = await sessions.validate(data);
let result = { authenticated: (code === 201) };
if (result.authenticated) {
let cs = await contacts.getContacts(data.tenant_id);
let json = merge(result, cs.data);
let items = Promise.all(cs.data.items.map(async contact => {
let result = await messages.getLastMessage(data.tenant_id, contact.item.contact_id, data.hash_id)
contact.item.last_message = result.code === 200 && result.data.length > 0 ? result.data[0] : null;
return contact;
}));
json.items = items;
result = json;
}
let event = result.authenticated ? 'valid_session' : 'invalid_session';
console.log('222');
proxy.send(event, result);
res.status(201).send({});
} catch (err) {
proxy.send('invalid_session', {socket_id: data.socket_id})
next (err);
}
});

Resources