Alexa ask a question and get response from external API
Asked Answered
L

2

11

I have set up a simple intent

{
  "interactionModel": {
    "languageModel": {
      "invocationName": "viva bank",
      "intents": [
        ...builtin intents...{
          "name": "ask",
          "slots": [{
            "name": "question",
            "type": "AMAZON.SearchQuery"
          }],
          "samples": [
            "when {question}",
            "how to {question}",
            "what {question}"
          ]
        }
      ],
      "types": []
    }
  }
}

But when I ask a question it gives me a generic error response like this:

Me: alexa ask viva bank when is the late fee charged

Alexa: Sorry, I don't know that.

Here is my lambda code, but I don't think it is getting that far.

'use strict';

const Alexa = require('ask-sdk-core');
var https = require('https');
var querystring = require('querystring');


const APP_ID = 'amzn1.ask.skill.1234';

const AskIntentHandler = {
  canHandle(handlerInput) {
    return !!handlerInput.requestEnvelope.request.intent.slots['question'].value;
  },
  handle(handlerInput) {
    var question = handlerInput.requestEnvelope.request.intent.slots['question'].value;
    console.log('mydata:', question);
    var responseString = '';
    const subscription_key = 'XXXX';

    var data = {
      simplequery: question,
      channel: 'Alexa'
    };
    var get_options = {
      headers: {
        'Subscription-Key': subscription_key
      }
    };

    https.get('https://fakeapi.com/' + querystring.stringify(data), get_options, (res) => {
      console.log('statusCode:', res.statusCode);
      console.log('headers:', res.headers);

      res.on('data', (d) => {
        responseString += d;
      });

      res.on('end', function(res) {
        var json_hash = JSON.parse(responseString);
        // grab the first answer returned as text and have Alexa read it
        const speechOutput = json_hash['results'][0]['content']['text'];
        console.log('==> Answering: ', speechOutput);
        // speak the output
        return handlerInput.responseBuilder.speak(speechOutput).getResponse();
      });
    }).on('error', (e) => {
      console.error(e);
      return handlerInput.responseBuilder.speak("I'm sorry I ran into an error").getResponse();
    });
  }

};

exports.handler = (event, context) => {
  const alexa = Alexa.handler(event, context);
  alexa.APP_ID = APP_ID;
  alexa.registerHandlers(AskIntentHandler);
  alexa.execute();
};

I'm really just looking to create a very simple pass through, where a question is asked to Alexa, and then I pipe that to an external API and have Alexa read the response.

Ledoux answered 26/4, 2019 at 15:11 Comment(3)
Is the problem connected on how you call the api https.get('https://fakeapi.com/') or on how you run the rest of the code? In both scenario why did you include all your logic to the question instead of just posting a simplified example of each of those. Also references to any documentation you used will be useful. Also you don't explain well what is your problem. I would have to read all your source code to understand if Alexa does have those information and fails in returning them to you. Please improve your question accordingly. ThanksYawning
Did you noticed in cloudwatch whether your intentHandler function was executed or not ?? I think this kind of response is given because Alexa is unable to understand the question intent.Cano
Could you post the cloudwatch logs ?Tragedy
F
3

You can make the question slot value as required if it is mandatory for the intent and you need to include the intent name. You can use async/await to handle the API.

const Alexa = require('ask-sdk-core');
const https = require('https');
const querystring = require('querystring');
const { getSlotValue } = Alexa;

const APP_ID = 'amzn1.ask.skill.1234';

const AskIntentHandler = {
  canHandle(handlerInput) {
    return (
      handlerInput.requestEnvelope.request.type === "IntentRequest" &&
      handlerInput.requestEnvelope.request.intent.name === "AskIntent"
    );
  },
  async handle(handlerInput) {
    const question = getSlotValue(handlerInput.requestEnvelope, "question");
    console.log("question ", question);
    const data = await getAnswer(question);
    const speechText = data;
    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .getResponse();
  }
};

const getAnswer = question => {
  const subscription_key = "XXXX";
  let data = {
    simplequery: question,
    channel: "Alexa"
  };
  let get_options = {
    headers: {
      "Subscription-Key": subscription_key
    }
  };
  return new Promise((resolve, reject) => {
    https
      .get(
        "https://fakeapi.com/" + querystring.stringify(data),
        get_options,
        res => {
          console.log("statusCode:", res.statusCode);
          console.log("headers:", res.headers);
          res.on("data", d => {
            responseString += d;
          });

          res.on("end", function(res) {
            var json_hash = JSON.parse(responseString);
            // grab the first answer returned as text and have Alexa read it
            const speechOutput = json_hash["results"][0]["content"]["text"];
            console.log("==> Answering: ", speechOutput);
            resolve(speechOutput);
          });
        }
      )
      .on("error", e => {
        console.error(e);
        resolve("I'm sorry I ran into an error");
      });
  });
};
Featurelength answered 1/6, 2019 at 18:2 Comment(1)
This is the best answer so farWakeful
L
0

In AskIntentHandler you should set your "canHandle" to the name of the intent like so in addition to checking the question slot.

const AskIntentHandler = {
  canHandle (handlerInput) {
  return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
      handlerInput.requestEnvelope.request.intent.name === 'AskIntent' &&
      !!handlerInput.requestEnvelope.request.intent.slots['question'].value
  },
  handle (handlerInput) {
     // API Request Here
  }
}

Also if "question" is always required to run the intent, you can set up a dialog so Alexa will ask the user for "question" if she doesn't recognize one.

https://developer.amazon.com/docs/custom-skills/delegate-dialog-to-alexa.html

Likeminded answered 6/5, 2019 at 16:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.