So I just started creating api agents for the Google Assistant. I followed everything in the api.ai documentation at https://api.ai/docs/getting-started/basic-fulfillment-conversation so the agent below gets the weather and returns a response. This is a working code but I wanted to get creative with the response. What I aim to do is get the country from the response and decide whether to display the celsius or fahrenheit equivalent of the returned temperature. But upon testing it in the console, it keeps using celsius. I also want to return friendly reminders based on the temperature which I'll add later on.
'use strict';
const http = require('http');
const host = 'api.worldweatheronline.com';
const wwoApiKey = '[api key]';
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 minTemp = forecast['mintempC'];
let maxTemp = forecast['maxtempC'];
let unit = '°C';
let location = response['data']['request'][0];
//get country
let country = location['query'].split(",")[1];
let conditions = response['data']['current_condition'][0];
let currentConditions = conditions['weatherDesc'][0]['value'];
if(country == "Liberia" || country == "Myanmar" || country == "United States of America")
{
minTemp = forecast['mintempF'];
maxTemp = forecast['maxtempF'];
unit = '°F';
}
// Create response
let output =
`Current conditions in the ${location['type']}
${location['query']} are ${currentConditions} with a projected high of
${maxTemp} ${unit} and a low of
${minTemp}${unit} on
${forecast['date']} in ${country}`;
// Resolve the promise with the output text
console.log(output);
resolve(output);
});
res.on('error', (error) => {
reject(error);
});
});
});
}
Although you've tagged this with api.ai and actions-on-google, and your code appears to come from https://api.ai/docs/getting-started/basic-fulfillment-conversation#setup_weather_api, this isn't really a problem with the Actions API itself. It looks like it has to do with how you're handling results from the WorldWeatherOnline (WWO) API and what you're expecting the input provided by the Google Assistant to be.
You have a few problems, all of them focused around this line:
let country = location['query'].split(",")[1];
This line gets the value that you provided in the query. In it, you're assuming a number of things, such as:
The user specifies the country (WWO can take just a city, if it is distinguishing enough).
The country is specified as the second field (when talking about US cities, specifying the State is common, which would make the country the third field).
Google Assistant specifies them capitalizing the first name of the country (likely, but not guaranteed).
If the country is the US, it is specified with the exact string "United States of America" instead of something shorter (but still recognizable) like "US" or "United States".
Additionally there is the logic issue that the person asking for the information may want it in the units that are familiar to them, rather than the units that are traditional for that country. An American asking about the weather in Spain probably still wants Fahrenheit.
Regardless - a good way to debug this would be to actually print out the value of location['query']
to see what the value is for a variety of inputs and adjust accordingly.
I'm not sure there is a direct and good way to handle what you're trying to do. Some thoughts:
You could try looking for the countries as a case-insensitive substring anywhere in the query string, but that might get you some false positives. Still... it is probably your easiest solution.
The suggestion by @Shuyang is a good one - but language locale is not always the best way to do it, and you'll need to use Google Actions extensions to the data provided through API.AI.
You could also ask for the user's current location (again, if you're using Google Actions) and then use a Geolocator to figure out where they are. But that is really messy.
You could treat it as part of the conversation - extend the Intent phrases to let them specify "in Fahrenheit" or with other possible phrases and do the conversion. Or store some information in a context and if they pose a followup phrase such as "What is that in Fahrenheit" provide the updated, converted, info.
(If you're really slick, you'll store the userId for this person in a database of some sort, and the next time they visit you, you'd just provide it in the units of their choice.)
I think the first one is easiest, with the last one being the most conversationally natural, but it all depends on what the value of location['query']
can actually be.