0

The question: Does Google Actions always work by firing the webhook post on every user input and I just have to work the logic so as to parse the json everytime and react only if my parameter of interest is found? Or is there a way to control the webhook post made by Google Actions API in order to only have it made when the action is completely filled by containing a value for the parameter of interest?

#

The details: I've got this nodejs app deployed on Heroku: https://github.com/quique123/myjsonparser/blob/master/app.js

Im using it as a webhook to a Google Actions API (Number Genie) example. In Number Genie the user starts the game with "Talk to number genie". The Genie responds with Im thinking of a number ... guess it. The user responds with a number and then (and only then) is the logic is run to compare the guess with the answer.

But Im having the api call made on every post request from google home. In other words, the webhook post is made every time the user interacts with the conversation api. It can be seen here where the body contains no user-input-parameter and Heroku responds to "talk to number genie":

2017-04-30T18:59:19.480900+00:00 heroku[router]: at=info method=POST path="/API/switches/sw1?password=123456" host=myjsonparser.herokuapp.com request_id=64d51b1c-3253-4a64-b7f5-a29a7750945b fwd="54.224.155.160" dyno=web.1 connect=1ms service=35ms status=200 bytes=254 protocol=https
2017-04-30T18:59:19.473973+00:00 app[web.1]: headers: {"host":"myjsonparser.herokuapp.com","connection":"close","accept":"*/*","content-type":"application/json; charset=UTF-8","cache-control":"no-cache","pragma":"no-cache","user-agent":"Java/1.8.0_112","x-request-id":"64d51b1c-3253-4a64-b7f5-a29a7750945b","x-forwarded-for":"54.224.155.160","x-forwarded-proto":"https","x-forwarded-port":"443","via":"1.1 vegur","connect-time":"1","x-request-start":"1493578759443","total-route-time":"0","content-length":"575"}
2017-04-30T18:59:19.474002+00:00 app[web.1]: body: {"id":"14797289-b0b8-492a-b030-bf9f05c7ea17","timestamp":"2017-04-30T18:59:19.413Z","lang":"en","result":{"source":"agent","resolvedQuery":"talk to number genie","speech":"","action":"generate_answer","actionIncomplete":false,"parameters":{},"contexts":[],"metadata":{"intentId":"688b0da5-547e-4c7a-8adc-189844834bcc","webhookUsed":"true","webhookForSlotFillingUsed":"false","intentName":"start_game"},"fulfillment":{"speech":"","messages":[{"type":0,"speech":""}]},"score":0.61},"status":{"code":200,"errorType":"success"},"sessionId":"ff2e0c97-552b-40d3-8f06-32e612476897"}
2017-04-30T18:59:19.477116+00:00 app[web.1]: postSwitch {"id":"sw1","state":"off","name":"Koko's Lamp"}

And when I test it using a number on Google Actions API, you can see in Heroku response to "44" that check_guess is included:

2017-04-30T19:00:31.901297+00:00 app[web.1]: headers: {"host":"myjsonparser.herokuapp.com","connection":"close","accept":"*/*","content-type":"application/json; charset=UTF-8","cache-control":"no-cache","pragma":"no-cache","user-agent":"Java/1.8.0_112","x-request-id":"5a7a2c31-9ce5-4b02-9bac-bcef55ad6818","x-forwarded-for":"54.224.155.160","x-forwarded-proto":"https","x-forwarded-port":"443","via":"1.1 vegur","connect-time":"1","x-request-start":"1493578831899","total-route-time":"0","content-length":"573"}
2017-04-30T19:00:31.901347+00:00 app[web.1]: body: {"id":"5478dfb5-54f3-451d-b975-4f984d1ce3cb","timestamp":"2017-04-30T19:00:31.858Z","lang":"en","result":{"source":"agent","resolvedQuery":"44","speech":"","action":"check_guess","actionIncomplete":false,"parameters":{"check_guess":"44"},"contexts":[],"metadata":{"intentId":"c863e1e2-c850-45d8-9b96-b57e0b1ee77e","webhookUsed":"true","webhookForSlotFillingUsed":"false","intentName":"provide_guess"},"fulfillment":{"speech":"","messages":[{"type":0,"speech":""}]},"score":1},"status":{"code":200,"errorType":"success"},"sessionId":"ff2e0c97-552b-40d3-8f06-32e612476897"}
2017-04-30T19:00:31.903553+00:00 app[web.1]: postSwitch {"id":"sw1","state":"on","name":"Koko's Lamp"}
2017-04-30T19:00:31.907017+00:00 heroku[router]: at=info method=POST path="/API/switches/sw1?password=123456" host=myjsonparser.herokuapp.com request_id=5a7a2c31-9ce5-4b02-9bac-bcef55ad6818 fwd="54.224.155.160" dyno=web.1 connect=1ms service=5ms status=200 bytes=253 protocol=https

The problem is that the postSwitch{} happens in both ocassions.

Is this the way Google Actions will always work, and I just have to work the logic so as to parse the json everytime and react only if check_guess is found? Or is there a way to control the webhook post made by Google Actions API in order to only have it made when the action is completely filled by containing a value for the parameter check_guess?

Prisoner
  • 49,922
  • 7
  • 53
  • 105
marciokoko
  • 4,988
  • 8
  • 51
  • 91
  • Are you using API.AI? Can you also add screenshots of the intents that are firing this off? If you're not using API.AI, can you add the Action Package that you're using. – Prisoner Apr 30 '17 at 19:35
  • There are actually MANY errors on their article at this point. They updated the sample code without fully updating the tutorial. If you have other problems, however, they're best asked in additional SO questions. This one is best left at trying to handle your question about routing. – Prisoner May 01 '17 at 11:44
  • "Action Packages" are only for using the non-API.AI method of implementing Actions. Don't worry about it if you're using API.AI. – Prisoner May 01 '17 at 11:44

1 Answers1

3

You can only set one webhook (one static URL) that will be called for all actions that are triggered with API.AI. While you will need to parse the JSON (you're using node.js, JSON.parse() isn't THAT difficult), you should be using the result.action field instead of trying to figure out which parameters are set. This will correspond to the Action field that you have set in API.AI.

This assumes that you have the webhook box checked for your action, of course. If not, you won't get a webhook call at all.

So, for example, with the starting intent configured like this:

enter image description here

It will send this JSON to your webhook.


{
 "originalRequest": {
  "source": "google",
  "data": {
   "surface": {
    "capabilities": [
     {
      "name": "actions.capability.AUDIO_OUTPUT"
     }
    ]
   },
   "inputs": [
    {
     "arguments": [],
     "intent": "assistant.intent.action.MAIN",
     "raw_inputs": [
      {
       "query": "talk to number genie",
       "input_type": 2,
       "annotation_sets": []
      }
     ]
    }
   ],
   "user": {
    "user_id": "kQmX8nX9ovcS9jfb3WKmwLk9YFlHGZH05YGbc8muNI8=",
    "permissions": []
   },
   "device": {
    "locale": "en-US"
   },
   "is_in_sandbox": true,
   "conversation": {
    "conversation_id": "1493637016599",
    "type": 1
   }
  }
 },
 "id": "9444bfe4-3c23-487a-84e7-fcbf1708d9e3",
 "timestamp": "2017-05-01T11:10:16.694Z",
 "lang": "en",
 "result": {
  "source": "agent",
  "resolvedQuery": "GOOGLE_ASSISTANT_WELCOME",
  "speech": "",
  "action": "generate_answer",
  "actionIncomplete": false,
  "parameters": {},
  "contexts": [
   {
    "name": "game",
    "parameters": {},
    "lifespan": 5
   },
   {
    "name": "google_assistant_welcome",
    "parameters": {},
    "lifespan": 0
   },
   {
    "name": "actions_capability_audio_output",
    "parameters": {},
    "lifespan": 0
   }
  ],
  "metadata": {
   "intentId": "56da4637-0419-46b2-b851-d7bf726b1b1b",
   "webhookUsed": "true",
   "webhookForSlotFillingUsed": "false",
   "intentName": "start_game"
  },
  "fulfillment": {
   "speech": "",
   "messages": [
    {
     "type": 0,
     "speech": ""
    }
   ]
  },
  "score": 1
 },
 "status": {
  "code": 200,
  "errorType": "success"
 },
 "sessionId": "1493637016599"
}

While the provide_guess intent might be configured like this

provide_guess screen shot

and provide this JSON to the webhook:


{
 "originalRequest": {
  "source": "google",
  "data": {
   "surface": {
    "capabilities": [
     {
      "name": "actions.capability.AUDIO_OUTPUT"
     }
    ]
   },
   "inputs": [
    {
     "arguments": [
      {
       "raw_text": "42",
       "text_value": "42",
       "name": "text"
      }
     ],
     "intent": "assistant.intent.action.TEXT",
     "raw_inputs": [
      {
       "query": "42",
       "input_type": 2,
       "annotation_sets": []
      }
     ]
    }
   ],
   "user": {
    "user_id": "kQmX8nX9ovcS9jfb3WKmwLk9YFlHGZH05YGbc8muNI8=",
    "permissions": []
   },
   "device": {
    "locale": "en-US"
   },
   "is_in_sandbox": true,
   "conversation": {
    "conversation_token": "[\"_actions_on_google_\",\"game\"]",
    "conversation_id": "1493637749915",
    "type": 2
   }
  }
 },
 "id": "09997ef5-5c0f-4c60-a69f-af06d6e4e3f5",
 "timestamp": "2017-05-01T11:22:34.377Z",
 "lang": "en",
 "result": {
  "source": "agent",
  "resolvedQuery": "42",
  "speech": "",
  "action": "check_guess",
  "actionIncomplete": false,
  "parameters": {
   "guess": "42"
  },
  "contexts": [
   {
    "name": "game",
    "parameters": {
     "guess.original": "42",
     "guess": "42"
    },
    "lifespan": 5
   },
   {
    "name": "_actions_on_google_",
    "parameters": {
     "guessCount": 0,
     "printed": "Welcome back to Number Genie. I'm thinking of a number from %s to %s. What's your first guess?",
     "guess.original": "42",
     "answer": 74,
     "guess": "42",
     "lastPrompt": "Welcome back to Number Genie. I'm thinking of a number from %s to %s. What's your first guess?",
     "steamSoundCount": 0,
     "fallbackCount": 0
    },
    "lifespan": 99
   },
   {
    "name": "actions_capability_audio_output",
    "parameters": {
     "guess.original": "42",
     "guess": "42"
    },
    "lifespan": 0
   }
  ],
  "metadata": {
   "intentId": "1e46ffc2-651f-4ac0-a54e-9698feb88880",
   "webhookUsed": "true",
   "webhookForSlotFillingUsed": "false",
   "intentName": "provide_guess"
  },
  "fulfillment": {
   "speech": "",
   "messages": [
    {
     "type": 0,
     "speech": ""
    }
   ]
  },
  "score": 1
 },
 "status": {
  "code": 200,
  "errorType": "success"
 },
 "sessionId": "1493637749915"
}

In your code, you would check for the value of result.action in your postSwitch() method and can then select the exact operations on your end based on this action (either generateAnswer() or checkGuess() based on your commented out code).

Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • Oh ok, so result.action is used to check a particular intent result, which is why its called the intent's action in console.api.ai. Well fiddling with it last night, I got the value by doing assistant.getArgument('check_guess') and I was able to get it logged in Heroku with my latest code on github. – marciokoko May 01 '17 at 16:37
  • Is this the proper way to access those values? – marciokoko May 02 '17 at 00:30
  • Which values? result.action? – Prisoner May 02 '17 at 05:16
  • Im trying to access the user's input which in the number genie example would be the check_guess parameter, which also happens to be the name of the action in result.action. So it would be something like result.action.check_guess:65. But instead of parsing the json as a dictionary, I used the sample code `let guess = parseInt(assistant.getArgument('check_guess'));` found in Google Actions Number Genie tutorial – marciokoko May 02 '17 at 23:05
  • It sounds like you're asking a different question. A good question - but different than the one asked here. Please raise it in a new SO question so this one can be correctly preserved for others who have the same question. – Prisoner May 03 '17 at 02:40