Is it possible to open widgets.getsitecontrol.com/ javascript from azure bot v4? - node.js

I want to open widgets.getsitecontrol.com/ javascript page that I have implemented on my website. Whenever I type 'Help' inside my bot, the widget should open. Is it possible to open it? Thanks. I am using node js version. If it is possible, please provide me an approach to solve this issue.

I'm not sure exactly how your widget functions, but when the user sends a 'help' message to the bot, you can send a back channel event to WebChat to trigger opening the widget. Take a look at the code snippets below.
Bot Code - NodeJs
When the bot receives a 'help' message from the user, the bot can send an event by sending an activity with the type set to 'event'. We can also give the outgoing activity a name attribute so we can send mutltiple types of events to WebChat. In this case, we are going to name the out going activity 'helpEvent'.
async onTurn(turnContext) {
if(turnContext.activity.type === ActivityTypes.Message) {
if (turnContext.activity.text.toLowerCase() === 'help') {
// Send Back Channel Help Event
await turnContext.sendActivity({ type: 'event', name: 'helpEvent'});
}
...
}
}
WebChat Custom Middleware
In WebChat, we are going to create a custom middleware to check incoming activities. When we encounter an activity that has a name and type that we recognize, trigger your event on the webpage. In the example below, I just alerted the use that they asked for help, but here is where you launch your widget.
const store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { name, type } = action.payload.activity;
if (type === 'event' && name === 'helpEvent') {
// Activate Widget
alert("You asked for help.");
}
}
return next(action);
}
);
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
}, document.getElementById('webchat'));
For more details on back channel events and creating a custom middleware in WebChat, checkout this sample in the WebChat Repo.
Hope this helps!

Related

How can I send a message from bot framework sdk after a period of inactivity? In nodejs

I am using the nodejs SDK for Bot Framework to develop a chatbot. I want to send a message to the user if they do not write in 5 minutes.
I do not find an example in bot-framework documentation and, in stackoverflow there are not solutions for a started bot (I do not need it to start the conversation). Where do I need to create the code? I have an index.js and a dialog file. How can I set the timer and restart it when the user send a message?
I'm using directline.
Thanks
There are two different ways you can approach this, one for directline only using events and one for all channels using setTimeout. The directline solution requires some code on your webchat client, but the latter requires you to save the conversation reference and start a new bot adapter. Both approaches could work.
Directline Only
You need to set up your webchat client to set up the timer and send an event to your bot if no activities are sent before the timer expires. You need to create a custom store to do this. Here is an example I used in the past:
const store = window.WebChat.createStore({}, function(dispatch) { return function(next) { return function(action) {
if (action.type === 'WEB_CHAT/SEND_MESSAGE') {
// Message sent by the user
clearTimeout(interval);
} else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' && action.payload.activity.name !== "inactive") {
// Message sent by the bot
clearInterval(interval);
interval = setTimeout(function() {
// Notify bot the user has been inactive
dispatch.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'inactive',
value: ''
}
});
}, 300000)
}
return next(action);
}}});
This will send an event to your bot with the name 'inactive'. Now you need to set up your bot to handle it. So in your this.onEvent handler you need to do something like this:
if (context.activity.name && context.activity.name === 'inactive') {
await context.sendActivity({
text: 'Are you still there? Is there anything else I can help you with?',
name: 'inactive'
});
}
All channels
As I'm typing this up, I'm realizing you should be able to emit the event from your bot itself and forego starting a new bot adapter instance. But I haven't tried that before, so I'm providing my existing solution. But you may wish to experiment with emitting an inactive event if the timeout is reached instead of the actions below.
That said, here is a solution you can use within your this.onMessage handler.
// Inactivity messages
// Reset the inactivity timer
clearTimeout(this.inactivityTimer);
this.inactivityTimer = setTimeout(async function(conversationReference) {
console.log('User is inactive');
try {
const adapter = new BotFrameworkAdapter({
appId: process.env.microsoftAppID,
appPassword: process.env.microsoftAppPassword
});
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity('Are you still there?');
});
} catch (error) {
//console.log('Bad Request. Please ensure your message contains the conversation reference and message text.');
console.log(error);
}
}, 300000, conversationData.conversationReference);
Note that you have to get and save the conversationReference if you go this route, so that you can call continueConversation if the timer expires. I typically do this in my this.onMessage handler as well just to make sure I always have a valid conversation reference. You can get it with the below code (I'm assuming you already have your conversation state and state accessor defined).
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
Now as I mentioned in the first solution, I believe you should be able to send an inactivity event in your try block instead of initiating the bot adapter. If you try that and it works, please let me know so I can update this solution!

How to send welcome message AND load a specific dialog automatically in Microsoft Bot Framework v.3 (Node.js)?

I'm trying both to show a welcome message when my bot starts up and also load a specific dialog. We are using version 3 in the company where I'm working (I know, it's old and not supported).
As far as the welcome message, https://learn.microsoft.com/en-us/azure/bot-service/nodejs/bot-builder-nodejs-handle-conversation-events?view=azure-bot-service-3.0 says to use on conversationUpdate, which works fine, but this seems to be contradicted by https://blog.botframework.com/2018/07/12/how-to-properly-send-a-greeting-message-and-common-issues-from-customers/, which suggests one should not use conversationUpdate, except when using DirectLine, but instead send an event. Is this the final word on the matter? Is there a better way?
I'd also like to load a dialog automatically after the welcome message. How do I do this? Can I access the session during the 'on conversationUpdate' event above and load the dialog directly there? Is there a better way?
Thanks for any help!
It is contradictory, but conversationUpdate is likely your best bet in most situations. However, because channels handle this differently, you should be aware that the result can vary. For direct line, it is a better option to utilize sending events.
An example, in case of need:
bot.on('conversationUpdate', function(message) {
if (message.membersAdded) {
message.membersAdded.forEach(function(identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Welcome");
bot.send(reply);
}
});
}
});
For immediately calling a specific dialog, do this:
bot.on('conversationUpdate', function (message) {
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
bot.beginDialog(message.address, '/main');
}
});
}
});
bot.dialog('/main', [
function (session, args, next) {
session.send("Glad you could join.");
session.beginDialog('/next');
}
]);
Simply combine the two for sending the welcome message and starting up a dialog.
Hope of help!

Using postBack to send bot text message using BotFramework v4 and Node.js

I am trying to send a postBack text message to my bot but I don't know the right syntax.
Here is my code:
if (postback.payload == "WHAT_IS_MENTAL_HEALTH") {
await turnContext.sendActivity("TO-DO: Forward on 'What Is Mental Health?' to Bot Handler");
ActionTypes.postBack("What Is Mental Health?");
}
I'm trying to forward on the text "What Is Mental Health?" to my bot so it will pull back the QnA Maker response for that question.
The steps for this are as follows:
User clicks a button on a Facebook Messenger Generic Template Card (e.g. “What Is Mental Health?” Button)
The button sends a postBack payload to the bot (e.g. “WHAT_IS_MENTAL_HEALTH”)
I am detecting the postBack payload from Facebook Messenger (e.g if (postBack.payload == “WHAT_IS_MENTAL_HEALTH”))
Once that particular postBack payload is detected I then want to send an additional postBack to my bot as text (e.g. “What Is Mental Health?”) so it can be interpreted by my QnA and respond with the correct answer which has been programmed into my QnA Knowledge Base.
Facebook Events and the Bot Framework
When Facebook sends an event to your bot, it sends an Activity with an Event ActivityType. For some events, the event data is in the Activity.Value property. For other events, like a PostBack from a Quick Reply, the activity will be a Message and the data will be in Activity.ChannelData. For example, your bot might receive a postBack event as an activity like this:
{
channelId: 'facebook',
[...]
type: 'message',
channelData: {
recipient: {...},
sender: {...},
message: {
[...],
quick_reply: {
[...],
payload: '<your payload>'
}
}
}
}
Handling Facebook Events
This answer is going to pull heavily from the Facebook Events Sample. I highly recommend looking at that for additional help.
Capture Messages and Events
First, you want to capture the facebook messages and events with onMessage() and onEvent():
this.onMessage(async (turnContext) => {
console.log('Processing a Message Activity.');
// Show choices if the Facebook Payload from ChannelData is not handled
if (!await this.processFacebookPayload(turnContext, turnContext.activity.channelData)) {
if (turnContext.activity.channelId !== 'facebook') {
await turnContext.sendActivity('This sample is intended to be used with a Facebook bot.');
}
await this.showChoices(turnContext);
}
});
this.onEvent(async (turnContext) => {
console.log('Processing an Event Activity.');
// Analyze Facebook payload from EventActivity.Value
await this.processFacebookPayload(turnContext, turnContext.activity.value);
});
Process the Messages/Events
Facebook can send many types of events. You may want to use an if or switch statement to handle each type:
async processFacebookPayload(turnContext, data) {
// At this point we know we are on Facebook channel, and can consume the Facebook custom payload present in channelData.
const facebookPayload = data;
if (facebookPayload) {
if (facebookPayload.postback) {
// Postback
await this.onFacebookPostback(turnContext, facebookPayload.postback);
return true;
} else if (facebookPayload.optin) {
// Optin
await this.onFacebookOptin(turnContext, facebookPayload.optin);
return true;
[...]
}
return false;
}
Specifically, Handle a PostBack
The sample just does:
async onFacebookPostback(turnContext, postback) {
console.log('Postback message received.');
// TODO: Your postBack handling logic here...
// Answer the postback and show choices
await turnContext.sendActivity('Are you sure?');
await this.showChoices(turnContext);
}
As you want to route the question to QnA Maker, you might (using the QnA Maker Sample as guidance):
async onFacebookPostback(turnContext, postback) {
// qnaMaker.getAnswers doesn't accept string input, so we need to adjust our turnContext
// to match what it expects, which is a string in Activity.Text
turnContext.activity.text = postback.payload;
const qnaResults = await this.qnaMaker.getAnswers(turnContext);
// If an answer was received from QnA Maker, send the answer back to the user.
if (qnaResults[0]) {
await turnContext.sendActivity(qnaResults[0].answer);
// If no answers were returned from QnA Maker, reply with help.
} else {
await turnContext.sendActivity('No QnA Maker answers were found.');
}
}

When bot is restarted or turned on, do a specific function

I'm trying to make it so that when my bot gets a restart, in the ready.js file it will search for webhooks it made in all of the guilds the bot is in, the bot will then use that webhook and send messages through it. I wasn't able to get anywhere and this is in JS.
I've referred to the documentation for Discord.JS and really haven't gotten anywhere. I tried to get the client ID from webhook.owner and see if the bot's ID matches up with it. I am not sure how to extract the client ID from webhook.owner
guild.fetchWebhooks()
if(webhook.owner == `${bot.user.id}`);
(suggested)
guild.fetchWebhooks()
if(webhook.owner == `${bot.user.username}`);
(actual)
This is the only code I could come up with, can add full file if needed.
I expect a bot that when it restarts (bot.on) then it will search through all of the guilds it is in and find webhooks that it owns, and send messages through it without doing it to another active webhook.
What ends up happening is that my bot just sends out a mention of the bot.
Do something like this:
bot.on('ready', async () => { // on ready
await bot.guilds.forEach(async guild => { // in all guilds:
const webhooks = await guild.fetchWebhooks(); // check for Webhooks
await webhooks.forEach(async webhook => { // for all found Webhooks:
if (webhook.owner.id == bot.user.id) { // check if bot owns them
webhook.send('test'); // Do something with the Webhook example
}
});
});
});

'Until loop' analogue needed - in order to continue bot dialog - after some status 'marker' is updated

'Until loop' analogue needed to continuously read status variable from helper function - and then (when the status variable is 'as we need it') - to resume bot conversation flow.
In my bot (botbuilder v.3.15) I did the following:
During one of my dialogues I needed to open external url in order
to collect some information from the user through that url.
After that I posted collected data (with conversation ID and other info) from that url to my bot app.js
file
After that I needed to resume my bot conversation
For that I created helper file - helper.js in which 'marker' variable is 'undefined' when the data from url is not yet collected, and 'marker' variable is some 'string' when the data is collected and we can continue our bot conversation
helper.js
var marker;
module.exports = {
checkAddressStatus: function() {
return marker;
},
saveAddressStatus: function(options) {
marker = options.conversation.id;
}
}
I can successfully update variable 'marker' with my data, by calling saveAddressStatus function from app.js.
However, when I get back to writing my code which is related to bot conversation flow (To the place in code after which I opened url - in file address.js, and from where I plan to continuously check the 'marker' variable whether it is already updated - in order to fire 'next()' command and continue with session.endDialogWithResult -> and then -> to further bot conversation flows - I cannot find the equivalent of 'until loop' in Node.js to resume my session in bot dialog - by returning 'next()' and continuing with the bot flow.
address.js
...
lib.dialog('/', [
function (session, args, next) {
...
next();
},
function (session, results, next) {
// Herocard with a link to external url
// My stupid infinite loop code, I tried various options, with promises etc., but it's all not working as I expect it
while (typeof helper.checkAddressStatus() == 'undefined') {
console.log('Undefined marker in address.js while loop')
}
var markerAddress = helper.checkAddressStatus();
console.log(markerAddress);
next(); // THE MOST IMPORTANT PART OF THE CODE - IF markerAddress is not 'undefined' - make another step in dialog flow to end dialog with result
function(session, results) {
...session.endDialogWithResult({markerAddress: markerAddress})
}
...
Any ideas how to make a very simple 'until loop' analoque in this context - work?
Having your bot stop and wait for a response is considered bad practice. If all of your bot instances are stuck waiting for the user to fill out the external form, your app won't be able to process incoming requests. I would at least recommend adding a timeout if you decide to pursue that route.
Instead of triggering your helper class in the endpoint you created, you should send a proactive message to the user to continue the conversation. To do this, you will need to get the conversation reference from the session and encode it in the URL that you send to the user. You can get the conversation reference from the session - session.message.address - and at the very least you will need to encode the bot id, conversation id, and the serviceUrl in the URL. Then when you send the data collected from the user back to the bot, include the conversation reference details for the proactive message. Finally, when your bot receives the data, recreate the conversation reference and send the proactive message to the user.
Here is how your conversation reference should be structured:
const conversationReference = {
bot: {id: req.body.botId },
conversation: {id: req.body.conversationId},
serviceUrl: req.body.serviceUrl
};
Here is an example of sending a proactive message:
function sendProactiveMessage(conversationReference ) {
var msg = new builder.Message().address(conversationReference );
msg.text('Hello, this is a notification');
msg.textLocale('en-US');
bot.send(msg);
}
For more information about sending proactive messages, checkout these samples and this documentation on proactive messages.
Hope this helps!

Resources