Asynchronous Bot Framework Response possible? - node.js

Most node.js botframework examples involve the notion of the waterfall dialog model, which is a great way to structure conversations, but we don't need that since we have our own chat system. We simply receive messages via the webhook, process them and respond without the dialog system.
Also, and now we are getting to the heart of the matter;), the examples I have seen communicate back to the botframework in the web context:
var connector = new builder.ChatConnector({config});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());
bot.dialog('/', (session, args) => {
session.sendTyping();
session.send( 'Echo'+ session.message.text);
})
The above example simply responds with an 'echo' and before that set's the typing status.
Our system works asynchronous, the way we currently work is by bypassing the connector listen and dialog scheme. A simplified example of how we queue a botframework message.
server.post('/api/messages/:name', (req, res, next)=>{
queue.post('botframework',req.params.name,req.body)
.then(()=>res.sendStatus(200))
});
In the queue processing, we construct the botframework objects:
//the ':name' from the snippet above is used to identify
//the bot a retrieve credentials
const connector = new botbuilder.ChatConnector({
appId: bot.properties.botframework_id.value.appId,
appPassword: bot.properties.botframework_id.value.appPassword
});
const username=message.from.name.replace(/[\s-;:"';$]/g,"_")+"_skype";
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let msg = new botbuilder.Message();
//processing...
//create the outgoing message...
bot.send(msg);
The problem here for us is that we simply don't know how to create a session object from a raw webhook message which is needed for the typing indicator and to ensure the order of messages when many messages are sent in quick succession.
Here is what we wish to accomplish:
//the ':name' from the snippet above is used to identify
//the bot a retrieve credentials
//the context is non HHTP
const connector = new botbuilder.ChatConnector({
appId: bot.properties.botframework_id.value.appId,
appPassword: bot.properties.botframework_id.value.appPassword
});
const username=message.from.name.replace(/[\s-;:"';$]/g,"_")+"_skype";
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let session = bot.createSession();
session.sendTyping();
let message = getNextMessageFromChatServer(message);
session.send(message);
//more messages to be send?
//....
So the question: How can we create a session object from the raw data send to the botframework webhook?

You should be able to build a session using loadSession
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let msg = new botbuilder.Message();
bot.loadSession(address, (err, session) => {
session.send("Message");
})

Related

Notify Users when Child is added on specific node using Cloud function

Is it possible for cloud function to listen for specific node for when a child is added and then send a notification to users located on a different node, and if that is possible how so? I am using node.js with Firebase realtime database and not Firestore.
This is my database:
I want the cloud function to listen every time a child is added on "Emergencies", and then notify all the users in the "Registered Admins"
This is the contents of the users in "Registered Admins" node, it has a child "Notification" containing the message, and I want to send that message to all the users, when a child is added on "Emergencies" node.
This is my cloud function using node.js. I've deployed it however it does not work, does not send any notification at all.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.listen = functions.database.ref("/Emergencies")
.onWrite(async (change, context) => {
change.after.val();
context.params.pushId;
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
const results = await Promise.all([getDeviceTokensPromise]);
tokensSnapshot = results[0];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return functions.logger.log(
'There are no notification tokens to send to.'
);
}
functions.logger.log(
'There are',
tokensSnapshot.numChildren(),
'tokens to send notifications to.'
);
// Notification details.
const payload = {
notification: {
title: "New Emergency Request!",
body: "Someone needs help check Emergenie App now!",
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
'Failure sending notification to',
tokens[index],
error
);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
Yes, that sounds possible and is in fact quite close to what the example on notifying users when something interesting happens does.
To send a message to a specific device, you 'll need to know the token for that device. If you want to broadcast a message to multiple users, you could subscribe those users to a topic. Just keep in mind that anyone can subscribe to a topic if they know its name, so you can't use that to send messages that only a certain group of users is allowed to see.

Sending Proactive Messages from Azure functions to botservice - node

I am using botframework v4, but coming over from v3, I have not found any documentation that is similar to the code I use below but for v4, regarding sending proactive messages from Azure Function App
Below is the code I previously used but am having trouble adapting:
var builder = require('botbuilder');
// setup bot credentials
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
module.exports = function (context, req) {
if (req.body) {
var savedAddress = req.body.channelAddress;
var inMemoryStorage = new builder.MemoryBotStorage();
var bot = new builder.UniversalBot(connector).set('storage', inMemoryStorage);
sendProactiveMessage(savedAddress, bot)
}
};
function sendProactiveMessage(address, bot) {
var msg = new builder.Message().address(address);
msg.textLocale('en-US');
var img = {
attachments: [{
contentType: "image/jpg",
contentUrl: latestUrl,
}]
};
msg.addAttachment(img.attachments[0]);
msg.text('hello');
bot.send(msg);
}
This works fine with v3 but not v4.
If possible I would also like to find a way to log a user out:
await botAdapter.signOutUser(innerDc.context, this.connectionName);
This is how I do it in the bot itself, but doing so from Azure Functions again is proving difficult.
Any help would be appreciated.
Great that you are making the move from v3 to v4! Have you had a look at Send proactive notifications to users? This example is pretty straight forward and can be used within an Azure function.
First you retrieve the Conversation Reference in your bot by calling TurnContext.getConversationReference(context.activity);. This is the reference you could use in your proactive function to open the conversation. In your case you provide that via the request body to a proactive function, so I will do the same in my example.
My proactive endpoint example is written in Typescript, however it works the same way in plain Javascript. Create a HTTP trigger in Azure Functions and use the following code. I have added comments inline for clarity.
const { BotFrameworkAdapter } = require('botbuilder');
// Create adapter.
// If you need to share this adapter in multiple functions, you could
// instantiate it somewhere else and import it in every function.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
module.exports = async function (context, req) {
// Validate if request has a body
if (!req.body) {
context.res = {
status: 400,
body: "Please pass a conversation reference in the request body"
};
return;
}
// Retrieve conversation reference from POST body
const conversationReference = req.body;
// Open the conversation and retrieve a TurnContext
await adapter.continueConversation(conversationReference, async turnContext => {
// Send a text activity
// Depending on the channel, you might need to use https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity('Proactive hello');
});
context.res = {
body: 'Message sent!'
};
};
In the end you could make a request to this Azure Function, where you pass the Conversation Reference as body of the type application/json.
Extending this example with features like signOutUser is simple. You can call all functions within the continueConversation function, just as in a normal bot. You can even receive the adapter object there if you wish.
await adapter.continueConversation(conversationReference, async turnContext => {
// Sign user out
turnContext.adapter.signOutUser(turnContext, 'connection-name');
});

TEAMS bot in node.js: 'Authorization has been denied for this request' in CreateConversation method

I have a TEAMS node.js bot running locally (with ngrok). I receive messages from TEAMS client and echo works
context.sendActivity(`You said '${context.activity.text}'`);
Now I want to send a 1to1 message to this user, but I receive
Error: Authorization has been denied for this request
when creating a conversation.
My code:
var sUserId = "29:1shb_5I6CkkerBVq4qPqcv5dGwDfkXx11Jbjc1UnGCIv"
var sServiceUrl = "https://smba.trafficmanager.net/emea/";
var sTenantId = "942369d2-208e-438b-894c-0d0e1510cf61";
var credentials = new BotConnector.MicrosoftAppCredentials({
appId: "xxxxxxx",
appPassword: "yyyyyyyy"
});
var connectorClient = new BotConnector.ConnectorClient(credentials, { baseUri: sServiceUrl });
const parameters = {
members: [ { id: sUserId } ],
isGroup: false,
channelData:
{
tenant: {
id: sTenantId
}
}
};
var conversationResource = await connectorClient.conversations.createConversation(parameters);
// I get the error here, next is not executed
await connectorClient.conversations.sendToConversation(conversationResource.id, {
type: "message",
from: { id: "xxxxxxx" },
recipient: { id: sUserId },
text: 'This a message from Bot Connector Client (NodeJS)'
});
appId & appPassword are valid (from .env file), if they are wrong I can not receive messages from TEAMS client
I have the same code to create a conversation in a .NET bot and it works for me:
var parameters = new ConversationParameters
{
Members = new[] { new ChannelAccount(sUserId) },
ChannelData = new TeamsChannelData
{
Tenant = new TenantInfo(sTenantId),
},
};
retValue = await connectorClient.Conversations.CreateConversationAsync(parameters);
What is wrong in my node.js code?
Thanks,
Diego
Have you trusted the service? It don't think so based on your code, and it's a classic cause of 401 in your case.
In node.js, do the following:
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
If you want more details around that, have a look to the documentation about getting 401 when sending proactive messages here
And also this SO answer about Teams and Proactive messaging, in particular last block.
Proactive messaging bot in Teams without mentioning the bot beforehand

Initiate twilio flex chat using node.js

I am having difficulty getting twilio flex to show messages created using the API using node.js.
I am creating a channel, adding a member, creating a message on the channel using the member.
The flex dashboard shows the incoming chat request in the task list, I can answer the chat request but none of the messages I save to the channel are show.
The interesting thing is if I use the twilio-flex-webchat.min.js script and initiate a chat from a web page and then get the ChannelSid for that conversation (using https://chat.twilio.com/v2/Services/ISXXXX/Channels) I can use the APIs to create messages for this channel and they appear on the flex dashboard. But I need all this to work via node.js.
I compared the task, channel, reservation, member and message twilio objects both for chat conversations using the twilio-flex-webchat.min.js web library and the objects created by the node.js code. I cannot find any notable difference.
Does anybody have any insights?
Here is my code.
const accountSid = 'ACXXXXXXXXXX';
const authToken = 'XXXXXXXXXXXXXXXX';
const workspaceSid = 'WSXXXXXXXXXXXXXXXx';
const workFlowSid = 'WWXXXXXXXXXXXXXXXXXXXXXX';
const serviceSid = 'ISXXXXXXXXXXXXXXXXXXXXXX';
const client = require('twilio')(accountSid, authToken);
(async () => {
//create channel
let channel = await client.chat.services(serviceSid)
.channels
.create({
attributes: JSON.stringify({
status: "ACTIVE",
from: "Some Person",
channel_type: "web"
}),
workflowSid: workFlowSid,
taskChannel: 'chat',
friendlyName: 'Flex WebChat',
type: 'private'
});
//create a member in this channel
let member = await client.chat.services(serviceSid)
.channels(channel.sid)
.members
.create({ identity: 'WEB_CLIENT' });
//post a message to this channel from the member
let message = await client.chat.services(serviceSid)
.channels(channel.sid)
.messages.create({ body: 'This is a test message', to: channel.sid, from: 'WEB_CLIENT' });
//create a task for my programable chat channel and associate the channel sid for my current conversation
let task = await client.taskrouter.workspaces(workspaceSid)
.tasks
.create({
attributes: JSON.stringify({
channelSid: channel.sid,
channelType: "web"
}),
workflowSid: workFlowSid,
taskChannel: 'chat',
});
})();
Thanks
Before adding a message and creating a Task, you need to create a proxy session, add the members as participants and then update the channel attributes:
{
"status": "ACTIVE",
"forwarding": true,
"twilioNumber": firstParticipant.proxyIdentifier,
"serviceNumber": someIdentity,
"from": from,
"channel_type": "sms",
"proxySession": session.sid
}

node-apn : Provider should be created per notification request or one-time

I am new to node-apn. I have implemented it in nodejs application. Below is my code.
var APN = require('apn')
var apnProvider = new APN.Provider({
token: {
key: "PATH_TO_FILE",
keyId: "KEY",
teamId: "TEAM"
},
production: false
});
module.exports = {
send: function (tokens, message, callBackFn) {
var note = new APN.Notification({
alert: "Breaking News: I just sent my first Push Notification",
});
// The topic is usually the bundle identifier of your application.
note.topic = "BUNDLE";
console.log(`Sending: ${note.compile()} to ${tokens}`);
service.send(note, tokens).then(callBackFn);
}
};
So in some documentation it says we should shutdown apnProvider.
So my question is should i create apnProvider globally (like i have done)?
OR should i create per send request (inside send function) & call shutdown after send notification.
I tried reading online. But i couldn't find any example like my requirements.

Resources