0

I am making app for google assistant using dialog flow. My app uses a webhook function deployed on firebase. Main question is how to get user location - I need to pass the coordinates to a URL. I am able to prompt the message for location permission, but how to confirm and get coordinates.

Do I need to add any intent for confirmation? I am asking for location permission in default welcome intent.enter image description hereenter image description hereenter image description hereenter image description hereenter image description here

The code is below:

'use strict';

process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').DialogflowApp;

const functions = require('firebase-functions');



//----global variables-----
const http = require('https');
var body = "";
var body2="";
var address="";
var latitude="";
var longitude="";
var lati="";
var long="";


//------ADDRESS_ACTION to tell the current address based on current coordinates----
const ADDRESS_ACTION = 'address_action';

//-----DIRECTION_INTENT to output the map guidence through basic card------
const DIRECTION_INTENT = 'direction_action';

//--------during welcome intent, ask the permiisions------
const DIR_PERMISS_CONF= 'intent.welcome';

exports.addressMaker = functions.https.onRequest((request, response) => {
  const app = new App({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));


  function welcomeIntentDirection (app) 
  {
    app.askForPermission("Hi! Welcome to !To show address/direction",app.SupportedPermissions.DEVICE_PRECISE_LOCATION);
    if (app.isPermissionGranted()) 
    {
     // app.tell('You said ' + app.getRawInput());
    }
    else
    {
     // app.tell('You said ' + app.getRawInput());
    }

  }



function makeDirection(app)
{

  //-------here we are extraction what user said, we will extract the place name, it is for "direction to PLACE_NAME"-------

  var stringRequest=JSON.stringify(request.body);
  var jsonRequest=JSON.parse(stringRequest);
  var jsonSpeech=jsonRequest.originalRequest.data.inputs[0].rawInputs[0].query;
  var arr=jsonSpeech.split("to");
  var placeName=arr[1].trim();

  //------Current Location--------------
  let deviceCoordinates = app.getDeviceLocation().coordinates;
  latitude=deviceCoordinates.latitude;
  longitude=deviceCoordinates.longitude;



//-----now send this place name to api to get coordinates-----
 var req2= http.get("https://api.url-with-PARAMETER-placeName",function(res){

  res.on("data", function(chunk2){ body2 += chunk2;  });
  res.on('end', function()
  {
    if (res.statusCode === 200) 
    {
      var data2 = JSON.parse(body2);

      try
      {
          lati=data2.geometry.lat;
          long=data2.geometry.lng;
      }catch(e){app.tell("Invalid or non-existent address");}    
    }
  });
});


//-------------------Here BUILDING the CARD (Rich RESPONSE)-----------------------
  app.ask(app.buildRichResponse()
    // Create a basic card and add it to the rich response
    .addSimpleResponse('Your directions are here:')
    .addBasicCard(app.buildBasicCard()

      .addButton('Start Guidence', 'https://www.google.com/maps/dir/?api=1&origin='+latitude+','+longitude+'&destination='+lati+','+long+'')
      .setImage('https://png.pngtree.com/element_origin_min_pic/16/11/30/a535f05d9d512610e25a036b50da036f.jpg', 'Image alternate text')
      .setImageDisplay('CROPPED')
  )
);

}


  function makeAddress (app) 
      {
        let deviceCoordinates = app.getDeviceLocation().coordinates;
        latitude=deviceCoordinates.latitude;
        longitude=deviceCoordinates.longitude;
        var req=http.get("https://api.url/reverse?coords="+latitude+","+longitude+"", 
        function(res)
        {
          res.on("data", function(chunk){ body += chunk;  });

          res.on('end', function()
          {
            if (res.statusCode === 200) 
            {
                try 
                {
                  var data = JSON.parse(body);
                  address=data.words;
                  app.tell('Alright, your address is '+address);
                } catch (e) { }
            }
            else 
            {
              app.tell("Unable to contact to server +"+res.statusCode);
            }

          });

        });
      }

  // d. build an action map, which maps intent names to functions
  let actionMap = new Map();
  actionMap.set(DIR_PERMISS_CONF,welcomeIntentDirection);
  actionMap.set(ADDRESS_ACTION, makeAddress);
  actionMap.set(DIRECTION_INTENT, makeDirection);



app.handleRequest(actionMap);
});
akash verma
  • 159
  • 1
  • 15

1 Answers1

1

Yes, you need an Intent to handle the response from the user. The model of Dialogflow is that all responses from the user need to be handled via an intent.

In this case, it would be your "confirmation" Intent.

You don't show a screen shot of the Intent (if you have further problems, please update the question to include it), but it needs to handle the Event actions_intent_PERMISSION. When handling this Event, you don't need any training phrases.

One that I have (with an Intent name and Action of result.location) looks like this:

enter image description here

As a side note, in your handler, you're trying to get the user name, but this name isn't available unless you've asked for permission for it. You didn't ask for permission. If you wanted to, you could ask for permission at the same time you asked for their location with something like

app.askForPermissions("To show your address", [
  app.SupportedPermissions.NAME,
  app.SupportedPermissions.DEVICE_PRECISE_LOCATION
]);

Update based on your screen shots.

It isn't clear what your conversation flow is meant to be, so I'm making a few assumptions.

But based on your code, you're asking for permission as a result of the Welcome intent. The user then gives permission, and you need an intent that accepts that reply.

I don't see an intent that accepts that reply.

In order to accept the reply, you need an Intent that handles the Event actions_intent_PERMISSION, as I illustrated in my screen shot above.

You also seem to be creating Contexts, although it isn't clear why.

Update based on the code you posted and your additional comments.

There are several issues with the code which may be causing some of the problems you're experiencing, and some other issues which may cause you problems in the future. To answer some of your questions in the comments and some unasked questions:

I have enabled Small Talk

This isn't hurting anything - but it probably isn't helping anything either. I would suggest you disable it until you get the core part of your conversation working.

I can get the coordinates

Good - so that Intent is working.

But...

I see that you're storing the coordinates in a global variable. That works in this case (but not quite, see the next question of yours), but won't scale if more than one person is using the action since the values could be mixed up in between user sessions.

In other words: If you use global variables in your webhook, every person has access to these values. This is VERY BAD.

What you want is a way to store user information in between calls to your webhook. There are a number of ways to do this that are covered elsewhere and can be rather involved. In general, however, you can store the values in either a context or in app.data and they will be returned to you as part of the same conversation.

I don't get a destination address when I ask the first time and When I ask a second time, it has the destination address

Your function makeDirection() contains an asynchronous operation to get the lat/lon of the destination address, however you're sending back the reply outside of that asynchronous block.

So what happens is that you start the http call, and then the code continues and you send the reply to the user with the empty destination. At some later point, the callback completes and the destination is set, but too late for the first call. But when you call it the second time, it goes through the same routine (starts the async call, and then immediately sends something back before the call completes), but the values from the previous run are set because you had set them in global variables.

To fix this, you're going to need to call app.ask() as part of your async callback. This might look something like this:

http.get("https://api.url-with-PARAMETER-placeName",function(res){

  res.on("data", function(chunk2){ body2 += chunk2;  });
  res.on('end', function(){
    if (res.statusCode === 200){
      var data2 = JSON.parse(body2);

      try{
        lati=data2.geometry.lat;
        long=data2.geometry.lng;
        app.ask( 
          app.buildRichResponse()
            .addThingsToRichResponse()
        );
      } catch(e){
        app.tell("Invalid or non-existent address");
      }
    }
  });
});

Issues with getting what the user said about the destination

Your code where you're trying to get the user's destination is... strange. Getting the rawInputs is legal, but trying to break it apart to get specific values isn't best practice, especially since the user might not say exactly what you think they're going to say. For example, what if they said "I'm heading towards somewhere".

Better is to have your direction_action Intent take a variety of different phrases and assign the "important" part of that (the destination) to a parameter. Then, in your webhook, you can read just the value of the parameter and pass that to the service to determine the location.

You also may wish to take a look at the recently announced Place Helper, which is similar to the device precise location helper, but also gets an address that the user specifies.

Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • Sir, can you make a video tutorial of "Name Psychic" , I think my all doubts will get cleared then. Link at https://developers.google.com/actions/samples/ – akash verma Apr 03 '18 at 10:09
  • When I say "yes" for the location permission, In the request it got updated and I am able to get location , but google assistant replies with "Can you say that again", or "I dont understant". – akash verma Apr 03 '18 at 10:12
  • That suggests that you're triggering the Fallback Intent, which means it isn't matching any other Intent. Updating your question with a screen shot of what Intent you think should be handling it may help us help you further. – Prisoner Apr 03 '18 at 10:18
  • just a minute sir – akash verma Apr 03 '18 at 10:19
  • In both default welcome intent and tell _address intent, fulfillment via webhook is enabled – akash verma Apr 03 '18 at 10:35
  • I've updated my answer, although the code you have there currently doesn't match the screen shots, so it is difficult to figure out what is going on. However, it looks like you still haven't implemented an Intent to handle the Event where you get the information back. – Prisoner Apr 03 '18 at 13:59
  • if I create a separate Intent that handles the Event actions_intent_PERMISSION, as you illustrated in your screen shot above, I have one question ---"When this intent will be triggered", when I say "yes". Do I have to put training phrases for this intent – akash verma Apr 03 '18 at 14:05
  • I can send you my updated source code, if you want for more understanding.I can provide you my gmail id – akash verma Apr 03 '18 at 14:08
  • No, you do not need training phrases for this Event. I'll update the answer to make that more clear. – Prisoner Apr 03 '18 at 14:08
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/168128/discussion-between-prisoner-and-akash-verma). – Prisoner Apr 03 '18 at 14:09
  • sir i have updated the code. Some Point : 1. I have enabled small talk also, 2. I am able to get coordinates, 3. when I ask "direction to "Place name" , it gave me output as basic card which contains the current lat & long, but not the destination address. when I ask it second time, it contains the destination address. Every time the same thing happens. Also somehow it is storing the placeName when i ask "direction to placeName". when i again ask "direction to some other place", it shows direction to first one every time – akash verma Apr 03 '18 at 16:13
  • @ Prisoner : Sir, what You said about async call is absolutely correct. I found that this is actually happening. So how can I make it to wait until the response is received and then build the card response – akash verma Apr 05 '18 at 10:36
  • Also, I don't want to store anything in the device storage. But need to store location coordinates into local variables for that particular instance until the response has been made. – akash verma Apr 05 '18 at 10:39
  • 1
    Answer updated. But... (1) Didn't we talk about async functions in another question of yours? (2) If you store them in local variables, more than one user has access to them. This is a security hole and your Action *will not work*. See the other link I reference about how to store them in a session. If you have more questions about this - ask another SO question. – Prisoner Apr 05 '18 at 14:39
  • yes Sir, I know a little about parameters. But I know how to take them in webhook. Only problem is how to assign the PLACE_NAME to a parameter in INTENT – akash verma Apr 07 '18 at 07:11
  • I'm not sure what you're asking, but it sounds like a new question. If you can ask it as a new SO question, making it clear what you're trying to do and what isn't working, with screen shots of the relevant intents, we may be able to help. – Prisoner Apr 07 '18 at 10:50
  • https://stackoverflow.com/questions/49707178/how-to-extract-the-parameter-from-intent-in-order-to-pass-it-to-webhook – akash verma Apr 07 '18 at 12:11