Search code examples
javascriptnode.jschatbotwit.aifacebook-chatbot

Getting started with wit.ai and Node.js


I am trying to use wit.ai quickstart example. The example works with the hardcoded values but when I use the third party weather API and try to give the response to the user it doesn't work.

Working code:

 const actions = {
         send(request, response) {
           const {sessionId, context, entities} = request;
           const {text, quickreplies} = response;
           console.log('sending...', JSON.stringify(response));
         },
         getForecast({context, entities}) {
           var location = firstEntityValue(entities, 'location');
           if (location) {
             context.forecast = 'sunny in ' + location; // we should call a weather API here
             delete context.missingLocation;
       
           } else {
             context.missingLocation = true;
             delete context.forecast;
           }
           return context;
         },
       };

Now I wrote function getWeather({ context, entities }, location) to call the third party weather API and get the weather info for the user's given location.

Nonworking code:

var getWeather = function ({ context, entities }, location) {
  console.log('Entities: ',entities)
  var url = 'http://api.openweathermap.org/data/2.5/weather?q=' + location + '&appid=myAppID';
  request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log(typeof body) 
      var weatherObj = JSON.parse(body);
      console.log('weather api res: ', weatherObj.weather[0].description);
      context.forecast = weatherObj.weather[0].description + ' ' + location; // we should call a weather API here
      delete context.missingLocation;
    }
  })
}

const actions = {
  send(request, response) {
    const {sessionId, context, entities} = request;
    const {text, quickreplies} = response;
    console.log('sending...', JSON.stringify(response));
  },
  getForecast({context, entities}) {
    var location = firstEntityValue(entities, 'location');
    if (location) {
      //Call a function which calls the third party weather API and then handles the response message.
      getWeather({ context, entities }, location);
    } else {
      context.missingLocation = true;
      delete context.forecast;
    }
    return context;
  },
};

Also if I change getWeather() function slightly and move context.forecast = 'sunny in ' + location; delete context.missingLocation; outside the callback fuction of request.get() call it will again work, but at this point I don't have weather info from 3rd party api:

Working code:

var getWeather = function ({ context, entities }, location) {
  //Keeping context.forecast outside the request.get() callback function is useless as we yet to get the weather info from the API
  context.forecast = 'sunny in ' + location;
  delete context.missingLocation;
  console.log('Entities: ',entities)
  var url = 'http://api.openweathermap.org/data/2.5/weather?q=' + location + '&appid=myAppID';
  request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log(typeof body) 
      var weatherObj = JSON.parse(body);
      console.log('weather api res: ', weatherObj.weather[0].description);
    }
  })
}

So how to make context.forecast = apiRes + location; line work inside the callback of http call? What I am doing wrong here?

NOTE: Error response I get from wit.ai:

Error: Oops, I don't know what to do.

at F:\..\node-wit\lib\wit.js:87:15
at process._tickCallback (internal/process/next_tick.js:103:7)

I am using npm package request to make http calls inside Node.


Solution

  • Solved the issue by properly resolving the promise. Here is the complete code:

    'use strict';
    
    let Wit = null;
    let interactive = null;
    
    var getWeather = function ( location) {
      var url = 'http://api.openweathermap.org/data/2.5/weather?q=' + location + '&appid=myAppID';
      return fetch(url, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        }
      })
        .then(rsp => {
          var res = rsp.json();
          return res;
        })
        .then(json => {
          if (json.error && json.error.message) {
            throw new Error(json.error.message);
          }
          return json;
        });
    }
    
    try {
      // if running from repo
      Wit = require('../').Wit;
      interactive = require('../').interactive;
    } catch (e) {
      Wit = require('node-wit').Wit;
      interactive = require('node-wit').interactive;
    }
    
    const accessToken = (() => {
      if (process.argv.length !== 3) {
        console.log('usage: node examples/quickstart.js <wit-access-token>');
        process.exit(1);
      }
      return process.argv[2];
    })();
    
    // Quickstart example
    // See https://wit.ai/ar7hur/quickstart
    
    const firstEntityValue = (entities, entity) => {
      const val = entities && entities[entity] &&
        Array.isArray(entities[entity]) &&
        entities[entity].length > 0 &&
        entities[entity][0].value;
      if (!val) {
        return null;
      }
      return typeof val === 'object' ? val.value : val;
    };
    
    const actions = {
      send(request, response) {
        const {sessionId, context, entities} = request;
        const {text, quickreplies} = response;
        return new Promise(function (resolve, reject) {
          console.log('sending...', JSON.stringify(response));
          return resolve();
        });
      },
      getForecast({context, entities}) {
        var location = firstEntityValue(entities, 'location');
        if (location) {
          return new Promise(function (resolve, reject) {
            return getWeather(location).then(weatherJson => {
              context.forecast = weatherJson.weather[0].description + ' in the ' + location;
              delete context.missingLocation;
              return resolve(context);
            })
          });
        } else {
          context.missingLocation = true;
          delete context.forecast;
          return Promise.reject(context);
        }
        return context;
      },
    };
    
    const client = new Wit({ accessToken, actions });
    interactive(client);