Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: No response has been set. Cloud Functions for Actions on Google Assistant

I am building an Assistant app for Google Home, using Dialogflow, Cloud Functions and the new NodeJS Client Library V2 for Actions on Google. In fact I am in the process of migrating my old code built with V1 to V2.

The Context

I am trying to get the user's location using two seperate intents: Request Permission (Intent that triggers/send permission request to the user) and User Info (Intent that checks if the user granted permission and then returns the data requested by the assistant to continue.

The Issue

The problem is that the same code that was working just fine on V1 isn't working on V2. So I had to do some refactoring. And when I deploy my cloud function I am able to successfully request the user's permission, get his location and then using an external library (geocode), I can convert the latlong to a human readable form. but for some reasons (I think its promises) I can't resolve the promise object and show it to the user

The Error

I get the error below:

enter image description here

The Code

Below is my Cloud function code. I have tried multiple versions of this code, using the request library, https library, etc. No luck...No luck

    const {dialogflow, Suggestions,SimpleResponse,Permission} = require('actions-on-google')  
    const functions = require('firebase-functions'); 
    const geocoder = require('geocoder');

    const app = dialogflow({ debug: true });

    app.middleware((conv) => {
        conv.hasScreen =
            conv.surface.capabilities.has('actions.capability.SCREEN_OUTPUT');
        conv.hasAudioPlayback =
            conv.surface.capabilities.has('actions.capability.AUDIO_OUTPUT');
    });

    function requestPermission(conv) {
        conv.ask(new Permission({
            context: 'To know who and where you are',
            permissions: ['NAME','DEVICE_PRECISE_LOCATION']
        }));
    }

    function userInfo ( conv, params, granted) {

        if (!conv.arguments.get('PERMISSION')) {

            // Note: Currently, precise locaton only returns lat/lng coordinates on phones and lat/lng coordinates 
            // and a geocoded address on voice-activated speakers. 
            // Coarse location only works on voice-activated speakers.
            conv.ask(new SimpleResponse({
                speech:'Sorry, I could not find you',
                text: 'Sorry, I could not find you'
            }))
            conv.ask(new Suggestions(['Locate Me', 'Back to Menu',' Quit']))
        }

        if (conv.arguments.get('PERMISSION')) {

            const permission = conv.arguments.get('PERMISSION'); // also retrievable with explicit arguments.get
            console.log('User: ' + conv.user)
            console.log('PERMISSION: ' + permission)
            const location = conv.device.location.coordinates
            console.log('Location ' + JSON.stringify(location))

            // Reverse Geocoding
            geocoder.reverseGeocode(location.latitude,location.longitude,(err,data) => {
                if (err) {
                    console.log(err)
                }


                // console.log('geocoded: ' + JSON.stringify(data))
                console.log('geocoded: ' + JSON.stringify(data.results[0].formatted_address))
                conv.ask(new SimpleResponse({
                    speech:'You currently at ' + data.results[0].formatted_address + '. What would you like to do now?',
                    text: 'You currently at ' + data.results[0].formatted_address + '.'
                }))
                conv.ask(new Suggestions(['Back to Menu', 'Learn More', 'Quit']))

            })

        }

    }


    app.intent('Request Permission', requestPermission);
    app.intent('User Info', userInfo);

    exports.myCloudFunction = functions.https.onRequest(app);

Any help is very much appreciated. Thanks

like image 220
AllJs Avatar asked Apr 18 '18 21:04

AllJs


2 Answers

You're right on your last guess - your problem is that you're not using Promises.

app.intent() expects the handler function (userInfo in your case) to return a Promise if it is using async calls. (If you're not, you can get away with returning nothing.)

The normal course of action is to use something that returns a Promise. However, this is tricky in your case since the geocode library hasn't been updated to use Promises, and you have other code that in the userInfo function that doesn't return anything.

A rewrite in this case might look something like this (I haven't tested the code, however). In it, I break up the two conditions in userInfo into two other functions so one can return a Promise.

function userInfoNotFound( conv, params, granted ){
  // Note: Currently, precise locaton only returns lat/lng coordinates on phones and lat/lng coordinates 
  // and a geocoded address on voice-activated speakers. 
  // Coarse location only works on voice-activated speakers.
  conv.ask(new SimpleResponse({
    speech:'Sorry, I could not find you',
    text: 'Sorry, I could not find you'
  }))
  conv.ask(new Suggestions(['Locate Me', 'Back to Menu',' Quit']))
}

function userInfoFound( conv, params, granted ){
  const permission = conv.arguments.get('PERMISSION'); // also retrievable with explicit arguments.get
  console.log('User: ' + conv.user)
  console.log('PERMISSION: ' + permission)
  const location = conv.device.location.coordinates
  console.log('Location ' + JSON.stringify(location))

  return new Promise( function( resolve, reject ){
    // Reverse Geocoding
    geocoder.reverseGeocode(location.latitude,location.longitude,(err,data) => {
      if (err) {
        console.log(err)
        reject( err );
      } else {
        // console.log('geocoded: ' + JSON.stringify(data))
        console.log('geocoded: ' + JSON.stringify(data.results[0].formatted_address))
        conv.ask(new SimpleResponse({
          speech:'You currently at ' + data.results[0].formatted_address + '. What would you like to do now?',
          text: 'You currently at ' + data.results[0].formatted_address + '.'
        }))
        conv.ask(new Suggestions(['Back to Menu', 'Learn More', 'Quit']))
        resolve()
      }
    })
  });

}

function userInfo ( conv, params, granted) {
  if (conv.arguments.get('PERMISSION')) {
    return userInfoFound( conv, params, granted );
  } else {
    return userInfoNotFound( conv, params, granted );
  }
}
like image 134
Prisoner Avatar answered Oct 31 '22 10:10

Prisoner


Thanks to @Prisoner, I was able to make it work. i didn't have to modify my Dialogflow structure or anything. All I had to do was change the Reverse Geocoding section to what @Prisoner suggested. And it worked for me.

//Reverse Geocoding

return new Promise( function( resolve, reject ){
    // Reverse Geocoding
    geocoder.reverseGeocode(location.latitude,location.longitude,(err,data) => {
      if (err) {
        console.log(err)
        reject( err );
      } else {
        // console.log('geocoded: ' + JSON.stringify(data))
        console.log('geocoded: ' + JSON.stringify(data.results[0].formatted_address))
        conv.ask(new SimpleResponse({
          speech:'You currently at ' + data.results[0].formatted_address + '. What would you like to do now?',
          text: 'You currently at ' + data.results[0].formatted_address + '.'
        }))
        conv.ask(new Suggestions(['Back to Menu', 'Learn More', 'Quit']))
        resolve()
      }
   })
});

I can now move on to other things!

like image 22
AllJs Avatar answered Oct 31 '22 10:10

AllJs