Building chatbot using amazon lex - aws-lambda

I tried to build the weather chatbot with Open Weather API and associated lamba function in the intent fulfillment.But even after adding enough sample utterances ,the chatbot shows the default answer "Intent FindingWeather(name of the bot) is fulfilled" and not the intended response.
Lambda Function
Any leads on how to solve this?
I expected the response to be "The temperature in London (input city name) is 12C and Humidity level is 55 %".
URL used :
const url = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "&units=metric&APPID=5676472da991b44958b01f0e17623688";

Related

bot framework and Microsoft teams - how to get all channels associated to a team?

I am trying to get all channels associated with a specific team so that my bot can send proactive messages. Based on the reading I've done, I need to use the FetchChannelList method in the Microsoft.Bot.Connector.Teams namespace, in the TeamsOperationsExtensions class.
If I do this:
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
ConversationList channels = connector.GetTeamsConnectorClient().Teams.FetchChannelList(activity.GetChannelData<TeamsChannelData>().Team.Id);
channels is null. If I break it down to only connector.GetTeamsConnectorClient(), that is not null, but connector.GetTeamsConnectorClient().Teams.FetchChannelList(activity.GetChannelData().Team.Id) is.
To break it down further, I tried to get activity.GetChannelData(). Only the Tenant property is not null. All the others (Channel, Team, EventType and Notification) are null.
I am using tunnelrelay, which forwards messages sent to the bot's public endpoint to a private endpoint, and am using tenant filter authentication in the messages controller. Not sure if that would cause any problems? (When I watch messages coming in through tunnel relay, I see there too that only Tenant is the only channeldata property which is not null. Here's what I see in tunnelrelay:
"entities":[{"locale":"en- US","country":"US","platform":"Windows","type":"clientInfo"}],"channelData":{"tenant":{"id":"our_tenant_id"}}}
Also, regarding the teamID expected as a parameter to the FetchChannelList method, how do I find out what that is for a given team other than the GetChannelData() method? I tried the powershell cmdlet Get-Team (for example: Get-Team -User me#abc.com). It returns a distinct groupId for each team I am a part of, but I'm assuming groupId != TeamId. Is that correct? And, where can I find the teamId that the FetchChannelList is expecting other than the GetChannelData method?
Thanks in advance for any help!
The problem here was that the message to the bot (the activity) was a direct message, not a part of a channel conversation. Apparently, the Channel and Team properties are only available in a channel conversation.
Also, regarding the team ID, one way to get it outside of code is to click the "..." next to the team and click "get link to team". You will see something like:
https://teams.microsoft.com/l/team/19%3a813345c7fafe437e8737057505224dc3%40thread.skype/conversations?groupId=Some_GUID&tenantId=Some_GUID
The line after team/ (19%3a813345c7fafe437e871111115934th3%40thread.skype) contains the teamId, but not exactly. If you replace the first % and the two characters immediately following it with : and the second % and the two characters immediately following it with #, that is your teamid. So, from:
19%3a813345c7fafe437e871111115934th3%40thread.skype
the team ID is:
19:813345c7fafe437e871111115934th3#thread.skype

LUIS - Can I have 2 languages (Chinese and English) in same App, and still have good result?

I am currently using MS LUIS for Chatbot.
Our country usually talks and chats using 2 languages, English and Chinese.
However, in LUIS I can only define one culture.
As a result, when my culture is set to English, and when I imports Chinese text, the confidence level is very low (e.g. English - 0.88, Chinese - 0.1). The other way round is the same.
The situation is the same even after I did tokenize the Chinese text using library like JieBa or THULAC.
Therefore when I did testing, it is usually very easy to fall into an unrelated intent.
I would like to make LUIS recognize both English AND Chinese easily. Are there any way to solve this problem?
Thank you very much for your help.
I would like to make LUIS recognize both English AND Chinese easily. Are there any way to solve this problem?
Yes, the way is to separate your LUIS apps/projects, 1 for each language, and use a language detection before calling LUIS.
That's the official approach from LUIS docs (see here):
If you need a multi-language LUIS client application such as a chat
bot, you have a few options. If LUIS supports all the languages, you
develop a LUIS app for each language. Each LUIS app has a unique app
ID, and endpoint log. If you need to provide language understanding
for a language LUIS does not support, you can use Microsoft Translator
API to translate the utterance into a supported language, submit the
utterance to the LUIS endpoint, and receive the resulting scores.
For the language detection you can use Text Analytics API from Microsoft Cognitive Services for example to get the text language, and then with this result query the right LUIS app.
How to use it?
Documentation of Language detection in Text Analytics API here
Text analytics API: here
As Nicolas mentioned above you can use Multilanguage chat application with separate LUIS apps for each culture.
In order to have single LUIS application, you could use the Translator Text API to translate all incoming messages before they're sent to LUIS. In this case you'll want to use middleware to handle the translation before your LUIS Recognizer is called. You can also use middleware to translate your bot's response so you don't have to use additional localization inside of your bot
Tokenizing in LUIS is different for each language in LUIS.
In the zh-cn culture, LUIS expects the simplified Chinese character set instead of the traditional character set.
Here is one more sample where you can select a language from the bot and continue the consversation as required.
After investigation and several testings, I guess I found a way on how to do that properly:
First of all, I am currently using MS BotFramework - NodeJS (3.14.0) version to create my Bot.
And in botbuilder class, you have a function called IntentDialog, it accepts a list of recognizers. So I wrote something like this:
In luis.js
const builder = require("botbuilder")
// Setting for LUIS.ai
// Universal API key for both apps
let luisAPIKey = process.env.LuisAPIKey;
// First assign variables for Chinese LUIS app
let luisAppId_Chi = process.env.LuisAppId_Chi;
let luisAPIHostName_Chi = process.env.LuisAPIHostName_Chi || 'westus.api.cognitive.microsoft.com';
let LuisModelUrl_Chi = 'https://' + luisAPIHostName_Chi + '/luis/v2.0/apps/' + luisAppId_Chi + '?subscription-key=' + luisAPIKey + '&verbose=true';
// Then assign variables for English LUIS app
let luisAppId_Eng = process.env.LuisAppId_Eng;
let luisAPIHostName_Eng = process.env.LuisAPIHostName_Eng || 'westus.api.cognitive.microsoft.com';
let LuisModelUrl_Eng = 'https://' + luisAPIHostName_Eng + '/luis/v2.0/apps/' + luisAppId_Eng + '?subscription-key=' + luisAPIKey + '&verbose=true';
// Return an object with 2 attributes: Chi for Chinese LUIS and Eng for English LUIS
let luis = {};
luis.chi = new builder.LuisRecognizer(LuisModelUrl_Chi);
luis.eng = new builder.LuisRecognizer(LuisModelUrl_Eng);
module.exports = luis;
In app.js
const luis = require("./luis")
builder.IntentDialog({ recognizers: [luis.chi, luis.eng] });
And when I tested in botEmulator, it seems that it will first check LUIS Chi app, and then go to LUIS Eng app.
I don't know what is the criteria/threshold that this recognizer used to controls whether jumps to another app or not. But at present it works for me to certain extend. It is not accurate by at least a good(?) start. :D
No MS Text translation API need.
By the way, the code will look nicer if I can get the topIntent and LUIS path right in session variable.
Hope it helps someone.

Recognize the user input inside the handler function using dialogflow.ai/api.ai and Microsoft Bot Builder

I am building a bot using the given technology stacks:
Microsoft Bot Builder
Node.js
Dialogflow.ai (api.ai)
We used waterfall model to implement a matched intent Dialog, which includes a couple of handler functions and prompts. After following this scenario I need to identify the entities inside an inner handler function, for a user input.
eg:
Bot : Where do you want to fly?
User: Singapore. (For this we added entities like SIN - Singapore,SIN(Synonym), So I need to resolve the value as SIN)
Any help on this scenario is much appreciated.
Here is a post Using api.ai with microsoft bot framework you can refer for your reqirement, and with a sample at https://github.com/GanadiniAkshay/weatherBot/blob/master/api.ai/index.js. The weather api key leveraged in this sample is out of date, but the waterfall and recognizer api key is still working.
Generally speaking:
Use api-ai-recognizer
Instantiate the apiairecognizer and leveraged builder.IntentDialog to include the recognizer:
var recognizer = new apiairecognizer("<api_key>");
var intents = new builder.IntentDialog({
recognizers: [recognizer]
});
In IntentDialogs, use builder.EntityRecognizer.findEntity(args.entities,'<entity>'); to recognize the intent entities.

Correct way to handle entities anytime in middle of conversation

I have started working with the LUIS and bot framework recently, after having some experience also with API AI / Google home development.
In the sample below that, I will use an example (from https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-dialog-waterfall) is exemplified a step by step interaction with a user. First, it asks for a date, then a number, then a name for the reserve, and so on.
var bot = new builder.UniversalBot(connector, [
function (session) {
session.send("Welcome to the dinner reservation.");
builder.Prompts.time(session, "Please provide a reservation date and time (e.g.: June 6th at 5pm)");
},
function (session, results) {
session.dialogData.reservationDate = builder.EntityRecognizer.resolveTime([results.response]);
builder.Prompts.text(session, "How many people are in your party?");
},
function (session, results) {
session.dialogData.partySize = results.response;
builder.Prompts.text(session, "Who's name will this reservation be under?");
},
function (session, results) {
session.dialogData.reservationName = results.response;
// Process request and display reservation details
session.send("Reservation confirmed. Reservation details: <br/>Date/Time: %s <br/>Party size: %s <br/>Reservation name: %s",
session.dialogData.reservationDate, session.dialogData.partySize, session.dialogData.reservationName);
session.endDialog();
}]);
In my code, I have a similar multi-parameter dialog, but I want to allow the user to answer with multiple information at the same time in any of the responses it have. For example, after providing the reservation date the user can say "a reserve for Robert for 10 people", so both numbers of people and reservation name are giving at the same time.
To identify these text entities I suppose I have to call LUIS and get the entities resolved from the session context. I notice that the bot object has a recognized method that I think can work for that.
My question is how do I organize the structure of the code and the LUIS utterances and entities? Right now I have an intent with some entities and several utterances samples, but if I send this 'partial' user sentence I think it will not be mapped to the same intent and may not identify the entities with a small sentence like that.
How should I handle this? Do I need to provide samples for the intent with these partial sentences, that may contain only some of the entities?
Thanks
Yes, you should provide samples for all those utterances that you want to your intent to recognize. Not a million of samples, but just as few to get everything trained.
Then, the other problem that you might want to solve next, is asking for the information for those entities missing in the utterance. You can do that manually or you could go one step further and explore the LUIS Action Binding library.

Parse.com Stripe: Creating a charge that is not captured

I'm working on an iOS app that uses Stripe to process payments. We are moving from a system that uses two separate charges for initial purchase and tip to a system that uses a single charge that begins as a hold on the user's account and then is captured upon setting the tip. This was the system that Stripe recommended to us when we inquired how to work with a single charge but also validate that the card can handle a charge of the designated amount.
For our back end, we are using Parse.com to track our orders, and so we are using Stripe's integration with Parse.com's Cloud Code as our server. Our main issue is that Parse.com doesn't seem to outright support most of Stripe's functionality (i.e. capturing charges). After some searching, I found that http POST requests were the best option to interact with Stripe.js and actually capture charges. However, I haven't been able to get quite that far because Parse.com is giving me a Code 141 error (Received unknown parameter: captured) when I try to create a charge that is uncaptured. Parse.com's Stripe API suggests that you can set all parameters through their Stripe.Charges.create, but it won't accept captured as a valid parameter.
To abstract for anyone else with this issue, how can I create a charge that has the parameter captured set to false using Parse.com Stripe API?
I have posted some of my Cloud Code below that should define a method to create a charge that has not yet been captured. This method is what is giving me the error that captured is not a valid parameter.
/**
* Create Hold on Card
* Required:
* orderCostInCents -- in cents ex. $10.24 = 1024
* customer -- cus_11EXEXEXEXEXEX
* description -- order.objectId to link it with order item.
*/
Parse.Cloud.define("holdAccount", function(request, response) {
//response.success("Not Charged");
var Stripe = require("stripe");
Stripe.initialize(kStripePrivateKey);
Stripe.Charges.create({
amount : request.params.orderCostInCents,
currency : "usd",
customer : request.params.customer,
captured : false,
description : request.params.description
},{
success: function(httpResponse) {
console.log(httpResponse);
response.success(httpResponse);
},
error: function(httpResponse) {
console.log(httpResponse.message);
response.error("Failed to create charge");
}
});
});
I believe that I can structure an http (POST) request after creating the charge by following the guidelines set at https://www.parse.com/questions/stripe-payment-capture-method-not-available. This guide might be very helpful to anyone else with my issue!
Best, and thanks for your help!
Edit: I realized that I didn't post the version of Cloud Code that we are using. It is 1.2.19.
Well, after taking a break from my hours of staring at the screen, I certainly feel like a doofus! The parameter I was using was captured, where the correct parameter should be capture. I was able to fix my issue by simply removing the "d" from the parameter name while creating the charge.
Whoops! I would still be open to advice on http requests via comments, but I will test those on my own and post a separate thread if I run into issues there as that issue is tangential to this one and thus off-topic.
For everyone joining, the answer is that the above code works perfectly if you replace the parameter captured with capture
Edit: For anyone else that is interested, the follow-up to this question was about actually making the capture via http requests on Parse Cloud Code. The following method works after much searching and trial and error. The hardest part here was figuring out how to format the URL since this is my first foray into http requests. If you need to chain parameters, simply add "&{parameter-name}={parameter-value}"
//kStripePrivateKey is your stripe private key
//Must pass in chargeID = stripe charge id and
//orderCostInCents = capture amount in cents as parameters
var captureURL = "https://"+ kStripePrivateKey +
":#api.stripe.com/v1/charges/"+
request.params.chargeID+
"/capture?amount="+request.params.orderCostInCents;
Parse.Cloud.httpRequest({
url: captureURL,
method: 'POST',
success: function(httpResponse) {
// Handle any success actions here
response.success(httpResponse);
}, error: function(httpResponse) {
response.error(httpResponse);
}
});

Resources