This commit is contained in:
GotPPay
2018-01-12 01:56:17 +01:00
parent 4c8c1c5e0e
commit b80843cb97
20 changed files with 482 additions and 453 deletions

View File

@@ -1,60 +0,0 @@
{
"languageModel": {
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "GetProcessIntent",
"samples": [
"tell me your process",
"what is your process",
"how do you work",
"about your proces"
],
"slots": []
},
{
"name": "GetProjectsIntent",
"samples": [
"list me your projects",
"show me what you have done",
"what did you do so far",
"what are your projects",
"your project"
],
"slots": []
},
{
"name": "GetServicesIntent",
"samples": [
"what are your services",
"what services do you ofer",
"what you do",
"your service",
"tell me service"
],
"slots": []
},
{
"name": "GetTechnologiesIntent",
"samples": [
"list me your technologies",
"give me your technologies",
"what you know"
],
"slots": []
}
],
"invocationName": "saburly"
}
}

205
backend/components/alexa.js Normal file
View File

@@ -0,0 +1,205 @@
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
.loadSkill (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');
});
});
},
};

View File

@@ -0,0 +1,25 @@
const constants = {};
constants.amazonResultCodes = {
ok:200,
accepted:202,
badRequest:400,
unauthorized:401,
notFound:404,
conflict:409,
payloadTooLarge:413
}
constants.apiResultCodes = {
genericError : -1,
ok:0,
amazonError:1,
databaseError:2,
}
constants.skillIDLength = 24;
module.exports = constants;

View File

@@ -1,268 +0,0 @@
var amazonHelper = require ('./helpers/amazon');
var databaseHelper = require ('./helpers/database');
const config = require ('./config');
require ('isomorphic-fetch');
var express = require ('express');
var alexa = require ('alexa-app');
var bodyParser = require ('body-parser');
var MongoClient = require ('mongodb').MongoClient;
var ObjectID = require ('mongodb').ObjectID;
const router = express.Router ();
var app = express ();
//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 :
// 0 : Not sending Message
// 1 : Waiting for name
// 2 : Waiting for email
// 3 : Waiting for message
// ALWAYS setup the alexa app and attach it to express before anything else.
var alexaApp = new alexa.app ('saburly'); // this means we still work with one skill
alexaApp.express ({
expressApp: app,
// 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,
});
// now POST calls to /test in express will be handled by the app.request() function
// from here on you can setup any other express routes or middlewares as nor
app.set ('view engine', 'ejs');
var updateIntentsJSON = function () {
databaseHelper
.loadSkill (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');
});
});
};
router.get ('/getSkill/:id', async (req, res, next) => {
const id = req.params.id;
if (id.length !== 24) {
res.json ([]);
} else {
databaseHelper
.getSkill (id)
.then (result => {
res.json (result);
})
.catch (err => {
res.json ([]);
});
}
});
router.get ('/deleteSkill/:skillID', async (req, res, next) => {
databaseHelper
.deleteSkill (req.params.id)
.then (result => {
res.json (result);
})
.catch (err => {
res.json (err);
});
});
router.post ('/updateSkill/:id', async (req, res, next) => {
let id = req.params.id;
let dataFromWeb = JSON.stringify(req.body);
let skill = JSON.parse(dataFromWeb);
let updateOnAmazon = skill.updateOnAmazon;
delete skill.updateOnAmazon;
delete skill._id;
console.log('id = ' + id);
if (id !== '-1') {
if (updateOnAmazon){
amazonHelper.updateSkill(skill).then(amazonResult=>{
console.log('Amazon : ' + amazonResult);
if (amazonResult === 200 || amazonResult === 202) {
//Skill uploaded, it's ok to update databaseI
databaseHelper
.updateSkill (id, skill)
.then (result => {
res.json ({result: 0, message: 'ok'});
updateIntentsJSON ();
})
.catch (e => {
res.json ({result: -1, message: 'error'});
});
}else{
res.json({result: -1, message: 'amazon error : ' + amazonResult});
}
}).catch(e=>{
res.json ({result: -1, message: e});
});
}else{
databaseHelper
.updateSkill (id, skill)
.then (result => {
res.json ({result: 0, message: 'ok'});
updateIntentsJSON ();
})
.catch (e => {
res.json ({result: -1, message: 'error'});
});
}
} else {
//no new skills for now
}
});
app.use (function (req, res, next) {
res.header ('Access-Control-Allow-Origin', '*');
res.header ('Access-Control-Allow-Headers', 'Origin, Content-Type');
res.header ('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header ('Access-Control-Allow-Credentials', 'true');
next ();
});
app.use (bodyParser.json ());
app.use ('/', router);
MongoClient.connect (config.dbURL)
.then (database => {
databaseHelper.initModule (database);
app.listen (config.PORT, () => {
console.log ('Express server running on port ' + config.PORT);
updateIntentsJSON ();
databaseHelper.loadTokens ();
});
})
.catch (e => {
console.log ('error : ' + e);
});

View File

@@ -1,11 +1,10 @@
require ('isomorphic-fetch');
const config = require ('../config');
const config = require ('../config/config');
var request = require ('request');
var databaseHelper = require ('./database');
var getBuildStatus = function (skillID) {
try {
fetch (
fetch (
`https://api.amazonalexa.com/v0/skills/${skillID}/interactionModel/locales/en-US/status`,
{
method: 'GET',
@@ -18,9 +17,6 @@ var getBuildStatus = function (skillID) {
.then (result => {
return result;
});
} catch (e) {
console.log ('err : ' + e);
}
};
var refreshTokens = function () {

View File

@@ -1,4 +1,4 @@
const config = require ('../config');
const config = require ('../config/config');
var ObjectID = require ('mongodb').ObjectID;
var db = null;
@@ -71,11 +71,10 @@ module.exports = {
db
.collection ('skill_list')
.update ({_id: ObjectID (id)}, skill, {upsert: true}, (err, result) => {
if (JSON.parse (result).nModified === 1) {
//database update ok
resolve ();
} else {
reject ();
if (err){
reject();
}else{
resolve();
}
});
});
@@ -86,8 +85,11 @@ module.exports = {
db
.collection ('skill_list')
.remove ({_id: ObjectID (id)}, (err, result) => {
if (err) reject (err);
resolve (result);
if (err){
reject (err);
}else{
resolve (result);
}
});
});
},
@@ -98,8 +100,11 @@ module.exports = {
.collection ('skill_list')
.find ({_id: ObjectID (id)})
.toArray ((err, result) => {
if (err) reject (err);
resolve (result);
if (err) {
reject (err);
}else{
resolve (result);
}
});
});
},

122
backend/server.js Normal file
View File

@@ -0,0 +1,122 @@
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 bodyParser = require ('body-parser');
var MongoClient = require ('mongodb').MongoClient;
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);
// from here on you can setup any other express routes or middlewares as nor
app.set ('view engine', 'ejs');
/*
router.get ('/deleteSkill/:skillID', async (req, res, next) => {
databaseHelper
.deleteSkill (req.params.id)
.then (result => {
res.json (result);
})
.catch (err => {
res.json (err);
});
});
*/
router.get ('/getSkill/:id', async (req, res, next) => {
const id = req.params.id;
if (id.length !== constants.skillIDLength) {
res.json ([]);
} else {
databaseHelper
.getSkill (id)
.then (result => {
res.json (result);
})
.catch (err => {
res.json ([]);
});
}
});
router.post ('/updateSkill/:id', async (req, res, next) => {
let id = req.params.id;
let dataFromWeb = JSON.stringify(req.body);
let skill = JSON.parse(dataFromWeb);
let updateOnAmazon = skill.updateOnAmazon;
delete skill.updateOnAmazon;
delete skill._id;
console.log('id = ' + id);
if (id !== '-1') {
if (updateOnAmazon){
amazonHelper.updateSkill(skill).then(amazonResult=>{
console.log('Amazon : ' + amazonResult);
if (amazonResult === constants.amazonResultCodes.ok || amazonResult === constants.amazonResultCodes.accepted) {
//Skill uploaded, it's ok to update databaseI
databaseHelper
.updateSkill (id, skill)
.then (result => {
res.json ({result: constants.apiResultCodes.ok, message: ''});
alexa.updateIntentsJSON ();
})
.catch (e => {
res.json ({result: constants.apiResultCodes.databaseError, message: ''});
});
}else{
res.json({result: constants.apiResultCodes.amazonError, message: amazonResult});
}
}).catch(e=>{
res.json ({result: constants.apiResultCodes.amazonError, message: 'unknown'});
});
}else{
databaseHelper
.updateSkill (id, skill)
.then (result => {
res.json ({result: constants.apiResultCodes.ok, message: ''});
alexa.updateIntentsJSON ();
})
.catch (e => {
res.json ({result: constants.apiResultCodes.databaseError, message: ''});
});
}
} else {
//no new skills for now
}
});
app.use (function (req, res, next) {
res.header ('Access-Control-Allow-Origin', '*');
res.header ('Access-Control-Allow-Headers', 'Origin, Content-Type');
res.header ('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header ('Access-Control-Allow-Credentials', 'true');
next ();
});
app.use (bodyParser.json ());
app.use ('/', router);
MongoClient.connect (config.dbURL)
.then (database => {
databaseHelper.initModule (database);
app.listen (config.PORT, () => {
console.log ('Express server running on port ' + config.PORT);
alexa.updateIntentsJSON ();
databaseHelper.loadTokens ();
amazonHelper.getStatus(config.SKILL_ID);
});
})
.catch (e => {
console.log ('error : ' + e);
});