Update the codebase to use ES6 and support Node v4.3 on Lambda (#13)
Fixes #6 This required a few changes to the code base. First of all, we need to update our lambda function to use Node v4.3. This can be done in the Lambda console on AWS, and is also done in the default.env file. Next, since Node 4.3 doesn't offer full ES6 support yet, we need to run our source code through babel to transform it to be ES5-friendly. The ES6 source now lives in the src/ directory, and the transformed js is output into a dist/ directory. The dist/ package is what ends up getting uploaded to lambda. One last issue is that node-lambda doesn't work with ES6 either, so we need to make sure it runs against the files in the dist/ directory instead. You can use the test-lambda npm script to do this automatically for you by executing npm run test-lambda in the terminal. This will trigger running babel to transform the js and then running node-lambda against the newly transformed files in dist/.
This commit is contained in:
parent
e09ee66d1c
commit
e31ac662a6
4
.babelrc
Normal file
4
.babelrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": ["transform-runtime"]
|
||||
}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ deploy.env
|
||||
lambda.zip
|
||||
node_modules/
|
||||
.env
|
||||
dist/
|
||||
|
@ -23,8 +23,8 @@ should point to your Couch Potato server, and `CP_API_KEY` which should have you
|
||||
You can use [node-lambda](https://github.com/motdotla/node-lambda) to test this skill locally. In
|
||||
the `test_events` directory are several event files you can use for testing, and they should map
|
||||
pretty well to each Intent. To test an intent, simply copy the contents of one of the json files in
|
||||
that directory and overwrite the contents of `event.json`. Then run `node-lambda run` from the
|
||||
command line.
|
||||
that directory and overwrite the contents of `event.json`. Make sure you run `npm install` from the
|
||||
command line to get the the latest npm packages, and then run `npm run test-lambda`.
|
||||
|
||||
## Setting up the Skill
|
||||
|
||||
|
@ -7,6 +7,6 @@ AWS_MODE=event
|
||||
AWS_MEMORY_SIZE=128
|
||||
AWS_TIMEOUT=10
|
||||
AWS_DESCRIPTION=
|
||||
AWS_RUNTIME=nodejs
|
||||
AWS_RUNTIME=nodejs4.3
|
||||
CP_URL=http://url-to-couch-potato-server
|
||||
CP_API_KEY=APIKEY
|
||||
|
@ -26,6 +26,9 @@
|
||||
},
|
||||
{
|
||||
"intent": "AMAZON.CancelIntent"
|
||||
},
|
||||
{
|
||||
"intent": "AMAZON.HelpIntent"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
132
lib/handlers.js
132
lib/handlers.js
@ -1,132 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var CouchPotato = require('node-couchpotato');
|
||||
var utils = require('./utils.js');
|
||||
|
||||
var WELCOME_DESCRIPTION = 'This skill allows you to manage your Couch Potato movie list.';
|
||||
var HELP_RESPONSE = ['You can ask Couch Potato about the movies in your queue or add new movies',
|
||||
'to it. Try asking "is The Godfather on the list?". If it\'s not and you want to add it, try',
|
||||
'saying "add The Godfather".'].join(' ');
|
||||
var CANCEL_RESPONSE = 'Exiting Couch Potato';
|
||||
|
||||
var config = require('dotenv').config();
|
||||
var cp = new CouchPotato({
|
||||
url: config.CP_URL,
|
||||
apikey: config.CP_API_KEY,
|
||||
debug:true
|
||||
});
|
||||
|
||||
function handleLaunchIntent(req, resp) {
|
||||
resp
|
||||
.say(WELCOME_DESCRIPTION)
|
||||
.say(HELP_RESPONSE)
|
||||
.send();
|
||||
}
|
||||
|
||||
function handleFindMovieIntent(req, resp) {
|
||||
var movieName = req.slot('movieName');
|
||||
|
||||
cp.movie.list({search: movieName, limit_offset: 5}).then(function (searchResp) {
|
||||
var movies = searchResp.movies;
|
||||
var result;
|
||||
|
||||
if (!movies || !movies.length) {
|
||||
resp.say('Couldn\'t find ' + movieName + ' queued for download. ');
|
||||
|
||||
cp.movie.search(movieName).then(function (searchResults) {
|
||||
utils.sendSearchResponse(searchResults, resp);
|
||||
});
|
||||
}
|
||||
else {
|
||||
result = movies[0].info;
|
||||
resp
|
||||
.say([
|
||||
'It looks like', result.original_title,
|
||||
'(' + result.year + ')', 'is already on your list.'
|
||||
].join(' '))
|
||||
.send();
|
||||
}
|
||||
});
|
||||
|
||||
//Async response
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleAddMovieIntent(req, resp) {
|
||||
var movieName = req.slot('movieName');
|
||||
|
||||
cp.movie.search(movieName,5).then(function (movies) {
|
||||
movies = utils.formatSearchResults(movies);
|
||||
utils.sendSearchResponse(movies, movieName, resp);
|
||||
});
|
||||
|
||||
//Async response
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleYesIntent(req, resp) {
|
||||
var promptData = req.session('promptData');
|
||||
var movie;
|
||||
|
||||
if (!promptData) {
|
||||
console.log('Got a AMAZON.YesIntent but no promptData. Ending session.');
|
||||
resp.send();
|
||||
}
|
||||
else if (promptData.yesAction === 'addMovie') {
|
||||
movie = promptData.searchResults[0];
|
||||
|
||||
cp.movie.add({
|
||||
title: movie.titles[0],
|
||||
identifier: movie.imdb
|
||||
}).then(function () {
|
||||
resp
|
||||
.say(promptData.yesResponse)
|
||||
.send();
|
||||
});
|
||||
|
||||
//Async response
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
console.log("Got an unexpected yesAction. PromptData:");
|
||||
console.log(promptData);
|
||||
resp.send();
|
||||
}
|
||||
}
|
||||
|
||||
function handleNoIntent(req, resp) {
|
||||
var promptData = req.session('promptData');
|
||||
|
||||
if (!promptData) {
|
||||
console.log('Got a AMAZON.YesIntent but no promptData. Ending session.');
|
||||
resp.send();
|
||||
}
|
||||
else if (promptData.noAction === 'endSession') {
|
||||
resp.say(promptData.noResponse).send();
|
||||
}
|
||||
else if (promptData.noAction === 'suggestNextMovie') {
|
||||
var movies = promptData.searchResults;
|
||||
resp
|
||||
.say(promptData.noResponse)
|
||||
.session('promptData', utils.buildPrompt(movies.slice(1)))
|
||||
.shouldEndSession(false)
|
||||
.send();
|
||||
}
|
||||
else {
|
||||
console.log("Got an unexpected noAction. PromptData:");
|
||||
console.log(promptData);
|
||||
resp.send();
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancelIntent(req, resp) {
|
||||
resp.say(CANCEL_RESPONSE).shouldEndSession(true).send();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleFindMovieIntent: handleFindMovieIntent,
|
||||
handleAddMovieIntent: handleAddMovieIntent,
|
||||
handleYesIntent: handleYesIntent,
|
||||
handleNoIntent: handleNoIntent,
|
||||
handleCancelIntent: handleCancelIntent
|
||||
};
|
15
lib/index.js
15
lib/index.js
@ -1,15 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var handlers = require('./handlers.js');
|
||||
|
||||
var alexa = require('alexa-app');
|
||||
var app = new alexa.app('couchPotato');
|
||||
|
||||
app.launch(handlers.handleLaunchIntent);
|
||||
app.intent('FindMovie', handlers.handleFindMovieIntent);
|
||||
app.intent('AddMovie', handlers.handleAddMovieIntent);
|
||||
app.intent('AMAZON.YesIntent', handlers.handleYesIntent);
|
||||
app.intent('AMAZON.NoIntent', handlers.handleNoIntent);
|
||||
app.intent('AMAZON.CancelIntent', handlers.handleCancelIntent);
|
||||
|
||||
module.exports = app;
|
15
package.json
15
package.json
@ -1,10 +1,13 @@
|
||||
{
|
||||
"name": "alexa-couchpotato",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.4",
|
||||
"description": "A skill to ask Alexa about your Couch Potato queue.",
|
||||
"main": "index.js",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"bundle": "mkdir -p bundle && cp -r {.env,index.js,lib,node_modules} bundle/ && cd bundle && bestzip ../lambda.zip * .env && rm -rf ../bundle"
|
||||
"build": "babel src -d dist",
|
||||
"zip": "cp -r {.env,deploy.env,package.json} dist/; cd dist; npm install --production; bestzip ../lambda.zip * .env;",
|
||||
"bundle": "npm run build; npm run zip;",
|
||||
"test-lambda": "npm run build; node-lambda run --handler dist/index.handler"
|
||||
},
|
||||
"author": "Joe Schmitt",
|
||||
"license": "MIT",
|
||||
@ -14,7 +17,11 @@
|
||||
"node-couchpotato": "^0.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.7.5",
|
||||
"babel-core": "^6.7.6",
|
||||
"babel-plugin-transform-runtime": "^6.7.5",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"bestzip": "^1.1.3",
|
||||
"node-lambda": "^0.7.1"
|
||||
"node-lambda": "^0.8.11"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var app = require('./lib');
|
||||
import app from './lib';
|
||||
|
||||
exports.handler = app.lambda();
|
128
src/lib/handlers.js
Normal file
128
src/lib/handlers.js
Normal file
@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
|
||||
import CouchPotato from 'node-couchpotato';
|
||||
|
||||
import {
|
||||
buildPrompt,
|
||||
sendSearchResponse,
|
||||
formatSearchResults
|
||||
} from './utils.js';
|
||||
|
||||
import {
|
||||
WELCOME_DESCRIPTION,
|
||||
HELP_RESPONSE,
|
||||
CANCEL_RESPONSE
|
||||
} from './responses.js';
|
||||
|
||||
const config = require('dotenv').config();
|
||||
const cp = new CouchPotato({
|
||||
url: config.CP_URL,
|
||||
apikey: config.CP_API_KEY,
|
||||
debug:true
|
||||
});
|
||||
|
||||
export default function handleLaunchIntent(req, resp) {
|
||||
resp
|
||||
.say(WELCOME_DESCRIPTION)
|
||||
.say(HELP_RESPONSE)
|
||||
.send();
|
||||
}
|
||||
|
||||
export function handleFindMovieIntent(req, resp) {
|
||||
const movieName = req.slot('movieName');
|
||||
|
||||
cp.movie.list({search: movieName, limit_offset: 5}).then(function (searchResp) {
|
||||
const movies = searchResp.movies;
|
||||
|
||||
if (!movies || !movies.length) {
|
||||
resp.say(`Couldn't find ${movieName} queued for download. `);
|
||||
|
||||
cp.movie.search(movieName).then(function (searchResults) {
|
||||
sendSearchResponse(searchResults, resp);
|
||||
});
|
||||
}
|
||||
else {
|
||||
const result = movies[0].info;
|
||||
resp
|
||||
.say(`It looks like ${result.original_title} (${result.year}) is already on your list.`)
|
||||
.send();
|
||||
}
|
||||
});
|
||||
|
||||
//Async response
|
||||
return false;
|
||||
}
|
||||
|
||||
export function handleAddMovieIntent(req, resp) {
|
||||
const movieName = req.slot('movieName');
|
||||
|
||||
cp.movie.search(movieName, 5).then(function (movies) {
|
||||
movies = formatSearchResults(movies);
|
||||
sendSearchResponse(movies, movieName, resp);
|
||||
});
|
||||
|
||||
//Async response
|
||||
return false;
|
||||
}
|
||||
|
||||
export function handleYesIntent(req, resp) {
|
||||
const promptData = req.session('promptData');
|
||||
|
||||
if (!promptData) {
|
||||
console.log('Got a AMAZON.YesIntent but no promptData. Ending session.');
|
||||
resp.send();
|
||||
}
|
||||
else if (promptData.yesAction === 'addMovie') {
|
||||
const movie = promptData.searchResults[0];
|
||||
|
||||
cp.movie.add({
|
||||
title: movie.titles[0],
|
||||
identifier: movie.imdb
|
||||
}).then(function () {
|
||||
resp
|
||||
.say(promptData.yesResponse)
|
||||
.send();
|
||||
});
|
||||
|
||||
//Async response
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
console.log('Got an unexpected yesAction. PromptData:');
|
||||
console.log(promptData);
|
||||
resp.send();
|
||||
}
|
||||
}
|
||||
|
||||
export function handleNoIntent(req, resp) {
|
||||
const promptData = req.session('promptData');
|
||||
|
||||
if (!promptData) {
|
||||
console.log('Got a AMAZON.NoIntent but no promptData. Ending session.');
|
||||
resp.send();
|
||||
}
|
||||
else if (promptData.noAction === 'endSession') {
|
||||
resp.say(promptData.noResponse).send();
|
||||
}
|
||||
else if (promptData.noAction === 'suggestNextMovie') {
|
||||
const movies = promptData.searchResults;
|
||||
resp
|
||||
.say(promptData.noResponse)
|
||||
.session('promptData', buildPrompt(movies.slice(1)))
|
||||
.shouldEndSession(false)
|
||||
.send();
|
||||
}
|
||||
else {
|
||||
console.log('Got an unexpected noAction. PromptData:');
|
||||
console.log(promptData);
|
||||
resp.send();
|
||||
}
|
||||
}
|
||||
|
||||
export function handleCancelIntent(req, resp) {
|
||||
resp.say(CANCEL_RESPONSE).shouldEndSession(true).send();
|
||||
}
|
||||
|
||||
export function handleHelpIntent(req, resp) {
|
||||
resp.say(HELP_RESPONSE).send();
|
||||
}
|
31
src/lib/index.js
Normal file
31
src/lib/index.js
Normal file
@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
import Alexa from 'alexa-app';
|
||||
|
||||
import handleLaunchIntent, {
|
||||
handleFindMovieIntent,
|
||||
handleAddMovieIntent,
|
||||
handleYesIntent,
|
||||
handleNoIntent,
|
||||
handleCancelIntent,
|
||||
handleHelpIntent
|
||||
} from './handlers.js';
|
||||
|
||||
const app = new Alexa.app('couchPotato');
|
||||
|
||||
app.launch(handleLaunchIntent);
|
||||
app.intent('FindMovie', handleFindMovieIntent);
|
||||
app.intent('AddMovie', handleAddMovieIntent);
|
||||
app.intent('AMAZON.YesIntent', handleYesIntent);
|
||||
app.intent('AMAZON.NoIntent', handleNoIntent);
|
||||
app.intent('AMAZON.CancelIntent', handleCancelIntent);
|
||||
app.intent('AMAZON.HelpIntent', handleHelpIntent);
|
||||
|
||||
app.post = function(request, response, type, exception) {
|
||||
if (exception) {
|
||||
// Always turn an exception into a successful response
|
||||
response.clear().say('An error occured: ' + exception).send();
|
||||
}
|
||||
};
|
||||
|
||||
export default app;
|
7
src/lib/responses.js
Normal file
7
src/lib/responses.js
Normal file
@ -0,0 +1,7 @@
|
||||
export const WELCOME_DESCRIPTION = 'This skill allows you to manage your Couch Potato movie list.';
|
||||
|
||||
export const HELP_RESPONSE = ['You can ask Couch Potato about the movies in your queue or add new movies',
|
||||
'to it. Try asking "is The Godfather on the list?". If it\'s not and you want to add it, try',
|
||||
'saying "add The Godfather".'].join(' ');
|
||||
|
||||
export const CANCEL_RESPONSE = 'Exiting Couch Potato';
|
@ -1,5 +1,5 @@
|
||||
function buildPrompt(movies) {
|
||||
var promptData = {
|
||||
export function buildPrompt(movies) {
|
||||
const promptData = {
|
||||
searchResults: movies,
|
||||
yesAction : 'addMovie',
|
||||
yesResponse: 'Added ' + movies[0].original_title + ' (' + movies[0].year + ')' + ' to your list of movies to download.'
|
||||
@ -17,9 +17,8 @@ function buildPrompt(movies) {
|
||||
return promptData;
|
||||
}
|
||||
|
||||
function sendSearchResponse(movies, movieName, resp) {
|
||||
|
||||
if(!movies || !movies.length > 0) {
|
||||
export function sendSearchResponse(movies, movieName, resp) {
|
||||
if (!movies || !movies.length > 0) {
|
||||
return resp.say('No movie found for ' + movieName).send();
|
||||
}
|
||||
|
||||
@ -30,29 +29,18 @@ function sendSearchResponse(movies, movieName, resp) {
|
||||
.send();
|
||||
}
|
||||
|
||||
function formatSearchResults(movies) {
|
||||
|
||||
var newMovies = [];
|
||||
|
||||
if (movies != undefined) {
|
||||
|
||||
for (var i = 0; i < movies.length; i++) {
|
||||
|
||||
newMovies.push({
|
||||
original_title: movies[i].original_title,
|
||||
in_library: movies[i].in_library,
|
||||
year: movies[i].year,
|
||||
titles: movies[i].titles,
|
||||
imdb: movies[i].imdb
|
||||
});
|
||||
export function formatSearchResults(movies) {
|
||||
if (movies) {
|
||||
return movies.map((movie) => {
|
||||
return {
|
||||
original_title: movie.original_title,
|
||||
in_library: movie.in_library,
|
||||
year: movie.year,
|
||||
titles: movie.titles,
|
||||
imdb: movie.imdb
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return newMovies;
|
||||
return [];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildPrompt: buildPrompt,
|
||||
sendSearchResponse: sendSearchResponse,
|
||||
formatSearchResults: formatSearchResults
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user