How to properly order async functions in node.js? - node.js

i got 3 adaptive cards that should display correctly one after the other, depending on which button is pushed. Unfortunately they just show randomly till the wished one appears. My function is pretty miserable and consumes too much in testing. Any suggestion is welcomed.
InputCard works fine cos is attached at the beginning in the index.js file, the mess starts later with the three ones exposed here. InputCard is repeated cos of the context.sendActivity('Back to menu').
async processCards (context, luisResult, next) {
this.logger.log('process');
// Retrieve LUIS result for Process Automation.
const result = luisResult.connectedServiceResult;
const intent = result.topScoringIntent.intent;
let InputCard = require('../resources/FaqCard.json');
let InputCard1 = require('../resources/InputCard.json');
let InputCard3 = require('../resources/UserPsw.json');
let CARDS = [
InputCard,
InputCard1,
InputCard3
];
let reply = { type: ActivityTypes.Message };
let buttons = [
{ type: ActionTypes.Submit, title: 'F.A.Q', value: '1' },
{ type: ActionTypes.Submit, title: 'Back to menu', value: '2' },
{ type: ActionTypes.Submit, title: 'login', value: '3' }
];
let card = CardFactory.heroCard('say what???', undefined,
buttons, { text: 'You can upload an image or select one of the following choices.' });
reply.attachments = [card];
let SelectedCard = CARDS[Math.floor((Math.random() * CARDS.length - 1) + 1)];
if (await context.sendActivity(`F.A.Q`))
return context.sendActivity({
attachments: [CardFactory.adaptiveCard(SelectedCard)]
});
if (await context.sendActivity('Back to menu'))
return context.sendActivity({
attachments: [CardFactory.adaptiveCard(SelectedCard) = this.CARDS(InputCard)]
});
if (await context.sendActivity('login'))
return context.sendActivity({
attachments: [CardFactory.adaptiveCard(SelectedCard) = this.CARDS(InputCard3)]
});
await context.sendActivity(`processCards top intent ${intent}.`);
await context.sendActivity(`processCards intents detected: ${luisResult.intents.map((intentObj) => intentObj.intent).join('\n\n')}.`);
if (await context.sendActivity(`myName ${intent}.` == 'myName')) {
return context.sendActivity(`ok, anything else????`);
}
if (luisResult.entities.length > 0) {
await context.sendActivity(`processCards entities were found in the message: ${luisResult.entities.map((entityObj) => entityObj.entity).join('\n\n')}.`);
await context.sendActivity(`ok, anything else?`);
}
await next();
}
After InputCard,should be possible to choose between FaqCard.json or UserPsw.json and after that,if wished, returning to InputCard too.

Related

NodeJs parallel request in a loop too slow

so I have this feature that I am working on that takes an object (Over 10k items) picks an item from this object, sends it to an api, which process it then give a response, then proceeds to the next item.
Currently using the async library, the mapLimit method and it works as aspected.
But my problem is that it’s takes too long to loop through the entire dataset cause of the length.
This feature is suppose to be a continual process, once the entire object is iterated, wait for few seconds then do the same thing again.
I tried forking a child_process for this, broke the objects into chunks and created a process for each chunks till it was completed. This worked well as intended but the memory consumption was massive other processes on the server failed as a result of lack of available memory, even after exiting the process after it was completed.
Please how do I achieve this at a faster rate?
I use this to get the list of wallets.
getListofWallet = async () => {
try {
const USDT = await usdt.query(sql`
SELECT * FROM USDT ORDER BY id DESC;
`);
let counter = 0;
let completion = 0;
async.mapLimit(USDT, 6, async (user) => {
let userDetail = {
email: user.email,
id: user.user_id,
address: user.address
}
try {
await this.getWalletTransaction(userDetail);
completion++;
} catch (TronGridException) {
completion++;
console.log(":: A TronGrid Exception Occured");
console.log(TronGridException);
}
if (USDT.length == completion || USDT.length == (completion-5)) {
setTimeout(() => {
this.getListofWallet();
}, 60000);
console.log('~~~~~~~ Finished Wallet Batch ~~~~~~~~~');
}
}
);
} catch (error) {
console.log(error);
console.log('~~~~~~~Restarting TronWallet File after Crash ~~~~~~~~~');
this.getListofWallet();
}
}
Then I use this to process to data sent and perform the neccessary action.
getWalletTransaction = async (walletDetail) => {
const config = {
headers: {
'TRON-PRO-API-KEY': process.env.api_key,
'Content-Type': 'application/json'
}
};
const getTransactionFromAddress = await axios.get(`https://api.trongrid.io/v1/accounts/${walletDetail.address}/transactions/trc20`, config);
const response = getTransactionFromAddress.data;
const currentTimeInMillisecond = 1642668127000; //1632409548000
response.data.forEach(async (value) => {
if (value.block_timestamp >= currentTimeInMillisecond && value.token_info.address == "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") {
let doesHashExist = await transactionCollection.query(sql`SELECT * FROM transaction_collection WHERE txhash=${value.transaction_id};`);
if (doesHashExist.length == 0) {
if (walletDetail.address == value.to) {
const checkExistence = await CryptoTransactions2.query(sql`
SELECT * FROM CryptoTransaction2 WHERE txHash=${value.transaction_id};
`);
if (checkExistence.length == 0) {
const xCollection = {
collection: "CryptoTransaction2",
queryObject: {
currency: "USDT",
sender: ObjectID("60358d21ec2b4b33e2fcd62e"),
receiver: ObjectID(walletDetail.id),
amount: parseFloat(tronWeb.fromSun(value.value)),
txHash: value.transaction_id,
description: "New wallet Deposit " + "60358d21ec2b4b33e2fcd62e" + " into " + value.to,
category: "deposit",
createdAt: new Date(),
updatedAt: new Date(),
},
};
await new MongoDbService().createTransaction(xCollection);
//create record inside cryptotransactions db.
await CryptoTransactions2.query(sql`INSERT INTO CryptoTransaction2 (txHash) VALUES (${value.transaction_id})`)
});
}

Transactions With Mongoose and NodeJs, calling an external function

I have some code that have implemented Transaction and it works perfectly, but now I need to call a function inside this code(it has a comment in my code example), both parts have to have success or then nothing have to be done. It is not working properly. How can I integrate the "sendCashback, function" to work perfectly with my Transaction?
This is my code:
var transaction: any
let cashback_sent: any
try{
const transactiontimectrl = Number(process.env.TRANSACTION_TIME_STOP)
let dt = new Date();
dt.setSeconds(dt.getSeconds() - transactiontimectrl);
const timectrl = dt.toISOString();
console.log('teste transaction1')
console.log(timectrl)
const session = await mongoose.startSession();
await session.withTransaction(async () => {
const transactionRes = await Transaction.findOne(
{
from: from,
//to: to,
//value: valuetodebit,
createdAt: {$gt: timectrl}
}).session(session)
if(!transactionRes){
transaction = await Transaction.create([
{
from,
to,
value: value,
division_factor: destinationUserGroup.division_factor,
title: title || null,
description: description || null,
type: type || null,
hasCashback: hasCashback,
realmoney: realmoney,
valuetodebit: valuetodebit
}],{ session })
}else{
throw new Error('Erro, uma transação semelhante foi realizada recentemente')
}
if (!transaction) {
throw new Error('Erro, tente novamente mais tarde')
}
console.log('#sadihjaisvq3')
let fromBalance
//Saldo real
if (realmoney) {
fromBalance = await Balance.findOne({
type: IBalanceType.GENERAL,
user: from
}).session(session)
}
//Saldo cashback
else {
fromBalance = await Balance.findOne({
type: IBalanceType.CASHBACK,
user: from
}).session(session)
}
console.log('****criar balance2')
let toBalance = await Balance.findOne({
type: IBalanceType.GENERAL,
user: to
}).session(session)
console.log('****criar balance1', toBalance)
if (!toBalance) {
console.log('****criar balance')
toBalance = await Balance.create(
{
user: to
})
}
let toLivetPay = await Balance.findOne({
type: IBalanceType.GENERAL,
user: toLivet
}).session(session)
if (!fromBalance || !toBalance || !toLivetPay) throw new Error()
if (!credit) {
fromBalance.value = fromBalance.value + parseFloat(valuetodebit) * -1
}
// fromBalance.value = fromBalance.value + parseFloat(valuetodebit) * -1
toBalance.value = toBalance.value + parseFloat(value)
let valorCash = Number(value) / 0.75
let valorLivet = Number(valorCash) * 0.25
toLivetPay.value = toLivetPay.value + parseFloat(String(valorLivet))
await fromBalance.save()
await toBalance.save()
await toLivetPay.save()
if (hasCashback) {
if (!saque) {
//EXTERNAL FUNCTION---------------------------------------------------------
cashback_sent = await sendCashback(
transaction[0],
body.originUser!,
body.destinationUser!
)
//------------------------------------------------------------------------
}
if (!cashback_sent) {
throw new Error('Erro ao distribuir cashback')
}
console.log('depois do cashback')
}
})
The transaction advanced usage is what you need. To work with async await, checkout this link.
As described in the linked mongoose documentation, you can call session.startTransaction() method to start a transaction, and whenever you wish to abort, call session.abortTransaction() to abort the transaction, after all the operations in the transaction are success, call session.commitTransaction() to commit the transaction.

multipage document pdf-lib.js with node & express

Im trying to create a multi-page document from templates on my file system, but I'm getting strange behaviour of the same page title across all pages in the document instead. Any ideas what I'm doing wrong here?
Something I don't quite get, is the way we add pages. Why do we need to reference newDoc in the example below, when we do await newDoc.copyPages(page, [0])? Instead of just newDoc.addPage(page)?
Would it be that the form field named Title is being overwritten because both pages have the same field name during the copying of data streams?
Note: I've been made aware that StackOverflow doesnt have a tag for pdf-lib.js.org, not to be confused with other pdf libraries.
const payload = {
rows: [{
id: 1,
title: 'Foo',
},{
id: 2,
title: 'Bar'
},
formData: {
hello: 'World',
lorum: 'Ipsum'
}
]
}
const makePdf = async (payload) => {
const newDoc = await PDFDocument.create()
newDoc.getForm().acroForm.dict.set(PDFName.of('NeedAppearances'), PDFBool.True)
for (const row of payload.rows) {
await addPage(row, payload.formData, newDoc)
}
return newDoc
}
const addPage = async (dataRow, formData, newDoc) => {
const rowId = dataRow.id
let templateName
switch(true) {
case (rowId === 1):
templateName = 'foo'
break
case (rowId === 2):
templateName = 'bar'
break
}
const templatePath = path.join(__dirname, `../templates/pdfs_/${templateName}.pdf`)
const template = await fs.readFileSync(templatePath)
const page = await PDFDocument.load(template)
const form = page.getForm()
form.acroForm.dict.set(PDFName.of('NeedAppearances'), PDFBool.True)
switch(templateName) {
case 'foo':
foo(form, formData)
break
case 'bar':
bar(form, formData)
}
// dataRow.title logs correct strings ie: 'Foo' & 'Bar'
form.getField('Title').setText(dataRow.title)
const [firstPage] = await newDoc.copyPages(page, [0])
return await newDoc.addPage(firstPage)
}
const bar = (form, formData) => {
form.getField('Lorum').setText(formData.lorum)
}
const foo = (form, payload) => {
form.getField('Hello').setText(formData.hello)
}
return makePdf(payload)
// Produces 2 page pdf with the same title
// [[ title: Foo, Hello: World ], [title: Foo, Lorum: Ipsum ]]
I don't have your template file so I'm not sure excatly what you are trying to achieve.
The copyPage is indeed required.
You should save the PDFDocument before you change it again.
I tried to rewrite your code, I ignored the PDFName and PDFBool line but I'm sure you can get the idea.
const {PDFDocument} = require('pdf-lib');
const fs = require('fs');
const payload = {
rows: [
{ id: 1, title: 'Foo', },
{ id: 2, title: 'Bar', },
],
formData: { hello: 'World', lorum: 'Ipsum', }
}
class PdfSample {
async loadTemplate(templatePath) {
const templateFile = fs.readFileSync(templatePath);
const templateDoc = this.templateDoc = await PDFDocument.load(templateFile);
this.templateForm = templateDoc.getForm();
}
async initDoc() {
this.resultDoc = await PDFDocument.create();
}
fillTextField(fieldName, value) {
const field = this.templateForm.getField(fieldName);
field.setText(value);
}
get formFieldsNames() {
return this.templateForm.getFields().map(field => field.getName());
}
async addDocPage() {
const newPageBytes = await this.templateDoc.save();
const newPage = await PDFDocument.load(newPageBytes);
const [pageToAdd] = await this.resultDoc.copyPages(newPage, [0]);
return this.resultDoc.addPage(pageToAdd);
}
async saveResult(outputPath) {
const pdfBytes = await this.resultDoc.save();
fs.writeFileSync(outputPath, pdfBytes);
}
}
const pdfSample = new PdfSample();
(async () => {
await pdfSample.initDoc();
await pdfSample.loadTemplate('./pdfs_/OoPdfFormExample.pdf');
console.log(pdfSample.formFieldsNames);
for (const row of payload.rows) {
pdfSample.fillTextField('Given Name Text Box', row.title);
pdfSample.fillTextField('Family Name Text Box', payload.formData.hello);
pdfSample.fillTextField('House nr Text Box', payload.formData.lorum);
await pdfSample.addDocPage(pdfSample.templateDoc);
}
await pdfSample.saveResult('./pdfs_/result.pdf');
})();
The sample form is from this site
You are trying to create a document with two form fields named Title, one on each page. This does not work, both are the same field and show the same value.
Rather then "copy" existing fields from a template, you must use something like
var newField = form.createTextField('Title' + dataRow.id);
newField.addToPage(firstPage, {x: ..., y: ...});
newField.setText(...);

How to edit/replace embeds in discordjs-commando

I am making a Type racing minigame with my discord bot, the code works... but I want to change the messages it sends to embeds, im new to Commando and it wont let me use the discord.js functions im used to using
I need to change all the bots responses to embeds, and make it so when it sends a new embed it just edits the old one so it isnt spamming messages. Here is my code:
const Commando = require('discord.js-commando')
const { words } = require('../../util/fast-type-words')
const example = {
channelId: {
message: 'message object',
stage: 'string',
counter: 'number',
currentWord: 'string',
remainingWords: ['words here'],
points: {
userId: 'points',
},
},
}
const games = {}
const stages = {
STARTING: (counter) => {
return `A new "fast type" game is starting in ${counter}s!`
},
IN_GAME: (word) => {
let spacedWord = ''
for (const character of [...word]) {
spacedWord += character
spacedWord += ' '
}
return `The new word is **${spacedWord}**!`
},
ENDING: (points) => {
const sorted = Object.keys(points).sort((a, b) => {
return points[b] - points[a]
})
let results = ''
for (const key of sorted) {
const amount = points[key]
results += `<#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
}
return `The game is now over Here's how everyone did:\n\n${results}------------------`
},
}
const selectWord = (game) => {
game.currentWord =
game.remainingWords[Math.floor(Math.random() * game.remainingWords.length)]
const index = game.remainingWords.indexOf(game.currentWord)
game.remainingWords.splice(index, 1)
}
const gameLoop = () => {
for (const key in games) {
const game = games[key]
const { message, stage } = game
if (stage === 'STARTING') {
let string = stages[stage](game.counter)
message.edit(string)
if (game.counter <= 0) {
game.stage = 'IN_GAME'
game.counter = 15
selectWord(game)
string = stages[game.stage](game.currentWord)
message.edit(string)
}
} else if (stage === 'IN_GAME') {
if (game.counter <= 0) {
game.stage = 'ENDING'
const string = stages[game.stage](game.points)
message.edit(string)
// Delete the game
delete games[key]
continue
}
}
--game.counter
}
setTimeout(gameLoop, 1000)
}
module.exports = class FastTypeGame extends Commando.Command {
constructor(client) {
super(client, {
name: 'fasttype',
group: 'games',
memberName: 'fasttype',
description: 'Starts a fast type game',
userPermissions: ['ADMINISTRATOR'],
})
client.on('message', (message) => {
const { channel, content, member } = message
const { id } = channel
const game = games[id]
if (game && game.currentWord && !member.user.bot) {
message.delete()
if (
game.stage === 'IN_GAME' &&
content.toLowerCase() === game.currentWord.toLowerCase()
) {
game.currentWord = null
const seconds = 2
const { points } = game
points[member.id] = points[member.id] || 0
message
.reply(`You won! +1 point (${++points[member.id]} total)`)
.then((newMessage) => {
newMessage.delete({
timeout: 1000 * seconds,
})
})
setTimeout(() => {
if (game.stage === 'IN_GAME') {
selectWord(game)
const string = stages[game.stage](game.currentWord)
game.message.edit(string)
}
}, 1000 * seconds)
}
}
})
gameLoop()
}
async run(message) {
const { channel } = message
message.delete()
channel.send('Preparing game...').then((message) => {
games[channel.id] = {
message,
stage: 'STARTING',
counter: 5,
remainingWords: [...words],
points: {
'719805930547445772': 4,
'723819104045105172': 1,
},
}
})
}
}
First change embeded content is not related to discord.js-commando to change the content of sended embeded message you need to get Message Object then using edit() method to pass the new embed content to it:
-Bonus: You can also edit text message into embed message.
Docs for edit method: https://discord.js.org/#/docs/main/stable/class/Message?scrollTo=edit
Example code:
let youCurrentMessage = await channel.send(embedContent);
yourCurrentMessage.edit(newEmbedContent);
yourCurrentMessage.edit(newEmbedContent2);
// If you edit message in other command , session.You need message id
let yourCurrentMessage = await msg.channel.messages.fetch(editMessageId);
yourCurrentMessage.edit(newEmbedContent);

Synchronising Lodash and mongoose query in nodejs

I am trying to accomplish synchronisation in Mongoose query inside a _.each lodash function like this -
let commentContainer = [];
let comments = {..} //json object having comments
_.each(comments, comment => {
User.findOne({_id: comment.createdBy}).exec()
.then(function(commentor){
var c = {
text: comment.text,
votes: comment.votes.length,
commentor: {
name: commentor.name,
profilePhoto: commentor.profilePhoto,
id: commentor._id
}
}
commentContainer.push(c);
});
});
}
console.log(commentContainer); //it shows []
How can I achieve it, I tried using setTimeout function by giving a delay but it does not seems a valid procedure.
modify your code like this way:
let fun = async() => {
let commentContainer = [];
let comments = {..} //json object having comments
await _.each(comments, comment => {
User.findOne({_id: comment.createdBy}).exec()
.then(function(commentor){
var c = {
text: comment.text,
votes: comment.votes.length,
commentor: {
name: commentor.name,
profilePhoto: commentor.profilePhoto,
id: commentor._id
}
}
commentContainer.push(c);
});
});
}
console.log(commentContainer); //it shows []
}
make your function async and use await keywoed when you need to wait for the process to complete before next iteration
It's because Node.js is asynchronous.You should use async/await or promise or callback when you are dealing with non-blocking call like DB-call or Http client call.
let comments = {..} //json object having comments
console.log(findUSer(comments)); // print result
async function findUSer(comments){
let commentContainer = [];
await _.each(comments, comment => {
User.findOne({_id: comment.createdBy}).exec()
.then(function(commentor){
var c = {
text: comment.text,
votes: comment.votes.length,
commentor: {
name: commentor.name,
profilePhoto: commentor.profilePhoto,
id: commentor._id
}
}
commentContainer.push(c);
});
});
}
return commentContainer;
}

Resources