How to add Get Started button in the typing bar using bot builder sdk for node.js - node.js

I am using bot builder sdk for node.js to create a chatbot. Also connected it to facebook channel. I am using the following code to greet the user:
var bot = new builder.UniversalBot(connector, [
(session, result, next) => {
let text = '';
switch(session.message.address.channelId) {
case 'facebook':
text = 'Hi ' + session.message.user.name + ' !';
break;
default:
text = 'Hi !';
}
session.sendTyping();
session.say(text);
next();
},
(session, say) => {
}
]);
The above code works fine, but I want to add "Get Started" button in the typing bar to invoke the above code. Note that this button appears only once. Please find image of the typing bar below:
Is there a way to achieve this using bot builder sdk for node.js ?
Thanks

Although one can certainly add a button to start any activity with the bot, but that will limit the bots potential to only one customizable channel, i.e. WebChat.
I think there are better 2 alternative ways to get the desired functionality which will work across many channels.
First
I would suggest to add a conversation update event. Code goes in the botbuilder's middleware. Here is a sample code from the docs.
bot.on('conversationUpdate', function (message) {
if (message.membersAdded && message.membersAdded.length > 0) {
// Say hello
var txt = "Send me a Hi";
var reply = new builder.Message()
.address(message.address)
.text(txt);
bot.send(reply);
});
What this will do is make the bot send a message Send me a Hi to the user, if it determines this is a first time visitor. This will give the visitor enough cue to send the bot Hi by typing it. Although he can enter whatever he wants, but this will result in the invocation of the 1st dialog configured which in this case is the will be the dialog which you have posted in question.
Second
You can mark some dialog to be invoked automatically if your bot has never encountered this visitor. Here is the sample code...
var bot = new builder.UniversalBot(connector);
bot.dialog('firstRun', function (session) {
session.userData.firstRun = true;
session.send("Hello...").endDialog();
}).triggerAction({
onFindAction: function (context, callback) {
// Only trigger if we've never seen user before
if (!context.userData.firstRun) {
// Return a score of 1.1 to ensure the first run dialog wins
callback(null, 1.1);
} else {
callback(null, 0.0);
}
}
});
Here we have split the bot creation and dialog registration in 2 steps. And while registering the firstRun dialog, we have provided it the triggerAction that if the visitor is new, then trigger this dialog.
Both of these approaches do not use adding some extra buttons and it is up to the bot either to educate him on sending some message which in turn will start the 1st dialog or directly start some dialog.
For more info on conversationEvent you can refer to this page

I tried the above options, but they didn't seem to be working for facebook messenger. But I found a solution to add the Get Started button into the typing bar of the messenger. For that we need to use the Facebook Graph API and not the bot builder sdk.
https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>
{
"get_started":{
"payload":"Get Started"
}
}
The above API call will add the button for you to get the conversation started.
Thanks all for the help!!

Related

Microsoft Azure Web Chat, trigger mic from other function

I have used Microsoft Bot Framework to create a bot for the client side. i.e. called WebChat. I have also added Speech SpeechRecognizer. However, I am trying to trigger a mic when a phrase is recited.
I couldn't find a function in Microsoft that does this. So I added my own speech recognizer that is called every second and once the phrase is called I want to call the mic function from the Cognitive Services.
How Can I achieve this?
I have got the speech recognizer from here
And the one I have written to identify a phrase is this:
function startDictation() {
if (window.hasOwnProperty('webkitSpeechRecognition')) {
var recognition = new webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = "en-US";
recognition.start();
recognition.onresult = function (e) {
var foundText = e.results[0][0].transcript;
console.log(foundText);
if (foundText == "hello hello") {
console.log("found text");
//call cognitive service mic function
recognition.stop();
}
else {
console.log("text not found");
recognition.stop();
startDictation();
}
};
recognition.onerror = function (e) {
console.log("found error", e);
recognition.stop();
}
}
}
Do let me know if any information is missing or miss-leading.
For more:
I tried to leverage startRecognizing() function in SpeechRecognizer class at https://github.com/Microsoft/BotFramework-WebChat/blob/master/src/CognitiveServices/SpeechRecognition.ts#L72 to trigger the recognize functionality. However, I found that only if I have click the mic item, then I could use startRecognizing() function to recognize the voice.
There is a trick workaround you can try to use at present:
I inspected the mic item, and try to triiger its click event in js, which worked exactly to recognize my speech.
You can try to use following js code snippet with jQuery:
$('.wc-mic').trigger("click")
Hope it helps.

Connecting LUIS to Microsoft Bot Framework

Over the holiday weekend, I've been trying to get a bot working using the Microsoft Bot Framework. I'm using version 3.9.1 of the botbuilder package for Node.js.
I've created an app and model at www.luis.ai. I have been able to successfully test my intents via the "Train & Test" feature. Then, in my actual Node code, I have the following:
let connector = new BotBuilder.ChatConnector({
appId: 'myId',
appPassword: 'myAppSecret'
});
let bot = new BotBuilder.UniversalBot(connector);
let luis = new BotBuilder.LuisRecognizer('myLuisAppUrl');
let intent = new BotBuilder.IntentDialog({ });
intent.recognizer(luis);
intent.matches('Intent.1', '/execute-report');
intent.matches('Intent.2', '/execute-batch-job');
intent.onDefault('/unknown');
bot.dialog('/', intent);
bot.dialog('/execute-report', [function(session, args, next) {
var result = ((Date.now() % 2) === 0) ? 'Report Ran!' : 'Failed';
session.send(result);
}]);
bot.dialog('/execute-batch-job', [function(session, args, next) {
var result = ((Date.now() % 2) === 0) ? 'Batch Job Ran!' : 'Unable to run Batch Job';
session.send(result);
}]);
bot.dialog('/unknown', [function(session, args, next) {
session.send('What did you ask for?');
}]);
When interacting with my bot, I always get "What did you ask for?". In other words, at this point, I know that:
I can successfully interact with my bot. However, the /unknown dialog is always being called, which is not the correct interaction.
My model in LUIS looks correct:
a. If I enter "Run Report" in the LUIS.ai Test app, the top scoring intent is "Intent.1"
b. If I enter "Execute Batch Job" in the LUIS.ai Test app, the top scoring intent is "Intent.2"
However, my bot is not sending the appropriate response. The /execute-report and /execute-batch-job dialogs are never used, even though they should be. I don't understand what I'm doing wrong. To me, I believe I've setup my bot correctly. I don't see what I'm doing wrong. Can someone please tell me what I'm doing wrong? Is there a way to see the response returned from LUIS in my Node code similar to what's seen in the "Test" app at LUIS.ai
If you go to line 89 of the LuisRecognizer and add the following on a new line: console.log(result); you will see the LUIS response object that your bot has received.
Your code looks correct to me, so the issue might be on the LUIS side. Have you published your app?

Unable to pass control to another method within a dialog in botbuilder for Node.js

I'm creating my first bot with Node.js and MS Bot Framework and I'm trying to figure out how to pass control from one method to another within a dialog.
In Bot Framework for C#, it's very easy:
context.Wait(NextMethodName);
where NextMethodName is the name of the method that runs after the bot receives the next user message.
I am trying to do a similar thing in Node.js. I have two functions. The first one prompts the user to enter something or click a button, and the second should process the user's input. I am struggling with passing control to the second function.
bot.dialog('subscribe', [
function (session) {
var card = new builder.HeroCard(session)
.title("Subscribe for reminders?")
.text("It seems you're not enrolled yet. Subscribe for reminders to submit your work hours?")
.buttons([
builder.CardAction.imBack(session, "subscribe", "Subscribe")
]);
var msg = new builder.Message(session).attachments([card]);
session.send(msg);
//next(); //compile error
},
function (session, results) {
if (results.response === 'subscribe') {
session.send('You are now subscribed to reminders through ' + session.message.user.channelId + '.');
}
else {
session.send('You must subscribe to reminders before using this bot.');
}
}
]);
How do I make the second function run after the user clicks the button or answers anything?
In node's botbuilder sdk, you can define waterfall dialogs that contains what are called as 'steps'. Each step leads to another (like a waterfall). According to docs:
'Waterfalls let you collect input from a user using a sequence of steps. A bot is
always in a state of providing a user with information or asking a
question and then waiting for input. In the Node version of Bot
Builder it's waterfalls that drive this back-n-forth flow'.
Some steps can start with a prompt to ask the user for information, and then the following step handles the response by saving it using dialogData. Then you can invoke the next function argument to pass control to the next step. In your case, calling next() gives you an error because that function isn't in scope, you must provide it as a parameter to your step function.
Check this sample here:
https://github.com/Microsoft/BotBuilder-Samples/tree/master/Node/core-MultiDialogs
In your first step code I would do:
function (session,args,next) {
var card = new builder.HeroCard(session)
.title("Subscribe for reminders?")
.text("It seems you're not enrolled yet. Subscribe for reminders to submit your work hours?")
.buttons([
builder.CardAction.imBack(session, "subscribe", "Subscribe")
]);
var msg = new builder.Message(session).attachments([card]);
session.send(msg);
next();
}
But that would just lead you to the next step, so if you want to wait for user input (with text prompt), or for example using HeroCard actions, like you defined in your sample:
Your card triggers an action called "subscribe" with the parameter "Subscribe" via a button. Think of this as a function that is called within your dialog by pressing the button on the card. Now to define that function, we do:
// An action is essentially a card calling a global dialog method
// with respective parameters. This dialog action will route the action
// command to a dialog.
bot.beginDialogAction('subscribe', '/subscribe');
// Create the dialog for the action...
bot.dialog('/subscribe', [
function (session, args) {
//do something!
}
]);

Messaging a user a bot does not know

I am using the Slack RTM node client and having a bit of an issue with DM's. Say a user joins the channel who has never DM'ed the bot before, the user types a command in the channel that the bot usually will respond to and by default the bot responds in a private message to the user. However, the bot cannot do this because the dataStore does not contain any DM data for this user. Code sample below...
rtm.on(RTM_EVENTS.MESSAGE, function (message) {
user = rtm.getUserById(message.user);
console.log(user); // It gets the user object fine
dm = rtm.getDMByName(user.name);
console.log(dm); // This is always undefined unless the user has DM'ed the bot previously
});
Is there a way around this? I can't seem to find anything in the docs or code to suggest there might be.
You can use the im.open method of the web API. Here's roughly how you'd do it with #slack/client (untested, apologies in advance!):
var webClient = new WebClient(token);
...
rtm.on(RTM_EVENTS.MESSAGE, function (message) {
var dm = rtm.getDMById(message.user);
if (dm) {
console.log(`Already open IM: ${dm}`);
// send a message or whatever you want to do here
} else {
webClient.im.open(message.user, function (err, result) {
var dm = result.channel.id;
console.log(`Newly opened IM: ${dm}`);
// send a message or whatever you want to do here
});
}
});

Pusher Window Refresh Issue

i am very new to pusher.com:
I am trying to set up a presence-channel Chat.
Here is my code:
var PresenceChannel = pusher.subscribe('presence-test_channel');
PresenceChannel.bind('pusher:subscription_succeeded', function(members){
$("#chatMembers").empty();
members.each(function(member) {
$("#chatMembers").prepend("<li id='"+member.info.employee_id+"'>"+member.info.customer_id+"</li>");
});
});
PresenceChannel.bind('pusher:member_added',function(member){
$("#chatMembers").prepend("<li id='"+member.info.employee_id+"'>"+member.info.customer_id+"</li>");
});
PresenceChannel.bind('pusher:member_removed',function(member){
$("li#"+member.info.employee_id).remove();
});
Its working as expected.
But i have a problem:
When i refresh one of the opened browser windows, the following events get fired:
PresenceChannel.bind('pusher:member_added',function(member){...
And directly after that,
PresenceChannel.bind('pusher:member_removed',function(member){...
get fired.
So, after a refresh of one window, the user get removed from my list, and
1 second later, the user again is added to the list....
1) Reload 1 browser window
2) The other window triggers 'pusher:member_removed': User removed from List
3) The other window triggers 'pusher:member_added': User added to the list agein
What to do ?
The 2nd window receives a pusher:member_removed because the 1st window has unloaded and the user had therefore left the presence channel. When the 2nd window reloads and the user resubscribes to the presence channel the pusher:member_added is triggered.
This is expected behaviour.
However, Pusher do add a delay to these event in order to try and stop events being triggered in this scenario. In your case it would seem that the delay in not long enough to stop that happening. In your situation there is an FAQ which provides some information about what you can do to work around this:
How can I stop users going offline for an instant when they navigate between pages?
It is simply solved.
Try this.
Pusher dashboard -> Webhooks
and add Webhook url & event type to Presense.
$app_secret = 'YOUR PUSHER SECRET KEY';
$app_key = $request->headers->get('X-Pusher-Key');
$webhook_signature = $request->headers->get('X-Pusher-Signature');
$body = file_get_contents('php://input');
$expected_signature = hash_hmac( 'sha256', $body, $app_secret, false );
if($webhook_signature == $expected_signature) {
// decode as associative array
$payload = json_decode( $body, true );
foreach($payload['events'] as &$event) {
// do something with the event
if ($event['name'] == 'member_added') {
// do process user joind & trigger message
$this->setAddMember($event);
} elseif ($event['name'] == 'member_removed') {
// do process user out & trigger message
$this->setRemoveMember($event);
}
}
header("Status: 200 OK");
}
else {
header("Status: 401 Not authenticated");
}
More detailed information see document this.
https://pusher.com/docs/webhooks

Resources