Search code examples
javascriptfacebookpromisebotssap-conversational-ai

How to add results from a promise based API call with message.addReply using Recast.ai?


I'm making a bot that searches restaurants based on location. Can anyone help me why this doesnt show up in FB messenger?:

restaurants(result.getMemory('location').raw)
.then(res=>{

  message.addReply(res);
  message.reply();

 });
}

The call to the restaurants function returns the results from a YELP API call (an array of restaurants) but when I add it as a reply to message, nothing happens in FB messenger.

Here is the full code for message.js:

    const recastai = require('recastai');

    const restaurants = require('./restaurants');

     // This function is the core of the bot behaviour
    const replyMessage = (message) => {
     // Instantiate Recast.AI SDK, just for request service
     const request = new recastai.request(process.env.REQUEST_TOKEN, 
    process.env.LANGUAGE);
   // Get text from message received
   const text = message.content;

    console.log('I receive: ', text);

  // Get senderId to catch unique conversation_token
  const senderId = message.senderId;

  // Call Recast.AI SDK, through /converse route
  request.converseText(text, { conversationToken: senderId })
  .then(result => {

    //Recast takes text analyses that, returns a result object, generates replies adds messages to reply stack and then sends the replies

    //Call Yelp API with when the intent is Location. When Yelp returns result we add it to the result.replies array. 
    //Then we add everything in result.replies to the messaging queue that sends the responses to FB


    if (result.action) {

      console.log('The conversation action is: ', result.action.slug);

    }

    // If there is not any message return by Recast.AI for this current conversation
    if (!result.replies.length) {
      message.addReply({ type: 'text', content: 'I don\'t have the reply to this yet :)' });
    } else {
      // Add each reply received from API to replies stack
      result.replies.forEach(replyContent => message.addReply({ type: 'text', content: replyContent }));
    }

    // Send all replies
    message.reply()
    //send initial reply generated by Recast first
    .then(() => {
    //call restaurant function that returns a list of results from API  
    //if the action is location and done
      if(result.action && result.action.slug === 'location' && result.action.done){

        restaurants(result.getMemory('location').raw)
          .then(res=>{

            console.log(res);

            message.addReply(res);
            message.reply();

          });

      }

    })
    .catch(err => {
      console.error('Error while sending message to channel', err);
    });
  })
  .catch(err => {
    console.error('Error while sending message to Recast.AI', err);
  });
};

module.exports = replyMessage;

And here is my restaurants.js code that is imported into the message.js file for the bot behavior:

const rp = require('request-promise');

// Load configuration
require('./config');

const restaurants = (location) => {
  return Promise.all([
    yelpCall(location)
  ]).then(result => {

    //result contains the return value from Yelp call

    return result;

  });
};

const yelpCall = (location) => {

  const auth = {
    method: 'POST',
    url: 'https://api.yelp.com/oauth2/token?grant_type=client_credentials&client_id='+ process.env.YELP_APP_ID +'&client_secret='+process.env.APP_SECRET
  };

  return rp(auth)
    .then(result => {
    const tokens = JSON.parse(result);
    return tokens;

  })
  .then(result=>{

    const options = {
      url: 'https://api.yelp.com/v3/businesses/search?location=' + location + "&term=thai",
      headers: {Authorization: "Bearer " + result.access_token}  
    };

    return rp(options).then(findings =>{

      return findings;

    });

  });

};

module.exports = restaurants;

Solution

  • A few thoughts :

    1. message.reply is thenable, therefore return message.reply() in two places.
    2. request.converseText() is thenable, therefore return request.converseText(...).
    3. restaurants is thenable, therefore return restaurants(...).
    4. in message.js, message.addReply() is passed object of the form {type:..., content:...} in two places but finally just res. Is that correct?
    5. in restaurants.js, Promise.all() appears to be unnecessary. It will cause its result to be wrapped in an array. module.exports = location => yelpCall(location); seems more appropriate.