Getting Agent Info from API.ai fulfilment request - node.js

Is there a way that I can get the API.ai agent information from the fulfillment request?
I am trying to capture any unique parameter from Node.js code that can be passed from Agent. This will be utilized for proceeding with the logic for multiple agents using a single code base.
For Amazon Alexa I could get the Skill Application Id from the session. Is there something similar in API.ai?

If you want to get agent id you can make something like this (using express framework):
app.post(`/:agent_id/webhook`, (req, res) => {
const apiai_agent_id = req.params.agent_id;
// webhook code...
});
And on fulfillment page in this case for each agent you have to specify it's own webhook URL like
https://mywebhooks.com/55982e7c-db17-47ee-92bb-176476228942/webhook
(you can get agent id for webhook URL from browser's address bar)

You can use the API.AI's sessionId or intentId which will be unique across agents. The sessionId and intentId* is sent with every fulfillment webhook request. The of the JSON sent are similar to this:
{
"id": "1a4b6209-51ec-47a1-a797-2e2f71926ac8",
"sessionId": "1503343146047"
...
}
but will contain other elements as well.
*intentId may include additional numeric identifier after the intent ID if you are using slot filling
Source: https://api.ai/docs/fulfillment#request

Related

Handle Dialogflow requests from multiple users through ngrok

I'm working on a website for school, using Dialogflow, ReactJs and NodeJs. I am pretty new to doing this and I am not certain whether my implementation will work for my use case.
My website involves a game, which the server-side needs game state information from. I am sending requests from React to NodeJs as follows:
axios({
method: 'post',
url: '/game_state',
data: {'current_player': current_player}
// ... (more data is sent in reality)
}
);
My post requests are then handled by NodeJs as follows:
app.post('/game_state',express.json(),(event)=>{
current_player = event.body.current_player;
// ... (more data)
});
This data can then be accessed by a Dialogflow request. For example, if the user asks: "Who's turn is it?", an intent with webhook fulfillment can then be triggered. The code for that:
function turn(agent){
current_player == 1 ? agent.add("Your turn") : agent.add("My turn");
}
As I said before, I am not very experienced with developing websites. I want to be able to use my implementation with multiple users at the same time, so one user's game states shouldn't influence those of another user.
So far, I've been using (the free version of) ngrok to test my website. My implementation works perfectly fine if only one user is accessing the link. However, when I open the website twice on the same computer (using the ngrok-generated link) and interact with e.g. the turn intent, I notice that the turn response will be the same for both even if for one session, it's the player's turn and for the other, it's the chatbot's turn.
Most likely, the post request is being overwritten given that the same link is used. I would like to find a solution that makes it possible to use the same link but have the server keep the game state information separate for each user. I feel like this might be possible with a user ID, but I'm stumped on how to implement that.
I know something like this may be an option: app.get("/game_state/:id", ...), but I am not sure how to also get this information to be available for the Dialogflow part of the implementation. Dialogflow calls the server itself, so it doesn't pass this user ID with it. Somehow, I'd need to distinguish it there as well.
Any suggestions would be greatly appreciated!
When you make Dialogflow detected intent API call at that time you are creating a session path
const sessionPath = await sessionClient.projectAgentSessionPath(PROJECTID, sessionId);
If you pass session Id = 123456789A so for this Id Dialogflow managed all context and parameter on Id 123456789A
session Id = USER
when you get the webhook call to the server you also get this session Id or we also called UserId like
let userId = agent.session.split('/')[4]
you can find more information here
dialogflow session

Stripe Elements not talking to stripe server - No such token: tok_xxxx

So I'm migrating from the stripe checkout to elements so that I can register and charge a customer in a single form.
I'm following their documentation:
https://stripe.com/docs/stripe-js/elements/quickstart
Specifically in Step 3 where the token is created with the following code:
// Create a token or display an error when the form is submitted.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the customer that there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
}
});
});
The documentation states:
The handler sends the fields to Stripe for tokenization...
However, it seems nothing is being sent to Stripe at all!
The bottom two requests show the two api calls made using the old checkout: First the card info is sent to /tokens and then my server handles this and makes the /customers request fine.
But when using the elements code nothing seems to be sent to stripe, even though a token is generated and sent to my server. So then I get the 'No such token' error!?
I can't for the life of me workout why stripe.createToken(card) isn't sending the details to their server and yet generates a token?? I've triple checked api keys and everything I can think of. Please help!!
What the actual heck... I got it to work but I don't know why this fixed it.
I'm using stripe elements on a .net mvc website and i'm initializing the Stripe class in a view so I can easily pass the key from my viewmodel like so:
var stripe = Stripe('#Model.StripePublicKey');
Which should work fine, right? No JS errors or anything and it was creating the token, just not posting to stripe's servers....
Anyway, I moved that line into the JS file so it's directly above the other stripe related JS and now it works!?

Making a POST request in webhook in an Actions on Google app.intent [duplicate]

This question already has an answer here:
Making an HTTP POST request from fulfillment in Dialogflow
(1 answer)
Closed 4 years ago.
We are creating an action that will take user's input and create an entity in our database (datastore).
Ideally, we would like to be able to access the user's raw input audio, but it doesn't seem that is possible.
As a work around we are going to send the speech-to-text of user's utterance to our backend services. We are using firebase cloud functions for our fulfillment and an external rest api for our crud operations.
We are trying to make a post request in a webhook to create an entity based on user's input, but when I check my logs it doesn't seem like the post request is reaching our service. I'm not able to debug what or if we are getting a response back
app.intent('favorite color', (conv, {color}) => {
const options = {
// options
};
function callback(error, response, body) {
// log response or error
}
request(options, callback);
const luckyNumber = color.length;
// Respond with the user's lucky number and end the conversation.
conv.close('This word has ' + luckyNumber + ' letters.');
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
This question is not the same as the one that it was marked as a duplicate because the solution was the account type not supporting POST requests to an external API and not the HTTP Client we were using
The Inline Editor in the Dialogflow console uses Firebase Cloud Functions, as you already know.
Unfortunately, Firebase Cloud Functions DOESN'T support external API calls in it's free plan. You may need to switch to blaze plan or deploy your fulfillment elsewhere.

Dialogfow webhook using static response

I'm designing a Google Actions webhook. I want to use the pre-defined static response for most requests, but in some situations, I want to return a 'simple response' to the user.
I've read that if my webhook does not return a response, then the static response will be used, but the webhook response format requires a 'Richresponse'. How can I send a response that will cause the static response to be used?
If you want to use the static responses that are defined in Dialogflow console, you can use the incoming property of the conversation object. here is the document explaining how it works.

Twilio is not sending creds in headers when specifying username:password format in the URL

I'm currently developing my app and I'm at the stage where I can start testing messages from Twilio. I configured my server on digital ocean with a public facing IP address and my Nodejs app is listening to calls from Twilio.
I also configured my phone number's message "request url" to "http://username:password#198.xxx.xxx.xxx/messages" with "HTTP POST".
When I debug the headers, I don't see the "authorization" headers. I'm I missing something here?
Any help is much appreciated!
Below is the code.
var headerValues = bag.req.headers.authorization.split(' ');
console.log(bag.req.headers);
var scheme = headerValues[0];
if (scheme === 'Basic') {
var credentials = headerValues[1];
var decoded = new Buffer(credentials, 'base64').toString().split(':');
bag.req.creds = {
userName: decoded[0],
password: decoded[1],
authType: 'basic'
}
}
I use the same setup as you do in several call centers I have built.
If you are using a proxy setup which requires username:password# before the IP address then your issue is likely with that proxy if you can access the code by going directly to the actual server ip address as I note below. However, you did not mention using a proxy just using a digital ocean droplet so I am responding assuming you do not have a proxy setup.
So if you do have a proxy setup make sure you can access the IP address of the server directly first.
Also if those are just extra variables you need to pass over you may be better off appending them after the IP address
for instance xxx.xxx.xxx.xxx/username/password
Then get them with req.params
for instance (and yes this will work with post data since its merely part of the URL and not an actual get command post)
router.post('/sms/:username/:password'), function(req, res, next){
username = req.params.username;
}
First you would not want to direct your request URL at "http://username:password#198.xxx.xxx.xxx/messages" with "HTTP POST".
If you do not have a domain directed at your IP address yet you want your request URL to be
https://198.xxx.xxx.xxx/inbound/sms
{Replacing /inbound/sms with whatever route you are using}
Then at the top of your route (I am using express so my setup may look different than your)
I have the node.js twilio library
, twilio = require('twilio')
, capability = new twilio.Capability(sid, auth)
, client = require('twilio')(sid, auth)
Then here is an example of my /sms route
router.post('/sms', function(req, res, next){
var sid = req.body.SmsSid;
var from = req.body.From;
var to = req.body.To;
var date = Date();
var body = req.body.Body;
if(req.body.NumMedia > 0){
code to handle MMS
}
Code to handle SMS data
res.send("Completed");
});
I ran into this this week and discovered that behavior surrounding Basic Auth in the URL is very cloudy. For one thing, it appears to be deprecated from the URI spec as it pertains to HTTP:
...
3.2.1. User Information
...
Use of the format "user:password" in the userinfo field is deprecated.
...7.5. Sensitive Information
URI producers should not provide a URI that contains a username or password that is intended to be secret. URIs are frequently displayed by browsers, stored in clear text bookmarks, and logged by user agent history and intermediary applications (proxies). A password appearing within the userinfo component is deprecated and should be considered an error (or simply ignored) except in those rare cases where the 'password' parameter is intended to be public.
...
Because of this, both Firefox and Chrome appear to just strip it out and ignore it. Curl, however, seems to convert it to a valid Authorization header.
Whatever the case, I believe this functionality is actually the responsibility of the HTTP user agent, and it appears that Twilio's user agent is not doing its job. Thus, there is no way to make basic auth work.
However, it appears Twilio's preferred method of auth is to simply sign the request using your account's secret auth key, which you can then verify when handling the request. See here.
On researching the raw NodeJS Request and IncomingMessage classes, there appears to be no way to get at the full, raw URL to compensate for Twilio's non-conformity. I believe this is because the actual data of an HTTP request doesn't contain the full URL.
My understanding is that it's actually the HTTP user agent that's responsible for extracting and formatting the auth info from the URL. That is, a conformant HTTP user agent should parse the URL itself, using the hostname and port portion to find the right door on the right machine, the protocol portion to establish the connection with the listener, the verb combined with the URL's path portion to indicate what functionality to activate, and presumably it is then responsible for converting the auth section of the URL to an official HTTP Authorization header.
Absent that work by the user agent, there is no way to get the auth data into your system.
(This is my current understanding, although it may not be totally accurate. Others, feel free to comment or correct.)

Resources