Accessing correct fulfilment request with intent - node.js

I hope someone can help me with this.
I have two separate intents news and weather in my dialog flow agent.
I have different input parameters for both of them.
And I am using two separate API for each of them.
When I use both of them separately it works fine.
but when I try to combine both of them in one agent it does not show relevant output based on question. i.e if I ask question regarding weather it shows me news.
Is there any way I can fix it.right now it goes directly to the second request. Here is my code:
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {dialogflow} = require('actions-on-google');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const http = require('http');
const host = 'api.worldweatheronline.com';
const wwoApiKey = '0cb58ac2d82f484fa75185834191912';
const NewsAPI = require('newsapi');
const newsapi = new NewsAPI('63756dc5caca424fb3d0343406295021');
process.env.DEBUG = 'dialogflow:*';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) =>
{
var agent = new WebhookClient({ request, response });
// Get the city and date from the request
let city = request.body.queryResult.parameters['geo-city'];// city is a required param
// Get the date for the weather forecast (if present)
let date = 'date';
if (request.body.queryResult.parameters['date']) {
date = request.body.queryResult.parameters['date'];
console.log('Date: ' + date);
}
//Get the search criteria from the request
const search = request.body.queryResult.parameters['search'];
console.log(search);
//Map the correct intent
let intentMap = new Map();
intentMap.set('misty.weather',getweather );
intentMap.set('misty.news', getnews);
agent.handleRequest(intentMap);
});
//Weather reuest function
function getweather(agent,city,date)
{
callWeatherApi(city, date).then((output) => {
response.json({ 'fulfillmentText': output }); // Return the results of the weather API to Dialogflow
}).catch(() => {
response.json({ 'fulfillmentText': `I don't know the weather but I hope it's good!` });
});
}
// news request function
function getnews(search,agent)
{
callNewsApi(search).then((output) => {
console.log("Indide request");
response.json({ 'fulfillmentText': output }); // Return the results of the news API to Dialogflow
}).catch((error) => {
console.log(error);
response.json({ 'fulfillmentText': `I don't know the news but I hope it's good!` });
});
}
// API call for weather
function callWeatherApi (city, date) {
return new Promise((resolve, reject) => {
// Create the path for the HTTP request to get the weather
let path = '/premium/v1/weather.ashx?format=json&num_of_days=1' +
'&q=' + encodeURIComponent(city) + '&key=' + wwoApiKey + '&date=' + date;
console.log('API Request: ' + host + path);
// Make the HTTP request to get the weather
http.get({host: host, path: path}, (response) => {
let body = ''; // var to store the response chunks
response.on('data', (d) => { body += d; }); // store each response chunk
response.on('end', () => {
// After all the data has been received parse the JSON for desired data
let response = JSON.parse(body);
let forecast = response['data']['weather'][0];
let location = response['data']['request'][0];
let conditions = response['data']['current_condition'][0];
let currentConditions = conditions['weatherDesc'][0]['value'];
// Create response
let output = `Current conditions in the ${location['type']}
${location['query']} are ${currentConditions} with a projected high of
${forecast['maxtempC']}°C or ${forecast['maxtempF']}°F and a low of
${forecast['mintempC']}°C or ${forecast['mintempF']}°F on
${forecast['date']}.`;
// Resolve the promise with the output text
console.log(output);
resolve(output);
});
response.on('error', (error) => {
console.log(`Error calling the weather API: ${error}`);
reject();
});
});
});
}
//API call for news
function callNewsApi(search)
{
console.log("Inside api call");
console.log(search);
return newsapi.v2.topHeadlines
(
{
source:'CBC News',
q:search,
langauge: 'en',
country: 'ca',
}
).then (response => {
// var to store the response chunks
// store each response chunk
console.log(response);
var articles = response['articles'][0];
console.log(articles);
console.log("Inside responce call");
// Create response
var output = `Current news in the '${search}' with following title is ${articles['titile']} which says that ${articles['description']}`;
console.log(output);
return output;
});
}
It is giving me a reference error at line 51 and 42. I suspect I am missing a perameter to display the output.
function getnews(search,agent)
{
callNewsApi(search).then((output) => {
console.log("Indide request");
response.json({ 'fulfillmentText': output }); // Return the results of the news API to Dialogflow
}).catch((error) => {
console.log(error);
response.json({ 'fulfillmentText': `I don't know the news but I hope it's good!` });
});
}
it shows highlights near response.json and says that response not defined.

When I am reading your code, it looks like you're posting 2 Cloud Functions, while via the Dialogflow console, you can only set one webhook URL or Cloud Function. Therefore your approach should be that your webhook URL (pointing to a VM, Container etc..) or Cloud Function; contains code that behaves like a 'router'.
You will create the WebhookClient just once.
All parameters can be retrieved out of the queryResult object.
The intentMap could look something like this:
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('misty.weather', getWeather);
intentMap.set('misty.news', getNews);
function getNews() { .. }
function getWeather() {..}
I've an example Dialogflow / AOG project with a (Google Cloud, Cloud Function), you can have a look at:
https://github.com/savelee/dialogflow-aog-tvguide/
With this cloud function:
https://github.com/savelee/dialogflow-aog-tvguide/blob/master/cloudfunction/tvguide/index-old.js

Related

How to send a NODE.JS post request from an Angular Project?

I have a NODE.JS api using expressjs that connects to an SQL Server, and I want to use it in an angular project. I make use two files, a route file and a controllers file. My route file is as follows:
module.exports = (app) => {
const UsrContrllr = require('../Controllers/users.controllers');
//1. GET ALL USERS
app.get('/api/users', UsrContrllr.func1);
//2. POST NEW USER
app.post('/api/user/new', UsrContrllr.func2);
};
And my controllers file is given below:
const mssql = require('mssql');
exports.func1 = (req, res) =>
{
// Validate request
console.log(`Fetching RESPONSE`);
// create Request object
var request = new mssql.Request();
// query to the database and get the records
const queryStr = `SELECT * FROM USERS`;
request.query(queryStr, function (err, recordset) {
if (err) console.log(err)
else {
if (recordset.recordset.toString() === '') {
res.send('Oops!!! Required data not found...');
}
else {
// send records as a response
res.send(recordset);
}
};
});
};
exports.func2 = (req, res) =>
{
// Validate request
console.log(`INSERTING RECORD ${req}`);
// create Request object
var request = new mssql.Request();
// query to the database and get the records
const queryStr = `INSERT INTO GDUSERS (USERCODE, PASSWORD, LANGUAGE, USERCLASS, FIRSTNAME, LASTNAME, CONTACTNO) VALUES ('${req.body.usercode}', '${req.body.password}', 'EN', '0', '${req.body.firstname}', '${req.body.lastname}', '${req.body.contactno}');`;
request.query(queryStr, function (err, recordset) {
if (err) console.log(err)
else {
if (recordset.recordset.toString() == '') {
res.send('Oops!!! Required data not found...');
}
else {
// Send records as response
res.send(recordset);
}
};
});
};
The GET request works well, but when I try to run the POST request directly from the angular application, I get an error stating
Cannot GET URL/api/user/new
The angular code in my angular project is:
signup() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
console.log(this.user); //User details come from a form
this.http.post(“URL", this.user, options)
.subscribe(
(err) => {
if(err) console.log(err);
console.log("Success");
});
}
I’m not sure whether the angular code I’m using, is right or not, and I don’t know where I’m going wrong. How does one exactly send a http POST request from an Angular project?
this i the way i handled my user signup with http.post calls. my approach is slightly different when signing up user because i am using a promise instead of observable (which i normally use for my servicecalls). but i will show you both ways.
createUser(user: User): Promise < string > {
const promise = new Promise < string > ((resolve, reject) => {
const userForPost = this.createUserForPost(user);
this.http.post(environment.backendUrl + '/api/user/signup', userForPost, this.config).toPromise < HttpConfig > ()
.then(createdUser => {
}).catch(error => {
console.log(error);
});
});
return promise;
}
here another example with an observable
createForumPost(forumPost: ForumPost) {
this.http.post < { message: string, forumPostId: string } > (environment.backendUrl + '/api/forumPosts', forumPost).subscribe((responseData) => {
const id = responseData.forumPostId;
forumPost.id = id;
});
}
i defined my URL somewhere else and then just use the environment.backedUrl + 'path' to define my path (the same as the path in your backend controller)
this is one of my first answers here on SO. i am sry if it is a bit messy
i hope i was able to help with my examples :)

How to avoid pending promises when sending data from google api to discord.js?

I am working on a Discord bot which could extract datas from an Excel sheet. For example when I will ask the bot : "!adress paul", I would like the bot to look for "paul" value in the column A of a sheet and return the value of the column B (of the row where the value Paul is) in the discord app.
So far, I succeeded to read data from the Excel sheet and report the datas in the console.log (when I call the console.log inside the function). However each time I call the value out of the function I have a 'Promise {}' that appears... I made many many research on the web, without finding the solution.
const discord = require('discord.js');
const {google} = require('googleapis');
const keys = require('./identifiants.json');
// Creation of the Spreadsheet client
const client1 = new google.auth.JWT(keys.client_email, null,
keys.private_key, ['https://www.googleapis.com/auth/spreadsheets']);
client1.authorize(function(err, tokens)
{
if(err)
{
console.log(err);
return;
}
else
{
console.log('Connected to Google Sheets');
}
});
// Creation of the Discrod client
const client2 = new discord.Client();
client2.on('ready', () =>
{
console.log('Connected to Discord');
});
// Create an event listener for messages
client2.on('message', message =>
{
if (message.content === '!address')
{
console.log("OK 1");
let paul = address(client1);
console.log(paul);
console.log("OK2");
}
});
async function address(client)
{
const gsapi = google.sheets({version:'v4',auth: client });
const opt1 = {spreadsheetId: 'ID OF MY SPREADSHEET', range: 'Sheet1!A1:B3'};
let data = await gsapi.spreadsheets.values.get(opt1);
return data.data.values;
}
client2.login("MY CLIENT LOGIN")
In the console the message "Promise {pending}" appears between "OK1" and "OK2". Can someone help me on that case please? I would be very grateful
Your address function is an async function so it will return a promise.
Therefore, you will need to use then to get the value:
client2.on('message', message => {
if (message.content === '!address') {
address(client1)
.then(paul => {
console.log(paul);
});
}
});
Alternatively, you could use async-await:
client2.on('message', async (message) => {
if (message.content === '!address') {
let paul = await address(client1);
console.log(paul);
}
});

Accessing Firestore via Cloud Function

So i have 2 Cloud Functions within the same file:
exports.Auth = functions.region('europe-west1').https.onRequest((req, res) =>
and
exports.IPN = functions.region('europe-west1').https.onRequest((req, res) =>
When adding the following code right at the start of my Auth function it adds a new document to the Firestore as expected, however, when i add the same code at the start of my IPN function, which is currently being called via Paypal's IPN Simulator, it does nothing, no errors.
let pin = RandomPIN(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
var userRef = db.collection('Users').doc(pin);
var setWithOptions = userRef.set({ Activated: false }, { merge: true });
console.log("PIN: "+pin);
What on earth is going on, i must be missing something?
Thanks in advance.
Update:
Here are the logs, first with the 2 middle lines commented and then uncommented It seems to be silently failing, i'm just not sure what is causing it.
Update with Complete function:
exports.IPN = functions.region('europe-west1').https.onRequest((req, res) =>
{
console.log("IPN Notification Event Received");
let pin = RandomPIN(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
var userRef = db.collection('Users').doc(pin);
var setWithOptions = userRef.set({ Activated: false }, { merge: true });
console.log("PIN: "+pin);
if (req.method !== "POST")
{
console.error("Request method not allowed.");
res.status(405).send("Method Not Allowed");
}
else
{
console.log("IPN Notification Event received successfully.");
res.status(200).end();
}
let ipnTransactionMessage = req.body;
// Convert JSON ipn data to a query string since Google Cloud Function does not expose raw request data.
let formUrlEncodedBody = querystring.stringify(ipnTransactionMessage);
// Build the body of the verification post message by prefixing 'cmd=_notify-validate'.
let verificationBody = `cmd=_notify-validate&${formUrlEncodedBody}`;
console.log(`Verifying IPN: ${verificationBody}`);
let options = {
method: "POST",
uri: getPaypalURI(),
body: verificationBody,
};
// POST verification IPN data to paypal to validate.
request(options, function callback(error, response, body)
{
if(!error && response.statusCode === 200)
{
if(body === "VERIFIED")
{
console.log(`Verified IPN: IPN message for Transaction ID: ${ipnTransactionMessage.txn_id} is verified.`);
SendPIN(ipnTransactionMessage.payer_email, pin);
}
else if(body === "INVALID")
console.error(`Invalid IPN: IPN message for Transaction ID: ${ipnTransactionMessage.txn_id} is invalid.`);
else
console.error("Unexpected reponse body.");
}
else
{
console.error(error);
console.log(body);
}
});
});
Indeed it is a problem of Promises chaining and also a problem due to the request library: request supports callback interfaces natively but does not return a promise, which is what you must do within a Cloud Function.
I would suggest that you watch these official Firebase videos from Doug : https://www.youtube.com/watch?v=7IkUgCLr5oA&t=28s and https://www.youtube.com/watch?v=652XeeKNHSk which explain this key concept.
You can use request-promise (https://github.com/request/request-promise) and the rp() method which "returns a regular Promises/A+ compliant promise".
It is not clear what SendPIN() is doing. Let's make the assumption it returns a Promise. If this is true, you could adapt your code along the following lines:
//....
const rp = require('request-promise');
//....
exports.IPN = functions.region('europe-west1').https.onRequest((req, res) => {
console.log('IPN Notification Event Received');
let pin = RandomPIN(
10,
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
);
var userRef = db.collection('Users').doc(pin);
if (req.method !== 'POST') {
console.error('Request method not allowed.');
res.status(405).send('Method Not Allowed');
} else {
let ipnTransactionMessage;
userRef
.set({ Activated: false }, { merge: true })
.then(() => {
console.log('PIN: ' + pin);
ipnTransactionMessage = req.body;
// Convert JSON ipn data to a query string since Google Cloud Function does not expose raw request data.
let formUrlEncodedBody = querystring.stringify(ipnTransactionMessage);
// Build the body of the verification post message by prefixing 'cmd=_notify-validate'.
let verificationBody = `cmd=_notify-validate&${formUrlEncodedBody}`;
console.log(`Verifying IPN: ${verificationBody}`);
let options = {
method: 'POST',
uri: getPaypalURI(),
body: verificationBody
};
// POST verification IPN data to paypal to validate.
return rp(options);
})
.then(response => {
//Not sure what you will get within the response object...
console.log(
`Verified IPN: IPN message for Transaction ID: ${
ipnTransactionMessage.txn_id
} is verified.`
);
return SendPIN(ipnTransactionMessage.payer_email, pin); //It is not clear what SendPIN is doing, let's make the assumption it returns a Promise...
})
.then(() => {
res.send('Success');
return null;
})
.catch(err => {
console.error(
`Invalid IPN: IPN message for Transaction ID: ${
ipnTransactionMessage.txn_id
} is invalid.`
);
res
.status(500)
.send(
'Error: ' +
err +
` - Invalid IPN: IPN message for Transaction ID: ${
ipnTransactionMessage.txn_id
} is invalid.`
);
return null;
});
}
});

i am stuck on the "world weather online" webhook code implementation in the API.AI given on their doc page

I am following step by step the starter guide of API.AI docs from their website, now i am stuck on the "fulfillment" section.
Everything is working but when i try to retrieve request info from my Actions on Google ,weather-app, then its not giving me response from fulfillment that I deployed. It still answer the default response that i hardcoded in the Weather-app agent.
'use strict';
const http = require('http');
const host = 'api.worldweatheronline.com';
const wwoApiKey = '***i used key here from their website***';
exports.weatherWebhook = (req, res) => {
// Get the city and date from the request
let city = req.body.result.parameters['geo-city']; // city is a required param
// Get the date for the weather forecast (if present)
let date = '';
if (req.body.result.parameters['date']) {
date = req.body.result.parameters['date'];
console.log('Date: ' + date);
}
// Call the weather API
callWeatherApi(city, date).then((output) => {
// Return the results of the weather API to API.AI
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': output, 'displayText': output }));
}).catch((error) => {
// If there is an error let the user know
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': error, 'displayText': error }));
});
};
function callWeatherApi(city, date) {
return new Promise((resolve, reject) => {
// Create the path for the HTTP request to get the weather
let path = '/premium/v1/weather.ashx?format=json&num_of_days=1' +
'&q=' + encodeURIComponent(city) + '&key=' + wwoApiKey + '&date=' + date;
console.log('API Request: ' + host + path);
// Make the HTTP request to get the weather
http.get({ host: host, path: path }, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (d) => { body += d; }); // store each response chunk
res.on('end', () => {
// After all the data has been received parse the JSON for desired data
let response = JSON.parse(body);
let forecast = response['data']['weather'][0];
let location = response['data']['request'][0];
let conditions = response['data']['current_condition'][0];
let currentConditions = conditions['weatherDesc'][0]['value'];
// Create response
let output = `Current conditions in the ${location['type']}
${location['query']} are ${currentConditions} with a projected high of
${forecast['maxtempC']}°C or ${forecast['maxtempF']}°F and a low of
${forecast['mintempC']}°C or ${forecast['mintempF']}°F on
${forecast['date']}.`;
// Resolve the promise with the output text
console.log(output);
resolve(output);
});
res.on('error', (error) => {
reject(error);
});
});
});
}
I had the same issue as of Aug 2018, and the go-to answer always seemed to come back to inputting "api.worldweatheronline.com".
I was able to get mine working by typing in the address below. Make sure you input your two key correctly too.
api.worldweatheronline.com/premium/v1/weather.ashx?key=yourwwokey
This seems to do the trick, although I don't know why the syntax of the address has changed from one to the other.

Consuming an REST API with Node.JS for a AWS Lambda Function

I just starting to learn how to develop Amazon Alexa Skills using the Alexa Skills Kit and AWS Lambda. I don't quite understand how to call an API and retrieve data from it. I found this template code from a Jordan Leigh video:
var endpoint = ""; // ENDPOINT GOES HERE
var body = "";
https.get(endpoint, (response) => {
response.on('data', (chunk) => body += chunk);
response.on('end', () => {
var data = JSON.parse(body);
var subscriberCount = data.items[0].statistics.subscriberCount;
context.succeed(
generateResponse(
buildSpeechletResponse(`Current subscriber count is ${subscriberCount}`, true),
{}
)
);
});
});
I understand that the endpoint variable will hold the url for the API, but I am unsure about the rest. In this code I believe he using a YouTube API for the current subscriber count. If I wanted to, for example, use the Dark Sky API to extract weather info, how would I go about this using this similar format?
Pretty much the same.
const https = require('https');
var body = "";
const url = "https://api.darksky.net/forecast/your-secret-key/37.8267,-122.4233"
var req = https.request(url, (res) => {
res.on('data', (d) => {
body += d;
});
res.on('end', () => {
var data = JSON.parse(body);
console.log("daily weather: ", data.daily);
});
});
req.on('error', (e) => {
console.error(e);
});
req.end();

Resources