Intercept bot messages before send in bot framework v4 Nodejs - node.js

I need to add fields before sending the object so I tried
turnContext.onSendActivities(async (ctx, activities, nextSend) => {
for (let activity of activities){
activity.channelData.data='hi'
console.log(activity)
}
return await nextSend();
})
but it doesn't reflect as what I understand the activities have been already sent and the function is for logging.
is there any way to make it work ?

Just include the onSendActivities() function within a handler. Then be sure to return await next() before proceeding to any following code, including the handler's await next(). The following setup will append the { data: 'hi' } object to every activity.
The only other step is to decide the right handler and the right logic to send the data you want when you want.
Hope of help!
this.onTurn(async (turnContext, next) => {
await turnContext.onSendActivities(async (ctx, activities, next) => {
for (let activity of activities) {
activity.channelData = { data: 'hi' };
}
return await next();
});
await next();
});

Related

Why does Async firebase fetching is not working? (NODE JS)

Building a NodeJS REST API.
Trying to send load data from FireBase collection, then sending it to the user (as API response).
Looks like the problem is that it's not waits for the firebase fetch to resolve, but send back a response without the collection data. (tried to use ASYNC-AWAIT but its not working)
exports.getChatMessages = async (req, res, next) => {
const chatId = req.params.chatId
const getChatData = () => {
db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot((snapshot) => {
snapshot.docs.forEach(msg => {
console.log(msg.data().messageContent)
return {
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
}
})
})
}
try {
const chatData = await getChatData()
console.log(chatData)
res.status(200).json({
message: 'Chat Has Found',
chatData: chatData
})
} catch (err) {
if (!err.statusCode) {
err.statusCode(500)
}
next(err)
}
}
As you can see, I've used 2 console.logs to realize what the problem, Terminal logs looks like:
[] (from console.logs(chatData))
All messages (from console.log(msg.data().messageContent))
Is there any way to block the code unti the firebase data realy fetched?
If I correctly understand, you want to send back an array of all the documents present in the messages subcollection. The following should do the trick.
exports.getChatMessages = async (req, res, next) => {
const chatId = req.params.chatId;
const collectionRef = db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc');
try {
const chatsQuerySnapshot = await collectionRef.get();
const chatData = [];
chatsQuerySnapshot.forEach((msg) => {
console.log(msg.data().messageContent);
chatData.push({
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
});
});
console.log(chatData);
res.status(200).json({
message: 'Chat Has Found',
chatData: chatData,
});
} catch (err) {
if (!err.statusCode) {
err.statusCode(500);
}
next(err);
}
};
The asynchronous get() method returns a QuerySnapshot on which you can call forEach() for enumerating all of the documents in the QuerySnapshot.
You can only await a Promise. Currently, getChatData() does not return a Promise, so awaiting it is pointless. You are trying to await a fixed value, so it resolves immediately and jumps to the next line. console.log(chatData) happens. Then, later, your (snapshot) => callback happens, but too late.
const getChatData = () => new Promise(resolve => { // Return a Promise, so it can be awaited
db.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot(resolve) // Equivalent to .onSnapshot((snapshot) => resolve(snapshot))
})
const snapshot = await getChatData();
console.log(snapshot)
// Put your transform logic out of the function that calls the DB. A function should only do one thing if possible : call or transform, not both.
const chatData = snapshot.map(msg => ({
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
}));
res.status(200).json({
message: 'Chat Has Found',
chatData
})
Right now, getChatData is this (short version):
const getChatData = () => {
db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot((snapshot) => {}) // some things inside
}
What that means is that the getChatData function calls some db query, and then returns void (nothing). I bet you'd want to return the db call (hopefully it's a Promise), so that your await does some work for you. Something along the lines of:
const getChatData = async () =>
db
.collection('chats')
// ...
Which is the same as const getChatData = async() => { return db... }
Update: Now that I've reviewed the docs once again, I see that you use onSnapshot, which is meant for updates and can fire multiple times. The first call actually makes a request, but then continues to listen on those updates. Since that seems like a regular request-response, and you want it to happen only once - use .get() docs instead of .onSnapshot(). Otherwise those listeners would stay there and cause troubles. .get() returns a Promise, so the sample fix that I've mentioned above would work perfectly and you don't need to change other pieces of the code.

Firebase Cloud Function no console.log when admin.firestore() is called

I have a webhook to recieve facebook messenger events in a cloud function like so:
export const facebookMessengerHook = functions.https.onRequest(async (req: express.Request, res: express.Response) => {
console.log(req);
console.log(req.method);
console.log(req.body);
if (req.method == "POST") {
const body = req.body;
console.log(body);
// Checks this is an event from a page subscription
if (body.object === 'page') {
res.status(200).send('EVENT_RECEIVED');
// Iterates over each entry - there may be multiple if batched
for (const entry of body.entry) {
// will only ever contain one message, so we get index 0
const webhook_data = entry.messaging[0];
console.log(webhook_data);
try {
// v THAT PART HERE v
const user = await admin.firestore().collection('users')
.where('facebookMessengerId', '==', webhook_data.sender.id)
.get();
// ^ THAT PART HERE ^
console.log(user);
} catch (e) {
console.log('No user');
}
}
}
else {
// Returns a '404 Not Found' if event is not from a page subscription
res.sendStatus(404);
}
}
});
It does not log anything, unless I comment out the marked part in the snippet.
Can someone please explain to me why and how to fix this, because I need to make a call to firestore and I also need the console.log for debug purposes?
Thanks for any help!
The problem most probably comes from the fact that by doing
res.status(200).send('EVENT_RECEIVED');
you actually indicate to the Cloud Function platform that the Cloud Function can be terminated before the rest of the asynchronous work (the set of calls to the get() method) is done. See the following official video form more detail. In other words, the Cloud Function is terminated before the promises returned by the get() method are resolved.
So you should modify your code as follows:
//....
if (body.object === 'page') {
// Iterates over each entry - there may be multiple if batched
for (const entry of body.entry) {
// will only ever contain one message, so we get index 0
const webhook_data = entry.messaging[0];
console.log(webhook_data);
try {
const user = await admin.firestore().collection('users')
.where('facebookMessengerId', '==', webhook_data.sender.id)
.get();
console.log(user);
} catch (e) {
console.log('No user');
//Here throw an error to be catched at an upper level
}
}
res.status(200).send('EVENT_RECEIVED');
}
//....
Note that you may use Promise.all() since you issue a series of fetch to the database. But with your code it is impossible to confirm that, because it does not show the exact use of these fetches.

botframework v4 proactive messages not going through activityhandler event handlers

I Have a notification dialog to be invoked proactively...
e.g.
Bot: Hi, you have an event scheduled in next 15 mts... blah blah
Bot: would you want me to email the details?
User input: yes/no
Bot: Great!
This is a simple waterfall dialog...
step1 inform and prompt confirm.
step2. process user input..
Now this dialog is initiated proactively .
step 1 works.
However the dialogContext / dialogStack is not getting saved and when user says yes it is going to main dailog and not this proactive dialog which should be on top of stack.
Basically, none of the activityHandler methods like onDialog event on the activityHandler are getting invoked for proactive dialog.
Question is how to have the messages from proactive Dialog go through activityHandler methods so that the dialogStack is persisted ?
I use nodejs.
Updating with Code sample below
// middleware
const { ActivityTypes } = require('botbuilder');
class MyMiddleware {
async onTurn(context, next) {
await context.onSendActivities(async (context, activities, nextSend) => {
console.log(`messages: ${activities.map( a => a.text).join(',')}`)
return await nextSend();
});
// By calling next() you ensure that the next Middleware is run.
return await next();
};
}
module.exports.MyMiddleware = MyMiddleware;
// main bot.
const { ActivityHandler, TurnContext } = require('botbuilder');
class ProactiveBot extends ActivityHandler {
constructor(conversationReferences) {
super();
this.conversationReferences = conversationReferences;
this.onConversationUpdate(async (context, next) => {
this.addConversationReference(context.activity);
await next();
});
this.onMembersAdded(async (context, next) => {
const membersAdded = context.activity.membersAdded;
for (let cnt = 0; cnt < membersAdded.length; cnt++) {
if (membersAdded[cnt].id !== context.activity.recipient.id) {
const welcomeMessage = 'Welcome to the Proactive Bot sample. Navigate to http://localhost:3978/api/notify to proactively message everyone who has previously messaged this bot.';
await context.sendActivity(welcomeMessage);
}
}
await next();
});
this.onMessage(async (context, next) => {
this.addConversationReference(context.activity);
await context.sendActivity(`You sent '${ context.activity.text }'`);
await next();
});
this.onDialog(async (context, next) =>{
console.log(`I am called`)
})
}
addConversationReference(activity) {
const conversationReference = TurnContext.getConversationReference(activity);
this.conversationReferences[conversationReference.conversation.id] = conversationReference;
}
}
module.exports.ProactiveBot = ProactiveBot;
// index
const bot = new ProactiveBot(conversationReferences);
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// Route to main dialog.
await bot.run(context);
});
});
server.get('/api/notify', async (req, res) => {
for (const conversationReference of Object.values(conversationReferences)) {
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity('proactive hello');
});
}
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.write('<html><body><h1>Proactive messages have been sent.</h1></body></html>');
res.end();
});
When I call notify api I expect the onDialog event to be called... and print "I am called". But that doest not get printed to console.
It looks like you do not have the following bit of code in your "mainBot.js" file. Adding it should solve your problem and not require you to save state after every step. I have a proactive dialog that presents the user with a "yes/no" option, as well. However, I do not have to save with every step in order to capture the user's response.
this.onDialog(async (context, next) => {
console.log('Dialog detected');
// Save any state changes.
await this.conversationState.saveChanges(context, false);
await this.userState.saveChanges(context, false);
await next();
});
That being said, I have a component dialog that is listening for the user's choice sending an appropriate response. The proactive dialog is not being captured but the response context is. Because the "mainDailog.js" extends the component dialog the context is added to the stack which is then processed via the this.onDialog() method in "mainBot.js". Thus, state is saved. Check out this SO response I posted recently that demo's this setup which includes the above code but is not displayed. In that case, the user also wanted a timer built into the process which you can ignore.
Hope of help!

How to returned poll data after each nodejs api call to reactjs component

I need to poll the data until the response.Status === 'UpdatesComplete'.
I have written this node js API function which basically polls the data -
const getResults = async (location) => {
try {
const response = await poll(location);
if (response.Status && response.Status === 'UpdatesComplete') {
return response;
}
return await getResults(location);
} catch (err) {
throw err;
}
};
app.get('/url', async (req, res) => {
try {
const results = await getResults(req.query);
res.json(formatData(results));
} catch (err) {
res.status(500).send(err);
console.error(err);
}
});
I am calling this API from ReactJS class component inside ComponentDidMount lifecycle method -
componentDidMount = () => {
axios.get('url', {
params: params
})
.then(response => {
console.log(response.data, 'data');
// setting the data on state
this.setState({ filteredList: response.data });
})
.catch(err => {
this.setState({ error: true });
});
};
This is working fine. But since the API is returning data till all the data has been fetched(after doing polling), it's taking a very long time on view to load the data. I am basically looking for returning the polling data to view as soon as the data fetched and simultaneously polling the API until all the data has been fetched. In this case, data will keep on updating after each polling on the view which will improve the user experience.
Thanks in advance.
You are finding the lazy loading or infinite scroll solution on the server-side. There is no simple way to do this.
The basic idea of the solution is to paginate your result with polling. ie.
call url?size=2&offset=0 from the client-side. Then on the server-side just poll first 2 results and return. next time call url?size=2&offset=2 and so-on.

How to: GET Data from 2 APIs, compare, POST bool

I'm working on a project that requires me to:
GET IDs from API1, push the IDs into an array, then map over those IDs, using them for a second GET request, where IDs are used as params for API2 GET request, populates an array with IDs or N for "Not existing" -- this array is then called in:
A POST request. This post maps over the returned array from the GET request. IF the item is not "N", it POSTS to API1 with checked: true. IF the item is "N", it emails us telling us API2 is missing this project.
I want this system to automatically do a GET and POST every 2 hours, so I'm using setInterval (not sure this is the best idea). EDIT: Cron job would be a better solution.
I'm working with NodeJS, Express, Request-Promise, Async / Await.
Here is some of my pseudo code so far:
// Dependencies
const express = require('express');
const axios = require('axios');
const mailgun = require('mailgun-js')({ apiKey, domain });
// Static
const app = express();
app.get('/', (req, res, next) => {
// Replace setInterval with Cron job in deployment
// Get All Ids
const orders = await getGCloud();
// Check if IDs exist in other API
const validations = await getProjectManagementSystem(orders);
// If they exist, POST update to check, else, mailer
validations.map(id => {
if (id !== 'n') {
postGCloud(id);
} else {
mailer(id);
}
});
}
// Method gets all IDs
const getGCloud = async () => {
try {
let orders = [];
const response = await axios.get('gCloudURL');
for (let key in response) {
orders.push(response.key);
}
return orders;
} catch (error) {
console.log('Error: ', error);
}
}
// Method does a GET requst for each ID
const getProjectManagementSystem = async orders => {
try {
let idArr = [];
orders.map(id => {
let response = await axios.get(`projectManagementSystemURL/${id}`);
response === '404' ? idArr.push('n') : idArr.push(response)
})
return idArr;
} catch (error) {
console.log('Error: ', error);
}
}
const postGCloud = id => {
axios.post('/gcloudURL', {
id,
checked: true
})
.then(res => console.log(res))
.catch(err => console.log(err))
}
const mailer = id => {
const data = {
from: 'TESTER <test#test.com>',
to: 'customerSuppoer#test.com',
subject: `Missing Order: ${id}`,
text: `Our Project Management System is missing ${id}. Please contact client.`
}
mailgun.messages().send(data, (err, body) => {
if (err) {
console.log('Error: ', err)
} else {
console.log('Body: ', body);
}
});
}
app.listen(6000, () => console.log('LISTENING ON 6000'));
The TL;DR: Need to do a GET request to API 1, then another GET request to API 2 following it (using IDs from API 1 as params), then send data from second GET to a POST request that then either updates API 1's data or emails Customer support. This is an automatic system that runs every two hours.
Main Questions:
1. Is it okay to have a setInterval in a get req?
2. Can I have a GET request automatically call a POST request?
3. If so, how can I pass GET request data onto a POST request?
To make it work for both of your calls one post and one get you have to do an Ajax call to get post processed information in another method.
I hope this works.

Resources