alexa-js / alexa-app

A framework for Alexa (Amazon Echo) apps using Node.js
https://www.youtube.com/watch?v=pzM4jv7k7Rg
MIT License
1.03k stars 212 forks source link

HTTPS GET Help #285

Closed NovaGL closed 7 years ago

NovaGL commented 7 years ago

I need some help, Async gets me all confused sync I am a novice in Node-Red.

I just want Alexa to say something it receives from a HTTPS Call. I am using (https://github.com/alexa/alexa-cookbook/blob/master/external-calls/httpsGet/src/index.js) as an example but I don't know how to adapt it to use Alexa app. Any help would be great.

Not sure if this is right

alexaApp.intent('Test', {},            
  function(req, res) {   
    httpsGet(myRequest,  (myResult) => {
                console.log("sent     : " + myRequest);
                console.log("received : " + myResult);
                this.res.say('The population of ' + myRequest + ' is ' + myResult);
                this.emit(':responseReady');
            }
        );
    });

The console log shows the correct info. But it goes wrong from there.

lazerwalker commented 7 years ago

If you're trying to handle an intent asynchronously, you'll need the code that fires off your network request to use promises rather than a callback function, and have the intent function return that promise. Without that, the library won't know to wait for your asynchronous call to return before sending a response to Amazon.

Without knowing where your httpsGet function is coming from or if it's something you can modify, a hypothetical Promise-based version might maybe look something like this:

alexaApp.intent('Test', {},            
  function(req, res) {   
    return httpsGet(myRequest).then((myResult) => {
      console.log("sent     : " + myRequest);
      console.log("received : " + myResult);
      res.say('The population of ' + myRequest + ' is ' + myResult);
     });
  });
});

(You'll notice I also removed the this from res.say. I also removed the this.emit call — that's something that's part of the official Alexa JS SDK, but doesn't exist / isn't necessary in the alexa-app library)

Hope that's helpful!

NovaGL commented 7 years ago

Thanks for that @lazerwalker. I think you have too many closing brackets.

EDIT: So I think I have done it now. Can you tell me if I need to correct anything in my code I'm a real newbie with Node.JS but I like to give it a go.

alexaApp.intent('test', {},            
  function(req, res) {   
    return httpsRequest(myRequest).then((myResult) => {
       console.log("sent     : " + myRequest);
       console.log("received : " + myResult);
       res.say('The population of ' + myRequest + ' is ' + myResult);
     });
  });
function httpsRequest(myData, postData) {
  var params = {
        host: 'cp6gckjt97.execute-api.us-east-1.amazonaws.com',
        port: 443,
        path: '/prod/stateresource?usstate=' + encodeURIComponent(myData),
        method: 'GET',

        // if x509 certs are required:
        // key: fs.readFileSync('certs/my-key.pem'),
        // cert: fs.readFileSync('certs/my-cert.pem')
    };
    return new Promise(function(resolve, reject) {
        var req = https.request(params, function(res) {
            // reject on bad status
            if (res.statusCode < 200 || res.statusCode >= 300) {
                return reject(new Error('statusCode=' + res.statusCode));
            }
            // cumulate data
            var body = [];
            res.on('data', function(chunk) {
                body.push(chunk);
            });
            // resolve on end
            res.on('end', function() {
                try {                    
                    body = JSON.parse(body).population;           
                } catch(e) {
                    reject(e);
                }
                resolve(body);
            });
        });
        // reject on request error
        req.on('error', function(err) {
            // This is not a "Second reject", just a different sort of failure
            reject(err);
        });
        if (postData) {
            req.write(postData);
        }
        // IMPORTANT
        req.end();
    });
}
lazerwalker commented 7 years ago

Yeah, that seems pretty reasonable!

If that's giving you trouble, you might want to investigate pre-existing node libraries that provide a Promise-based API to make HTTP requests, such as https://github.com/request/request-promise (you'll find tons more at npmjs.com, I haven't used that one specifically).

You may also want to check out the async section of the readme for this project: https://github.com/alexa-js/alexa-app#asynchronous-handlers-example. The README in general is likely to be more useful to you than Amazon's alexa-cookbook examples, as alexa-app operates fairly differently from the official Alexa JS SDK.

NovaGL commented 7 years ago

Thanks. Don't really understand your async examples but doesn't really matter.

I am trying to make my own Alexa Sonos skill which could be adapted to control any music system that provides metadata.

This particular function is used for getting the now playing info. I will also adapt it to get the favorites of a player, but that's the next step.

It will never be released as it's for private use but hopefully I can give the source out so people can do their own stuff if it ever gets to that level.