Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTP request with Alexa and Lambda on SDK v2, how to make it works?

I am playing with ASK SDK v2 provided by Amazon in order to make Skill for Alexa but I face an architectural problem :

First of all, the HTTP request works like a charm but I would like to return speach response if and only if my HTTP request is complete but I don't even know if it's possible because of the "handle" function that should return something (look at comments) :

const MyIntentHandler = {

  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'LaunchRequest' || (request.type === 'IntentRequest' && request.intent.name === 'MyIntent');
  },
  handle(handlerInput) {

    var options = {
      host: 'http://foo.com',
      port: 80,
      path: '/mypath',
      method: 'GET'
    };

    var req = http.request(options, function(result){
      result.on("end", function(){
        //I would like to return speak here like that :
        //return handlerInput.responseBuilder.speak("It works").withSimpleCard("MyTestApp", "It works").getResponse()
      })
    });
    req.end();
    //And I would like to remove this line to manage response in result.on("end", function(){}) above
    return handlerInput.responseBuilder.speak("It works").withSimpleCard("MyTestApp", "It works").getResponse();
  },
};

Any idea to deal with this ?

like image 316
Ashbay Avatar asked May 01 '18 13:05

Ashbay


People also ask

Can Alexa send HTTP requests?

It abstracts the complexities of making requests behind a simple interface, and makes HTTP requests easy and simple. If you're using Alexa Hosted as the backend for your skills, there's an added advantage of the Requests library being built-in, so you don't need to install or upload it manually.

How is a lambda function linked to an Alexa skill?

You can use Lambda functions to build services that give new skills to Alexa, the Voice assistant on Amazon Echo. The Alexa Skills Kit provides the APIs, tools, and documentation to create these new skills, powered by your own services running as Lambda functions.

How do you fix there was a problem with the requested skill's response?

One simple way to debug this issue is copying the input JSON from Alexa skill simulator and paste it in the lambda's configure test events. Now run test and it'll generate all the error logs in the lambda itself, for your easy reference.


2 Answers

I found the official way to make it :

1) Create a new funtion that manage http request and return a promise :

function httpGet(options) {
  return new Promise(((resolve, reject) => {

    const request = http.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      if (response.statusCode < 200 || response.statusCode >= 300) {
        return reject(new Error(`${response.statusCode}: ${response.req.getHeader('host')} ${response.req.path}`));
      }

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    
    request.on('error', function (error) {
      reject(error);
    });
    
    request.end();
  }));
}

2) In the Intent, return a promise in which you call your httpGet function :

const MyIntentHandler = {

  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'LaunchRequest' || (request.type === 'IntentRequest' && request.intent.name === 'MyIntent');
  },
  handle(handlerInput) {

    var options = {
      host: 'http://foo.com',
      port: 80,
      path: '/mypath',
      method: 'GET'
    };

    return new Promise((resolve, reject) => {
     httpGet(options).then((response) => {
       resolve(handlerInput.responseBuilder.speak("It is done.").getResponse());
     }).catch((error) => {
        resolve(handlerInput.responseBuilder.speak('Thor is not available at the moment. Please try again later or contact your administrator.')
        .getResponse());
      });
    });
  },
};

This is the way to do it properly. My example is base on alexa petmatch sample.

like image 86
Ashbay Avatar answered Oct 03 '22 16:10

Ashbay


Also we can use request module to call API as follows

const SearchIntentHandler = {
  canHandle(handlerInput) {
    return (
      handlerInput.requestEnvelope.request.type === "IntentRequest" &&
      handlerInput.requestEnvelope.request.intent.name === "SearchIntent"
    );
  },
  handle(handlerInput) {
    const query = handlerInput.requestEnvelope.request.intent.slots.SearchQuery.value;
    return new Promise((resolve, reject) => {
      getSearchResults(query).then((response) => {
        resolve(handlerInput.responseBuilder.speak(response).getResponse());
      }).catch((error) => {
        resolve(handlerInput.responseBuilder.speak('This is not available at the moment.').getResponse());
      });
    });
  }
};


function getSearchResults(query){
  return new Promise((resolve, reject)=>{
    let options = { 
      method: 'POST',
      url: 'http://url.com',
      headers: {'Cache-Control': 'no-cache','Content-Type': 'application/x-www-form-urlencoded' },
      form: { text: query } 
    };
    request(options, function (error, response, body) {
      if (error) throw new Error(error);
      let data = body ? JSON.parse(body) : ""
      return resolve(data);
    });
  });
}
like image 37
Sharad Avatar answered Oct 03 '22 18:10

Sharad