From 2f59e12aa75fcbd6a1da1125549fb15af224bb0b Mon Sep 17 00:00:00 2001 From: GotPPay Date: Thu, 18 Jan 2018 19:50:13 +0100 Subject: [PATCH 1/6] test new library --- backend/controllers/index.js | 1 + backend/controllers/saburlyEntryPoint.js | 40 ++++++++++++++++++++++++ backend/package.json | 1 + backend/server.js | 6 ++-- 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 backend/controllers/saburlyEntryPoint.js diff --git a/backend/controllers/index.js b/backend/controllers/index.js index 93f7810..bdcb063 100644 --- a/backend/controllers/index.js +++ b/backend/controllers/index.js @@ -1,5 +1,6 @@ var express = require ('express'), router = express.Router (); router.use ('/skill', require ('./skill')); +router.use ('/saburly', require('./saburlyEntryPoint')); module.exports = router; diff --git a/backend/controllers/saburlyEntryPoint.js b/backend/controllers/saburlyEntryPoint.js new file mode 100644 index 0000000..8b3f90e --- /dev/null +++ b/backend/controllers/saburlyEntryPoint.js @@ -0,0 +1,40 @@ +var express = require ('express'), router = express.Router (); +const config = require('../config/config'); +var bodyParser = require ('body-parser'); +var Alexa = require('alexa-sdk'); + + +router.get('/', async (req, res) => { + console.log("GET request on /saburly"); + // Build the context manually, because Amazon Lambda is missing + var context = { + succeed: function (result) { + console.log(result); + res.json(result); + }, + fail:function (error) { + console.log(error); + } + }; + + const handlers = { + 'LaunchRequest': function () { + console.log("Launch request"); + this.emit(':tell', 'Welcome to Saburly'); + this.emit('HelloWorldIntent'); + }, + + 'HelloWorldIntent': function () { + console.log("Hello world intent"); + this.emit(':tell', 'Hello World!'); + } + }; + + // Delegate the request to the Alexa SDK and the declared intent-handlers + var alexa = Alexa.handler(req.body, context); + alexa.appId = config.SKILL_ID; + alexa.registerHandlers(handlers); + alexa.execute(); +}); + +module.exports = router; \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 74d58ac..421c735 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,6 +5,7 @@ "main": "test.js", "dependencies": { "alexa-app": "4.2.0", + "alexa-sdk": "^1.0.25", "body-parser": "^1.13.1", "ejs": "^2.5.7", "express": "^4.13.0", diff --git a/backend/server.js b/backend/server.js index 859389c..32e38e1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4,7 +4,7 @@ const config = require ('./config/config'); const constants = require ('./config/constants'); require ('isomorphic-fetch'); var express = require ('express'); -var alexa = require('./components/alexa'); +//var alexa = require('./components/alexa'); var MongoClient = require ('mongodb').MongoClient; @@ -14,7 +14,7 @@ const router = express.Router (); var app = express (); // ALWAYS setup the alexa app and attach it to express before anything else. -alexa.init (app); +//alexa.init (app); app.set ('view engine', 'ejs'); app.use (require ('./middleware')); //common middleware for all requests @@ -26,7 +26,7 @@ MongoClient.connect (config.DB_URL) app.listen (config.PORT, () => { console.log ('Express server running on port ' + config.PORT); - alexa.updateIntentsJSON (); + //alexa.updateIntentsJSON (); databaseHelper.loadTokens (); }); }) -- 2.47.3 From 68287d49fff8e474f7cb869eafcedab3fb4b5069 Mon Sep 17 00:00:00 2001 From: GotPPay Date: Thu, 18 Jan 2018 21:33:44 +0100 Subject: [PATCH 2/6] complete switch to new alexa package --- backend/components/alexa.js | 204 ----------------------- backend/controllers/saburlyEntryPoint.js | 39 +---- backend/controllers/skill.js | 6 +- backend/helpers/amazon.js | 2 + backend/models/alexa.js | 50 ++++++ backend/package.json | 1 - backend/server.js | 13 +- 7 files changed, 62 insertions(+), 253 deletions(-) delete mode 100644 backend/components/alexa.js create mode 100644 backend/models/alexa.js diff --git a/backend/components/alexa.js b/backend/components/alexa.js deleted file mode 100644 index 2fc124d..0000000 --- a/backend/components/alexa.js +++ /dev/null @@ -1,204 +0,0 @@ -var alexa = require ('alexa-app'); -const config = require ('../config/config'); -var databaseHelper = require ('../helpers/database'); - -//User data for sending message, this is skill-related and will be in some skill container -var Name = null; -var Email = null; -var Message = null; -var State = 0; // states should be defined in seperate file. (Not sending message, Waiting for name, Waiting for email, Waiting for message) -//For now (this is not long term solution) -// 0 : Not sending Message -// 1 : Waiting for name -// 2 : Waiting for email -// 3 : Waiting for message - -var alexaApp = new alexa.app ('saburly'); // this means we still work with one skill - -module.exports = { - init: function (express) { - alexaApp.express ({ - expressApp: express, - - // verifies requests come from amazon alexa. Must be enabled for production. - // You can disable this if you're running a dev environment and want to POST - // things to test behavior. enabled by default. - checkCert: false, - - // sets up a GET route when set to true. This is handy for testing in - // development, but not recommended for production. disabled by default - debug: true, - }); - }, - - updateIntentsJSON: function () { - databaseHelper - .getSkill (config.SKILL_DB_ID) - .then (skill => { - skill.intents.map (intent => { - alexaApp.intent ( - intent.intentName, - { - slots: [], - utterances: intent.questions, - }, - function (request, response) { - return response.say (intent.answer).shouldEndSession (false); - } - ); - }); - - alexaApp.launch ((request, response) => { - return response.say (skill.invocationAnswer).shouldEndSession (false); - }); - - alexaApp.intent ( - 'EmailIntentLaunch', - { - slots: [], - utterances: [ - 'I want to send a message', - 'I would like to send a message', - 'I would like to leave a message', - 'Leave a message', - ], - }, - function (request, response) { - Name = null; - Email = null; - Message = null; - State = 1; - - return response - .say ('Ok. What is your name') - .shouldEndSession (false); - } - ); - - //TODO : Watch out for this intent. It will make trouble with other regular intents - //if other intents have utterance with just one slot like {Data} - //It should be taken care somwhere before this, to check if Email Intent is invoked - //This is problem only if we introduce slot options for regular intents for users - alexaApp.intent ( - 'EmailIntent', - { - slots: { - Name: 'AMAZON.US_FIRST_NAME', - Email: 'AMAZON.LITERAL', - Message: 'AMAZON.LITERAL', - Data: 'AMAZON.LITERAL', - }, - utterances: [ - 'My name is {-|Name}', - 'I am {-|Name}', - '{dawdw at dwd wdw|Data}', - 'My email is {wadwwdw at wadwwd wdw|Email}', - 'Send replay to {fkofkeofe at dppfam wd|Email}', - 'My message is {Quick brown fox jumps over lazy dog|Message}', - ], - }, - function (request, response) { - let Data = undefined; - try { - if (!Name) Name = request.slot ('Name'); - } catch (e) { - console.log ('Error. No name slot '); - Name = undefined; - } - try { - if (!Email) Email = request.slot ('Email'); - } catch (e) { - console.log ('Error. No Email slot'); - Email = undefined; - } - try { - if (!Message) Message = request.slot ('Message'); - } catch (e) { - console.log ('Error. No Message slot'); - Message = undefined; - } - try { - Data = request.slot ('Data'); - } catch (e) { - console.log ('Error. No Data slot'); - Data = undefined; - } - console.log ('State : ' + State); - - //TODO : Responses could be configurable for each skill ? - - if (State === 1) { - //Was waiting for name, so if Name is null, name is probably in Data - if ((!Name && Data) || Name) { - //got the name, let's continue for the email - if (!Name) Name = Data; - State = 2; - return response - .say ('Ok ' + Name + '. What is your email ?') - .shouldEndSession (false); - } else { - //Something is wrong, ask for name again - return response - .say ( - 'Sorry, I didnt understand your name. Can you say it again ?' - ) - .shouldEndSession (false); - } - } else if (State === 2) { - //was waiting for email, so if Email is null, email is probably in Data - if ((!Email && Data) || Email) { - //Got the email, first verify email and than continue to message - if (!Email) Email = Data; - //TODO : verify email - State = 3; - return response - .say ('Great. Whats the message ?') - .shouldEndSession (false); - } else { - //Something is wrong, ask for the email again - return response - .say ( - 'Sorry, I didnt understan you email. Can you say it again ?' - ) - .shouldEndSession (false); - } - } else if (State === 3) { - //Was waiting for message, so if Message is null, message is probably in Data - if ((!Message && Data) || Message) { - //Ok, we got all informations. Exit email intent - if (!Message) Message = Data; - State = 0; - //TODO : Send email - console.log ( - 'Name : ' + - Name + - ' | Email : ' + - Email + - ' | Message : ' + - Message - ); - return response.say ( - 'Message sent. Someone will contact you ASAP' - ); - } else { - //Something is wrong, ask for the message again - return response - .say ( - 'Sorry, I didnt understand your message. Can you say it again ?' - ) - .shouldEndSession (false); - } - } else { - console.log ('State strange ! ' + State); - } - } - ); - }) - .catch (err => { - console.log (err); - alexaApp.launch ((request, response) => { - return response.say ('Sorry, there was no skill with that name'); - }); - }); - }, -}; diff --git a/backend/controllers/saburlyEntryPoint.js b/backend/controllers/saburlyEntryPoint.js index 8b3f90e..e698d84 100644 --- a/backend/controllers/saburlyEntryPoint.js +++ b/backend/controllers/saburlyEntryPoint.js @@ -1,40 +1,9 @@ var express = require ('express'), router = express.Router (); -const config = require('../config/config'); var bodyParser = require ('body-parser'); -var Alexa = require('alexa-sdk'); +var alexa = require ('../models/alexa'); - -router.get('/', async (req, res) => { - console.log("GET request on /saburly"); - // Build the context manually, because Amazon Lambda is missing - var context = { - succeed: function (result) { - console.log(result); - res.json(result); - }, - fail:function (error) { - console.log(error); - } - }; - - const handlers = { - 'LaunchRequest': function () { - console.log("Launch request"); - this.emit(':tell', 'Welcome to Saburly'); - this.emit('HelloWorldIntent'); - }, - - 'HelloWorldIntent': function () { - console.log("Hello world intent"); - this.emit(':tell', 'Hello World!'); - } - }; - - // Delegate the request to the Alexa SDK and the declared intent-handlers - var alexa = Alexa.handler(req.body, context); - alexa.appId = config.SKILL_ID; - alexa.registerHandlers(handlers); - alexa.execute(); +router.post ('/', bodyParser.json (), async (req, res) => { + alexa.run (req, res); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/controllers/skill.js b/backend/controllers/skill.js index 1d8cd44..6cc36e7 100644 --- a/backend/controllers/skill.js +++ b/backend/controllers/skill.js @@ -3,7 +3,7 @@ const constants = require ('../config/constants'); var databaseHelper = require ('../helpers/database'); var amazonHelper = require ('../helpers/amazon'); var bodyParser = require ('body-parser'); -var alexa = require ('../components/alexa'); +var alexa = require ('../models/alexa'); router.get ('/:id', async (req, res, next) => { const id = req.params.id; @@ -49,7 +49,7 @@ router.put ('/:id', bodyParser.json (), async (req, res, next) => { amazonResult === constants.amazonResultCodes.ACCEPTED ) { res.json ({result: constants.apiResultCodes.OK, message: ''}); - alexa.updateIntentsJSON (); + alexa.updateModel (); } else { res.status(constants.HTTPResultCodes.INTERNAL_SERVER_ERROR).json ({ result: constants.apiResultCodes.AMAZON_ERROR, @@ -65,7 +65,7 @@ router.put ('/:id', bodyParser.json (), async (req, res, next) => { }); }else{ res.json ({result: constants.apiResultCodes.OK, message: ''}); - alexa.updateIntentsJSON (); + alexa.updateModel (); } }) .catch (() => { diff --git a/backend/helpers/amazon.js b/backend/helpers/amazon.js index 1fd9410..d6c554b 100644 --- a/backend/helpers/amazon.js +++ b/backend/helpers/amazon.js @@ -82,6 +82,7 @@ var generateInteractionModel = function (skill) { }); //Special Email Intents : + /* allIntents.push ({ name: 'EmailIntentLaunch', slots: [], @@ -123,6 +124,7 @@ var generateInteractionModel = function (skill) { 'My message is {The quick brown fox jumps over the lazy dog.The quick brown fox jumps over the lazy dog.The quick brown fox jumps over the lazy dog.|Message}', ], }); + */ result.interactionModel = {}; diff --git a/backend/models/alexa.js b/backend/models/alexa.js new file mode 100644 index 0000000..a4b0471 --- /dev/null +++ b/backend/models/alexa.js @@ -0,0 +1,50 @@ +var Alexa = require ('alexa-sdk'); +const config = require ('../config/config'); +var databaseHelper = require ('../helpers/database'); + +// Build the context manually, because Amazon Lambda is missing +var context = { + succeed: function (result) { + console.log (result); + res.json (result); + }, + fail: function (error) { + console.log (error); + //We could send error json from here + }, +}; + +var handlers = {}; + +module.exports = { + run: function (req, res) { + var alexa = Alexa.handler (req.body, context); + alexa.appId = config.SKILL_ID; + alexa.registerHandlers (handlers); + alexa.execute (); + }, + updateModel: function () { + //Get info from database, and store it for faster response on intent + databaseHelper + .getSkill (config.SKILL_DB_ID) + .then (activeSkill => { + handlers = {}; + handlers = { + LaunchRequest: function () { + this.response.speak (activeSkill.invocationAnswer); + this.emit (':responseReady'); + }, + }; + activeSkill.intents.map (intent => { + handlers[intent.intentName] = function () { + this.response.speak (intent.answer); + this.emit (':responseReady'); + }; + }); + }) + .catch (e => { + //Something is wrong, skill is not ready, use catch-all intent to inform user + console.log ('Error. Skill doesnt exist'); + }); + }, +}; diff --git a/backend/package.json b/backend/package.json index 421c735..b0c0319 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,6 @@ "description": "", "main": "test.js", "dependencies": { - "alexa-app": "4.2.0", "alexa-sdk": "^1.0.25", "body-parser": "^1.13.1", "ejs": "^2.5.7", diff --git a/backend/server.js b/backend/server.js index 32e38e1..f39f9b9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,11 +1,7 @@ -var amazonHelper = require ('./helpers/amazon'); var databaseHelper = require ('./helpers/database'); const config = require ('./config/config'); -const constants = require ('./config/constants'); -require ('isomorphic-fetch'); var express = require ('express'); -//var alexa = require('./components/alexa'); - +var alexa = require ('./models/alexa'); var MongoClient = require ('mongodb').MongoClient; var ObjectID = require ('mongodb').ObjectID; @@ -13,10 +9,7 @@ var ObjectID = require ('mongodb').ObjectID; const router = express.Router (); var app = express (); -// ALWAYS setup the alexa app and attach it to express before anything else. -//alexa.init (app); - -app.set ('view engine', 'ejs'); +app.set ('view engine', 'ejs'); // Should be removed app.use (require ('./middleware')); //common middleware for all requests app.use (require ('./controllers')); //all routes @@ -26,7 +19,7 @@ MongoClient.connect (config.DB_URL) app.listen (config.PORT, () => { console.log ('Express server running on port ' + config.PORT); - //alexa.updateIntentsJSON (); + alexa.updateModel (); databaseHelper.loadTokens (); }); }) -- 2.47.3 From 48badce0f085e59799315c2944fe1d70a4add687 Mon Sep 17 00:00:00 2001 From: GotPPay Date: Thu, 18 Jan 2018 21:43:25 +0100 Subject: [PATCH 3/6] fix 'res not defined' --- backend/models/alexa.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/models/alexa.js b/backend/models/alexa.js index a4b0471..ebfce94 100644 --- a/backend/models/alexa.js +++ b/backend/models/alexa.js @@ -2,22 +2,22 @@ var Alexa = require ('alexa-sdk'); const config = require ('../config/config'); var databaseHelper = require ('../helpers/database'); -// Build the context manually, because Amazon Lambda is missing -var context = { - succeed: function (result) { - console.log (result); - res.json (result); - }, - fail: function (error) { - console.log (error); - //We could send error json from here - }, -}; - var handlers = {}; module.exports = { run: function (req, res) { + // Build the context manually, because Amazon Lambda is missing + var context = { + succeed: function (result) { + console.log (result); + res.json (result); + }, + fail: function (error) { + console.log (error); + //We could send error json from here + }, + }; + var alexa = Alexa.handler (req.body, context); alexa.appId = config.SKILL_ID; alexa.registerHandlers (handlers); -- 2.47.3 From 6bfd4adcaf84e3be1cb695c7a06d48069e4be5a3 Mon Sep 17 00:00:00 2001 From: GotPPay Date: Thu, 18 Jan 2018 21:50:26 +0100 Subject: [PATCH 4/6] keep session open --- backend/models/alexa.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/models/alexa.js b/backend/models/alexa.js index ebfce94..20a5e34 100644 --- a/backend/models/alexa.js +++ b/backend/models/alexa.js @@ -31,13 +31,15 @@ module.exports = { handlers = {}; handlers = { LaunchRequest: function () { - this.response.speak (activeSkill.invocationAnswer); + this.response.listen (activeSkill.invocationAnswer);// Using listen so session doesn't end + //TODO : Maybe to ask user does he want to hear possible intents + //For this functionality, we need explanation text for each intent (Question) this.emit (':responseReady'); }, }; activeSkill.intents.map (intent => { handlers[intent.intentName] = function () { - this.response.speak (intent.answer); + this.response.listen (intent.answer); //Using listen so session doesn't end this.emit (':responseReady'); }; }); -- 2.47.3 From 9e4b06bd4c540554984690199420e78ce3c580f7 Mon Sep 17 00:00:00 2001 From: GotPPay Date: Thu, 18 Jan 2018 21:56:56 +0100 Subject: [PATCH 5/6] keep session open - fix --- backend/models/alexa.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/models/alexa.js b/backend/models/alexa.js index 20a5e34..ad58067 100644 --- a/backend/models/alexa.js +++ b/backend/models/alexa.js @@ -24,6 +24,8 @@ module.exports = { alexa.execute (); }, updateModel: function () { + //TODO : Alexa-sdk has handlers like SessionEndedRequest, Unhandled ,... that can be used + //Get info from database, and store it for faster response on intent databaseHelper .getSkill (config.SKILL_DB_ID) @@ -31,7 +33,9 @@ module.exports = { handlers = {}; handlers = { LaunchRequest: function () { - this.response.listen (activeSkill.invocationAnswer);// Using listen so session doesn't end + this.response + .speak (activeSkill.invocationAnswer) + .listen (' Would you like to continue ?'); //TODO : Maybe to ask user does he want to hear possible intents //For this functionality, we need explanation text for each intent (Question) this.emit (':responseReady'); @@ -39,7 +43,9 @@ module.exports = { }; activeSkill.intents.map (intent => { handlers[intent.intentName] = function () { - this.response.listen (intent.answer); //Using listen so session doesn't end + this.response + .speak (intent.answer) + .listen ('Would you like to continue ?'); this.emit (':responseReady'); }; }); -- 2.47.3 From a570640fe14aebee78860973d43d5ea9450b62bf Mon Sep 17 00:00:00 2001 From: GotPPay Date: Thu, 18 Jan 2018 22:07:23 +0100 Subject: [PATCH 6/6] hande unknown questions --- backend/config/constants.js | 5 +++++ backend/models/alexa.js | 25 +++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/backend/config/constants.js b/backend/config/constants.js index 0fe470f..6674956 100644 --- a/backend/config/constants.js +++ b/backend/config/constants.js @@ -26,6 +26,11 @@ constants.HTTPResultCodes = { constants.SKILL_ID_LENGTH = 24; +constants.voiceResponseStrings = { + QUESTION_NOT_FOUND : 'Sorry, I didnt understan', + GENERIC_CONTINUE : 'Would you like to continue' +} + module.exports = constants; \ No newline at end of file diff --git a/backend/models/alexa.js b/backend/models/alexa.js index ad58067..d95c855 100644 --- a/backend/models/alexa.js +++ b/backend/models/alexa.js @@ -1,6 +1,7 @@ var Alexa = require ('alexa-sdk'); const config = require ('../config/config'); var databaseHelper = require ('../helpers/database'); +const constants = require ('../config/constants'); var handlers = {}; @@ -24,31 +25,47 @@ module.exports = { alexa.execute (); }, updateModel: function () { - //TODO : Alexa-sdk has handlers like SessionEndedRequest, Unhandled ,... that can be used - //Get info from database, and store it for faster response on intent databaseHelper .getSkill (config.SKILL_DB_ID) .then (activeSkill => { handlers = {}; + + //Handler for launch request handlers = { LaunchRequest: function () { this.response .speak (activeSkill.invocationAnswer) - .listen (' Would you like to continue ?'); + .listen (constants.voiceResponseStrings.GENERIC_CONTINUE); //Phrase from listen doesn't work !!! //TODO : Maybe to ask user does he want to hear possible intents //For this functionality, we need explanation text for each intent (Question) this.emit (':responseReady'); }, }; + + //Handlers for user defined questions activeSkill.intents.map (intent => { handlers[intent.intentName] = function () { this.response .speak (intent.answer) - .listen ('Would you like to continue ?'); + .listen (constants.voiceResponseStrings.GENERIC_CONTINUE); //Phrase from listen doesn't work !!! this.emit (':responseReady'); }; }); + + //Default handlers for unknown questions and session close + + handlers.Unhandled = function () { + this.response + .speak (constants.voiceResponseStrings.QUESTION_NOT_FOUND) + .listen (constants.voiceResponseStrings.GENERIC_CONTINUE); + } + + handlers.SessionEndedRequest = function(){ + //We don't care for now + } + + }) .catch (e => { //Something is wrong, skill is not ready, use catch-all intent to inform user -- 2.47.3