@@ -0,0 +1,11 @@ | |||||
language: node_js | |||||
node_js: | |||||
- "node" | |||||
- "7" | |||||
- "6" | |||||
- "5" | |||||
- "4" | |||||
- "lts/*" | |||||
script: npm test | |||||
env: | |||||
CODECLIMATE_REPO_TOKEN: a174b03227cdc953c831f45bebcf0af357ec1b8a4b8477f36444c16f084a409f |
@@ -1,14 +1,16 @@ | |||||
# Dota 2 Random - Alexa Skill | # Dota 2 Random - Alexa Skill | ||||
Skill is now available from the [Alexa Skill Store](https://www.amazon.com/binhonglee-Dota2-Random/dp/B073W4GDLS)! | |||||
[![Build Status](https://travis-ci.org/binhonglee/dota2-random.svg?branch=master)](https://travis-ci.org/binhonglee/dota2-random) [![Test Coverage](https://codeclimate.com/github/binhonglee/dota2-random/badges/coverage.svg)](https://codeclimate.com/github/binhonglee/dota2-random/coverage) [![Dependency Status](https://gemnasium.com/badges/github.com/binhonglee/dota2-random.svg)](https://gemnasium.com/github.com/binhonglee/dota2-random) | |||||
#### Dependency | |||||
- alexa-sdk | |||||
## Skill is now available from the [Alexa Skill Store](https://www.amazon.com/binhonglee-Dota2-Random/dp/B073W4GDLS)! | |||||
### Dependency | |||||
- node.js | - node.js | ||||
- alexa-sdk | |||||
## Documentations | |||||
### Documentations | |||||
### Intents | |||||
#### Intents | |||||
| Intents | Description | | | Intents | Description | | ||||
|:---------|:------------| | |:---------|:------------| | ||||
@@ -20,7 +22,7 @@ Skill is now available from the [Alexa Skill Store](https://www.amazon.com/binho | |||||
| `AMAZON.HelpIntent` | Provide examples on how to use the skill | | | `AMAZON.HelpIntent` | Provide examples on how to use the skill | | ||||
| `Stop` | Stop the skill | | | `Stop` | Stop the skill | | ||||
### ROLE_TYPES (Custom slot) | |||||
#### ROLE_TYPES (Custom slot) | |||||
- melee | - melee | ||||
- ranged | - ranged | ||||
- carry | - carry | ||||
@@ -0,0 +1,146 @@ | |||||
{ | |||||
"name": "dota2-random", | |||||
"version": "1.0.0", | |||||
"lockfileVersion": 1, | |||||
"requires": true, | |||||
"dependencies": { | |||||
"alexa-sdk": { | |||||
"version": "1.0.11", | |||||
"resolved": "https://registry.npmjs.org/alexa-sdk/-/alexa-sdk-1.0.11.tgz", | |||||
"integrity": "sha1-u8XZXJh4vHbEtI95Ca1iDIEESZU=", | |||||
"requires": { | |||||
"aws-sdk": "2.95.0", | |||||
"i18next": "3.5.2", | |||||
"i18next-sprintf-postprocessor": "0.2.2" | |||||
}, | |||||
"dependencies": { | |||||
"aws-sdk": { | |||||
"version": "2.95.0", | |||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.95.0.tgz", | |||||
"integrity": "sha1-JuIdsUlEOx8GOUnch5hPDRdwDmo=", | |||||
"requires": { | |||||
"buffer": "4.9.1", | |||||
"crypto-browserify": "1.0.9", | |||||
"events": "1.1.1", | |||||
"jmespath": "0.15.0", | |||||
"querystring": "0.2.0", | |||||
"sax": "1.2.1", | |||||
"url": "0.10.3", | |||||
"uuid": "3.0.1", | |||||
"xml2js": "0.4.17", | |||||
"xmlbuilder": "4.2.1" | |||||
}, | |||||
"dependencies": { | |||||
"buffer": { | |||||
"version": "4.9.1", | |||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", | |||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", | |||||
"requires": { | |||||
"base64-js": "1.2.1", | |||||
"ieee754": "1.1.8", | |||||
"isarray": "1.0.0" | |||||
}, | |||||
"dependencies": { | |||||
"base64-js": { | |||||
"version": "1.2.1", | |||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", | |||||
"integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" | |||||
}, | |||||
"ieee754": { | |||||
"version": "1.1.8", | |||||
"resolved": "http://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", | |||||
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" | |||||
}, | |||||
"isarray": { | |||||
"version": "1.0.0", | |||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | |||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" | |||||
} | |||||
} | |||||
}, | |||||
"crypto-browserify": { | |||||
"version": "1.0.9", | |||||
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", | |||||
"integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" | |||||
}, | |||||
"events": { | |||||
"version": "1.1.1", | |||||
"resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", | |||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" | |||||
}, | |||||
"jmespath": { | |||||
"version": "0.15.0", | |||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", | |||||
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" | |||||
}, | |||||
"querystring": { | |||||
"version": "0.2.0", | |||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", | |||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" | |||||
}, | |||||
"sax": { | |||||
"version": "1.2.1", | |||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", | |||||
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" | |||||
}, | |||||
"url": { | |||||
"version": "0.10.3", | |||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", | |||||
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", | |||||
"requires": { | |||||
"punycode": "1.3.2", | |||||
"querystring": "0.2.0" | |||||
}, | |||||
"dependencies": { | |||||
"punycode": { | |||||
"version": "1.3.2", | |||||
"resolved": "http://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", | |||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" | |||||
} | |||||
} | |||||
}, | |||||
"uuid": { | |||||
"version": "3.0.1", | |||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", | |||||
"integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" | |||||
}, | |||||
"xml2js": { | |||||
"version": "0.4.17", | |||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", | |||||
"integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", | |||||
"requires": { | |||||
"sax": "1.2.1", | |||||
"xmlbuilder": "4.2.1" | |||||
} | |||||
}, | |||||
"xmlbuilder": { | |||||
"version": "4.2.1", | |||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", | |||||
"integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", | |||||
"requires": { | |||||
"lodash": "4.17.4" | |||||
}, | |||||
"dependencies": { | |||||
"lodash": { | |||||
"version": "4.17.4", | |||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", | |||||
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"i18next": { | |||||
"version": "3.5.2", | |||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-3.5.2.tgz", | |||||
"integrity": "sha1-kwOQ1cMYzqpIWLUt0OQOayA/n0E=" | |||||
}, | |||||
"i18next-sprintf-postprocessor": { | |||||
"version": "0.2.2", | |||||
"resolved": "https://registry.npmjs.org/i18next-sprintf-postprocessor/-/i18next-sprintf-postprocessor-0.2.2.tgz", | |||||
"integrity": "sha1-LkCfEENXk4Jpi2otpwzapVHWfqQ=" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -6,14 +6,18 @@ | |||||
"dependencies": { | "dependencies": { | ||||
"alexa-sdk": "^1.0.7" | "alexa-sdk": "^1.0.7" | ||||
}, | }, | ||||
"scripts": { | |||||
"test": "npm install -g standard && standard && node test/testflow.js && npm uninstall -g standard" | |||||
}, | |||||
"author": "binhonglee", | "author": "binhonglee", | ||||
"license": "MIT", | "license": "MIT", | ||||
"repository": { | "repository": { | ||||
"type": "git", | "type": "git", | ||||
"url": "git+ssh://git@github.com/binhonglee/dota2-alexa-skill.git" | |||||
"url": "git+ssh://git@github.com/binhonglee/dota2-random.git" | |||||
}, | }, | ||||
"bugs": { | "bugs": { | ||||
"url": "https://github.com/binhonglee/dota2-alexa-skill/issues" | |||||
"url": "https://github.com/binhonglee/dota2-random/issues" | |||||
}, | }, | ||||
"homepage": "https://github.com/binhonglee/dota2-alexa-skill#readme" | |||||
"homepage": "https://github.com/binhonglee/dota2-random#readme", | |||||
"devDependencies": {} | |||||
} | } |
@@ -81,7 +81,8 @@ function randomHero (categories, callback) { | |||||
} | } | ||||
if (!possible) { | if (!possible) { | ||||
callback('Random failed') | |||||
var errorMessage = 'Random failed' | |||||
callback(errorMessage) | |||||
return | return | ||||
} | } | ||||
@@ -0,0 +1,7 @@ | |||||
LaunchRequest | |||||
AMAZON.HelpIntent | |||||
AnyRequest | |||||
RequestIntent Role=agility | |||||
RequestTwoIntent RoleOne=melee RoleTwo=ranged | |||||
RequestThreeIntent RoleOne=melee RoleTwo=intelligence RoleThree=support | |||||
Stop |
@@ -0,0 +1,290 @@ | |||||
// Test Flow - a multiple intent test script for Alexa Lambda code | |||||
// Launch from a Terminal Prompt. Examples: | |||||
// node testflow | |||||
// node testflow staterequest.txt | |||||
// Originally taken from https://github.com/alexa/alexa-cookbook/blob/master/testing/TestFlow/testflow.js | |||||
// Toggle on or off various debugging outputs | |||||
const options = { | |||||
speechOutput: true, | |||||
slots: true, | |||||
stdout: false, // standard output / console.log() in your code | |||||
requestEvent: false, // show the full request JSON sent to your code | |||||
reprompt: false, | |||||
delay: 1.0 // seconds between requests | |||||
} | |||||
var locale = 'en-US' | |||||
var fs = require('fs') | |||||
var MyLambdaFunction = require('../src/index.js') // Your Lambda source with exports.handler | |||||
var MyDialog = './test/dialogs/default.txt' | |||||
var appId = '' // 'amzn1.ask.skill.d60ceb98-befe-4f49-a627-1f20f6e98d94' | |||||
var slotArray = [] | |||||
if (process.argv[2]) { | |||||
MyDialog = './dialogs/' + process.argv[2] | |||||
} | |||||
console.log() | |||||
console.log('================================================================================') | |||||
console.log('Running test sequence from dialog file : ', MyDialog) | |||||
console.log() | |||||
const OriginalConsoleLog = console.log | |||||
var slotname = '' | |||||
var slotvalue = '' | |||||
var sa = {} | |||||
var currentLine = 1 | |||||
var lineArray = [] | |||||
var Intent = '' | |||||
var prompt = false | |||||
var context = { | |||||
'succeed': function (data) { | |||||
if (data.response.shouldEndSession) { | |||||
sa = {} | |||||
} else { | |||||
sa = data.sessionAttributes | |||||
} | |||||
console.log = OriginalConsoleLog | |||||
var textToSay = data.response.outputSpeech.ssml | |||||
if (options.speechOutput) { | |||||
console.log('\n\x1b[36m%s\x1b[0m', textToSay) | |||||
} | |||||
if (data.response.reprompt && data.response.reprompt.outputSpeech && data.response.reprompt.outputSpeech.ssml) { | |||||
var textReprompt = data.response.reprompt.outputSpeech.ssml | |||||
if (options.reprompt) { | |||||
// console.log('%s \x1b[33m\x1b[1m%s\x1b[0m \x1b[2m%s\x1b[0m', currentLine+1, Intent, sdkState) | |||||
console.log('\x1b[36m \x1b[2m%s\x1b[0m ', textReprompt) | |||||
} | |||||
} | |||||
if (data.response.shouldEndSession) { | |||||
console.log('\n================= Session Ended =================') | |||||
} | |||||
if (currentLine < lineArray.length) { | |||||
console.log() | |||||
runSingleTest(lineArray, currentLine++, sa) | |||||
} else { | |||||
console.log('') | |||||
process.exit() | |||||
} | |||||
}, | |||||
'fail': function (err) { | |||||
console.log('context.fail occurred') | |||||
console.log(JSON.stringify(err, null, '\t')) | |||||
} | |||||
} | |||||
fs.readFile(MyDialog, function (err, data) { // open dialog sequence file and read Intents | |||||
// var newSession = true | |||||
if (err) {} | |||||
lineArray = cleanArray(data.toString().split('\n')) // remove empty or comment lines (# or //) | |||||
runSingleTest(lineArray, 0, {}) | |||||
}) | |||||
function runSingleTest (myLineArray, currentLine, sa) { | |||||
// console.log('--------------------------------------------------------------------------------') | |||||
// console.log('testing line ', currentLine) | |||||
// console.log('testing line values ', myLineArray[currentLine]) | |||||
prompt = false | |||||
var newSession = true | |||||
if (currentLine > 0) { | |||||
newSession = false | |||||
} | |||||
var tokenArray = myLineArray[currentLine].split(' ') | |||||
if (tokenArray[0].replace('\r', '') === '?') { // pause and prompt the user to confirm | |||||
prompt = true | |||||
// console.log(' ----------------- > prompt') | |||||
tokenArray.shift() // removes first item | |||||
} | |||||
var requestType = tokenArray[0].replace('\r', '') | |||||
tokenArray.shift() | |||||
if (requestType === 'stop') { | |||||
console.log('') | |||||
process.exit() | |||||
} | |||||
Intent = requestType | |||||
// slotArray = [] | |||||
var sdkState = '' | |||||
if (sa['STATE']) { | |||||
sdkState = sa['STATE'] | |||||
} | |||||
console.log('%s \x1b[33m\x1b[1m%s\x1b[0m \x1b[2m%s\x1b[0m', currentLine + 1, Intent, sdkState) | |||||
processArray(tokenArray, function (request) { | |||||
prepareTestRequest(sa, newSession, request) | |||||
}) | |||||
} | |||||
function processArray (arr, cb) { | |||||
if (arr.length > 0) { | |||||
var equalsPosition = arr[0].indexOf('=') | |||||
slotname = arr[0].substr(0, equalsPosition) | |||||
slotvalue = decodeURI(arr[0].substr(equalsPosition + 1, 300)).replace('\r', '') | |||||
promptForSlot(prompt, slotname, slotvalue, (newValue) => { | |||||
// console.log('slotname, slotvalue, newValue') | |||||
// console.log(slotname, slotvalue, newValue) | |||||
var answer = newValue.toString().trim() | |||||
// console.log('answer = ' + answer) | |||||
if (answer === '') { | |||||
answer = slotvalue | |||||
} | |||||
if (answer !== '') { | |||||
slotArray.push('"' + slotname + '": {"name":"' + slotname + '","value":"' + answer + '"}') | |||||
} | |||||
arr.shift() | |||||
processArray(arr, cb) // RECURSION | |||||
}) | |||||
} else { // nothing left in slot array | |||||
var slotArrayString = '{' + slotArray.toString() + '}' | |||||
var slotObj = JSON.parse(slotArrayString) | |||||
var req = { | |||||
'type': 'IntentRequest', | |||||
'intent': { | |||||
'name': Intent, | |||||
'slots': slotObj | |||||
}, | |||||
'locale': locale | |||||
} | |||||
cb(req) | |||||
// process.exit() | |||||
} | |||||
} | |||||
function prepareTestRequest (sa, newSession, request) { | |||||
var eventJSON = { | |||||
'session': { | |||||
'sessionId': 'SessionId.f9e6dcbb-b7da-4b47-905c.etc.etc', | |||||
'application': { | |||||
'applicationId': appId | |||||
}, | |||||
'attributes': sa, | |||||
'user': { | |||||
'userId': 'amzn1.ask.account.VO3PVTGF563MOPBY.etc.etc' | |||||
}, | |||||
'new': newSession | |||||
}, | |||||
request, | |||||
'version': '1.0' | |||||
} | |||||
if (options.requestEvent) { | |||||
console.log(JSON.stringify(request, null, 2)) | |||||
} | |||||
// blocking pause | |||||
var waitTill = new Date(new Date().getTime() + options.delay * 1000) | |||||
while (waitTill > new Date()) {} | |||||
// call the function | |||||
if (options.stdout) { | |||||
MyLambdaFunction['handler'](eventJSON, context, callback) | |||||
} else { | |||||
// console.log('setting log to {}') | |||||
console.log = function () {} | |||||
// console.log('set log to {}') | |||||
MyLambdaFunction['handler'](eventJSON, context, callback) | |||||
console.log = OriginalConsoleLog | |||||
} | |||||
} | |||||
function promptForSlot (prompt, slotname, slotvalue, callback) { | |||||
if (prompt) { | |||||
process.stdout.write('\x1b[34m' + slotname + ' \x1b[0m\x1b[32m [' + slotvalue + ']\x1b[0m: ') | |||||
// console.log('\x1b[34m%s :\x1b[0m\x1b[32m %s\x1b[0m ', slotname, slotvalue ) | |||||
process.stdin.once('data', function (data) { | |||||
var answer = data.toString().trim() | |||||
// console.log(answer) | |||||
if (answer === '') { | |||||
if (slotvalue === '') { | |||||
// no default, user must type something | |||||
console.error('Error: No default slot value defined, user must type a slot value.') | |||||
process.exit() | |||||
} else { | |||||
answer = slotvalue | |||||
} | |||||
} | |||||
callback(answer) | |||||
}) | |||||
} else { | |||||
if (options.slots) { | |||||
console.log('\x1b[34m%s :\x1b[0m\x1b[32m %s\x1b[0m ', slotname, slotvalue) | |||||
} | |||||
callback(slotvalue) | |||||
} | |||||
} | |||||
function callback (error, data) { | |||||
if (error) { | |||||
console.log('error: ' + error) | |||||
} else { | |||||
console.log(data) | |||||
} | |||||
} | |||||
function cleanArray (myArray) { | |||||
var cleanedArray = [] | |||||
for (var i = 0; i < myArray.length; i++) { | |||||
if (myArray[i] !== '' && myArray[i].substring(0, 1) !== '#' && myArray[i].substring(0, 2) !== '//') { | |||||
cleanedArray.push(myArray[i]) | |||||
} | |||||
} | |||||
return cleanedArray | |||||
} | |||||
// | |||||
// const fontcolor = { | |||||
// Reset = '\x1b[0m', | |||||
// Bright = '\x1b[1m', | |||||
// Dim = '\x1b[2m', | |||||
// Underscore = '\x1b[4m', | |||||
// Blink = '\x1b[5m', | |||||
// Reverse = '\x1b[7m', | |||||
// Hidden = '\x1b[8m', | |||||
// | |||||
// FgBlack = '\x1b[30m', | |||||
// FgRed = '\x1b[31m', | |||||
// FgGreen = '\x1b[32m', | |||||
// FgYellow = '\x1b[33m', | |||||
// FgBlue = '\x1b[34m', | |||||
// FgMagenta = '\x1b[35m', | |||||
// FgCyan = '\x1b[36m', | |||||
// FgWhite = '\x1b[37m', | |||||
// | |||||
// BgBlack = '\x1b[40m', | |||||
// BgRed = '\x1b[41m', | |||||
// BgGreen = '\x1b[42m', | |||||
// BgYellow = '\x1b[43m', | |||||
// BgBlue = '\x1b[44m', | |||||
// BgMagenta = '\x1b[45m', | |||||
// BgCyan = '\x1b[46m', | |||||
// BgWhite = '\x1b[47m' | |||||
// } |