Call LUIS outside of MessageController and after Authentication - sharepoint

Working on a Bot that authenticates using ADAL (AuthBot) and then post-Auth takes the user input and sends to LUIS to gather intents/entities. I use what's returned to build a URI that I send to Sharepoint Online REST API. If user input is valid, Sharepoint returns JSON that I parse and return to user.
The trouble is getting the user input to my LUIS class after authentication. I call AuthBot ActionDialog from my MessageController.
if (message.Type == "Message")
{
return await Conversation.SendAsync(message, () => new ActionDialog());
}
Within ActionDialog, I'm not sure how to move move the Message to the LUIS Class
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<Message> item)
{
var message = await item;
if (message.Text == "logon")
{
if (string.IsNullOrEmpty(await context.GetAccessToken(ConfigurationManager.AppSettings["ActiveDirectory.ResourceId"])))
{
await context.Forward(new AzureAuthDialog(ConfigurationManager.AppSettings["ActiveDirectory.ResourceId"]), this.ResumeAfterAuth, message, CancellationToken.None);
}
else
{
context.Wait(MessageReceivedAsync);
}
}
else if (string.IsNullOrEmpty(await context.GetAccessToken(ConfigurationManager.AppSettings["ActiveDirectory.ResourceId"])))
{
await context.Forward(new AzureAuthDialog(ConfigurationManager.AppSettings["ActiveDirectory.ResourceId"]), this.ResumeAfterAuth, message, CancellationToken.None);
}
else
{
//this is where I want to send the next user input from bot to LUIS class.
}
}
The LUIS Class is standard and looks like this:
//Define the LuisModel that will be used. The LuisModel JSON file can be found at ~/json/letseat.json
[LuisModel("ModelID", "ModelSecret")]
[Serializable]
public class LuisDialog : LuisDialog<object>
Any Ideas? Thanks.

I assume that you are using AuthBot (by looking at the code).
What you need to add is the following:
await base.MessageReceived(context, item);
That will just pass the message to LUISDialog's MessageReceived implementation; which will issue a query to LUIS to understand which intent should be executed.

Related

Microsoft Bot Framework mixing conversation and dialogs between users who send request to bot at same time

I have implemented a bot using Microsoft Bot Framework SDK 4. The Bot is working fine, however I have experienced its not working when two users sends the request to the bot at same time.
I have printed the turnContext values for the users on each turn and its printing correct user and conversation data, however when the beginDialog calls, then sometimes its encountered with Error or sometimes it's mixing dialog response to users (Bot send response to other user).
I have implemented multiple dialogs steps using Waterfall Dialog: Find below code snippet : Where Message 'Hi there :) Welcome User : Name of the user ' is working fine with multiple users, but beginDialog is not working and mixing response to users.
In bot.ts
adapter.onMessage = async turnContext => {
const results = await adapter.continueDialog()
const recognitionResult = await Luis.recognize(turnContext)
const { topScoringIntent } = recognitionResult
if (topScoringIntent === 'welcome') {
await turnContext.sendActivity(`Hi there :) Welcome User ${turnContext.activity.form.name}` )
await adapter.beginDialog(DialogsTimebot.GreetingDialog, {
recognitionResult
})
}
}
In adapter.ts
async beginDialog(dialog: Dialog, options: object | undefined) {
if (!dialogMap.has(dialog)) {
const id = `dialog-${randomId()}`
const dialogClass = new (class extends ComponentDialog {
constructor() {
super(id)
this.addDialog(
new WaterfallDialog(
`waterfall-${id}`,
dialog.steps.map(step => modStep(step, dialogMap))
)
)
}
})()
dialogMap.set(dialog, { id, dialogClass })
_dialogSet.add(dialogClass)
}
const dialogId = dialogMap.get(dialog)!.id
await _dialogContext.beginDialog(dialogId, options)
},
Can anyone please help how to solve this issue? I have got stuck and not able to find the root cause. Thank you.

How can I add user info to conv.user.storage?

I'm using Actions Builder to create my chatbot and after user logins using Google I want to save his ID to storage variable.
This storage variable doesn't exist on conv.user.
So I do this:
if (conv.user.verificationStatus === 'VERIFIED') {
conv.user.storage = {};
conv.user.storage.id = str.rows[0].id;
console.log("STORAGE");
console.log(conv.user.storage.id);
}
But on Google Assistant it returns the error message and on my Webhook it's all good (no errors shown):
Google Assistant Error
What can I do to save/persist at least my user ID for future referings?
Since user has the Google Sign In process done once, every time he enters in your action you have his info on the request (payload). It´s automatically added to user storage.
You should store it on conv.user.params and refer to it in your code.
You may have a get and set method to help you with:
getUserId(conv) {
return conv.user.params.userId;
}
setUserId(conv, userId) {
try {
conv.user.params.userId = userId;
} catch (e) {
throw new error("Error setting USERID");
}
return userId;
}

Detect end of conversation and ask for a feedback in azure Bot

I am creating a chat bot using azure bot framework in Nodejs.
QnA maker to store question answers and one LUIS app.
Now I want to detect end of conversation(either by checking no reply from long time or refreshing a webpage) and add feedback card at the end of conversation.
You can achieve this by use of the onEndDialog method and the use of a separate class to manage the feedback process.
First, I have a component dialog that imports the feedback.js file and calls the associated onTurn() method within onEndDialog.
Next, I create the mainDialog.js file in which MainDialog extends FeedbackDialog. In this way, FeedbackDialog sits "on top" of MainDialog listening for specific user inputs or activities. In this case, it is listening for EndDialog() to be called. You will likely want to add additional validation to be sure it only fires when the EndDialg() you want is called.
Lastly, in the feedback.js file, this is where your feedback code/logic lives. For simplicity, I'm using a community project, botbuilder-feedback, for generating a user feedback interface. The majority of the code is focused on creating and managing the "base" dialog. Additional dialog activity comes from within the botbuilder-feedback package.
For reference, this code is based partly on the 13.core-bot sample found in the Botbuilder-Samples repo.
Hope of help!
feedbackDialog.js:
const { ComponentDialog } = require('botbuilder-dialogs');
const { Feedback } = require('./feedback');
class FeedbackDialog extends ComponentDialog {
constructor() {
super();
this.feedback = new Feedback();
}
async onEndDialog ( innerDc ) {
return await this.feedback.onTurn( innerDc );
}
}
module.exports.FeedbackDialog = FeedbackDialog;
mainDialog.js:
const { FeedbackDialog } = require( './feedbackDialog' );
class MainDialog extends FeedbackDialog {
[...]
}
module.exports.MainDialog = MainDialog;
feedback.js:
const { ActivityTypes } = require('botbuilder');
const { DialogTurnStatus } = require('botbuilder-dialogs');
const Botbuilder_Feedback = require('botbuilder-feedback').Feedback;
class Feedback {
async onTurn(turnContext, next) {
if (turnContext.activity.type === ActivityTypes.Message) {
await Botbuilder_Feedback.sendFeedbackActivity(turnContext, 'Please rate this dialog');
return { 'status': DialogTurnStatus.waiting };
} else {
return { 'status': DialogTurnStatus.cancelled };
}
await next();
};
}
module.exports.Feedback = Feedback;

Using conditional operators with QnAMaker - operators aren't routing correctly

I'm having difficulty figuring out what most likely is a simple issue, which relates to a 'if then else' problem in my code (NodeJS, Bot Framework v4).
I can't quite figure out why the relevant card isn't being shown depending on the number of semi-colons it finds in the response string from QnAMaker.
When testing with the Bot Framework emulator, it only returns one response type, whether that's plain text or one Rich Card no matter how many semi-colons are in the response.
I've tried to see if it's the length of the string it's having problems with by parsing the number value in the length statement. Didn't make a difference sadly. Notably if I use any other conditional operator such as '===' for example, it breaks the response completely.
const { ActivityTypes, CardFactory } = require('botbuilder');
const { WelcomeCard } = require('./dialogs/welcome');
// const { HeroCard } = require('./dialogs/welcome');
// const { VideoCard } = require('./dialogs/welcome');
class MyBot {
/**
*
* #param {TurnContext} on turn context object.
*/
constructor(qnaServices) {
this.qnaServices = qnaServices;
}
async onTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.Message) {
for (let i = 0; i < this.qnaServices.length; i++) {
// Perform a call to the QnA Maker service to retrieve matching Question and Answer pairs.
const qnaResults = await this.qnaServices[i].getAnswers(turnContext);
const qnaCard = qnaResults.includes(';');
// If an answer was received from QnA Maker, send the answer back to the user and exit.
if (qnaCard.toString().split(';').length < 3) {
await turnContext.sendActivity(qnaResults[0].answer);
await turnContext.sendActivity({
text: 'Hero Card',
attachments: [CardFactory.heroCard(HeroCard)]
});
} else if (qnaCard.toString().split(';').length > 3) {
await turnContext.sendActivity(qnaResults[0].answer);
await turnContext.sendActivity({
text: 'Video Card',
attachments: [CardFactory.videoCard(VideoCard)]
});
} else if (qnaCard.toString().split(';').length === 0) {
await turnContext.sendActivity(qnaResults[0].answer);
return;
}
}
// If no answers were returned from QnA Maker, reply with help.
await turnContext.sendActivity('No QnA Maker answers were found.');
} else {
await turnContext.sendActivity(`[${ turnContext.activity.type } event detected]`);
} if (turnContext.activity.type === ActivityTypes.ConversationUpdate) {
// Handle ConversationUpdate activity type, which is used to indicates new members add to
// the conversation.
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
// Do we have any new members added to the conversation?
if (turnContext.activity.membersAdded.length !== 0) {
// Iterate over all new members added to the conversation
for (var idx in turnContext.activity.membersAdded) {
// Greet anyone that was not the target (recipient) of this message
// the 'bot' is the recipient for events from the channel,
// context.activity.membersAdded == context.activity.recipient.Id indicates the
// bot was added to the conversation.
if (turnContext.activity.membersAdded[idx].id !== turnContext.activity.recipient.id) {
// Welcome user.
// When activity type is "conversationUpdate" and the member joining the conversation is the bot
// we will send our Welcome Adaptive Card. This will only be sent once, when the Bot joins conversation
// To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
const welcomeCard = CardFactory.adaptiveCard(WelcomeCard);
await turnContext.sendActivity({ attachments: [welcomeCard] });
}
}
}
}
}
}
module.exports.MyBot = MyBot;
Ideally, what I'm hoping to see is if I ask a question which has 3 semi-colons in the response, it outputs a Hero Card. If it has more than 3, then a Video Card and if it doesn't have either, a text response.
I'm not a js specialist, but I'm quite confused by the following:
const qnaCard = qnaResults.includes(';');
In Javascript, includes is the following (source):
The includes() method determines whether an array includes a certain
value among its entries, returning true or false as appropriate.
So here your qnaCard is true or false. But it looks like you are trying to use it as if it was containing the text:
if (qnaCard.toString().split(';').length < 3) {
...
You have to work on the object containing the answer: qnaResults[0].answer.

How to view response of Luis.ai in azure web bot app

I am working on a web bot app and I link it with luis.ai
I want to view the response of luis like which intent is been called and what was the entity called. I am using bot emulator version 4, but you can't find any type of info related to your intent or entities in it.
Is there any way we can see the json response of luis.ai ?
So that I can start building my bot further.
I am asking this because look How am I gonna know what's the format of luis response, how to get data from it as long as I don't know In which format i an receiving the response.
any details tutorial please?
There two ways to see luis response.
you can go to luis.ai and then copy the url of pulishment(in pulish section). And paste it in the navegator. You will get url like this: 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/xxxx?subscription-key=xxxxxxxxxx&verbose=true&timezoneOffset=0&q=your sentence', and setup q= your sentence.
Another way, you can log all luis conversation in you code. If you use c sharp bot builder, you can use this luis class.
[Serializable]
public class LogedLuisService : ILuisService
{
private ILuisService service;
private string moduleType;
public LogedLuisService(ILuisService service)
{
this.service = service;
}
public Uri BuildUri(LuisRequest luisRequest)
{
return service.BuildUri(luisRequest);
}
public LuisRequest ModifyRequest(LuisRequest request)
{
return service.ModifyRequest(request);
}
public Task<LuisResult> QueryAsync(Uri uri, CancellationToken token)
{
return service
.QueryAsync(uri, token)
.ContinueWith(
task => {
Trace.WriteLine("Luis: " + " : " + JsonConvert.SerializeObject(task.Result));
return task.Result;
});
}}

Resources