├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── bot ├── handlers │ ├── save-expense-data.js │ ├── save-income-data.js │ ├── set_alarm.js │ └── summary.js └── scenarios │ ├── botGames.json │ ├── dialogs.json │ ├── router.json │ ├── sage.pegg.new-expense.json │ ├── sage.pegg.new-income.json │ ├── sage.pegg.router.json │ ├── smoking.json │ ├── stomachPain.json │ └── validationsAndCarousels.json ├── config ├── dev.sample.json └── index.js ├── exceed.js ├── images ├── workout1.png └── workout2.png ├── index.js ├── loadOnDemand.js ├── package-lock.json ├── package.json ├── proxyMode.js └── router.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # remove private 7 | *.private.* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional REPL history 40 | .node_repl_history 41 | 42 | .DS_Store 43 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/proxyMode.js", 9 | "stopOnEntry": false, 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "preLaunchTask": null, 13 | "runtimeExecutable": null, 14 | "runtimeArgs": [ 15 | "--nolazy" 16 | ], 17 | "env": { 18 | "NODE_ENV": "development" 19 | }, 20 | "console": "internalConsole", 21 | "sourceMaps": false, 22 | "outDir": null 23 | }, 24 | { 25 | "name": "Attach", 26 | "type": "node", 27 | "request": "attach", 28 | "port": 5858, 29 | "address": "localhost", 30 | "restart": false, 31 | "sourceMaps": false, 32 | "outDir": null, 33 | "localRoot": "${workspaceRoot}", 34 | "remoteRoot": null 35 | }, 36 | { 37 | "name": "Attach to Process", 38 | "type": "node", 39 | "request": "attach", 40 | "processId": "${command.PickProcess}", 41 | "port": 5858, 42 | "sourceMaps": false, 43 | "outDir": null 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Catalyst Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bot Trees 2 | This is a sample bot app that uses the [bot-graph-dialog](https://github.com/CatalystCode/bot-graph-dialog) extension for dynamically loading graph-based dialogs. 3 | Use this app as a reference for using the `bot-graph-dialog` extension. 4 | 5 | **Read more about the motivation for developing this extension [here](https://www.microsoft.com/developerblog/real-life-code/2016/11/11/Bot-Graph-Dialog.html)** 6 | 7 | 8 | ## Getting Started 9 | 10 | **Note**- This page assumes you're already familar with the [core concepts](https://docs.botframework.com/en-us/node/builder/guides/core-concepts/#navtitle) of bots and how to run them locally on your machine. 11 | You'll need to have a bot provisioned in the developer portal. Follow [these instructions](https://docs.botframework.com/en-us/csharp/builder/sdkreference/gettingstarted.html) (look for the _Registering your Bot with the Microsoft Bot Framework_ section) to register your bot with the Microsoft Bot Framework. 12 | 13 | ``` 14 | git clone https://github.com/CatalystCode/bot-trees.git 15 | cd bot-trees 16 | npm install 17 | ``` 18 | 19 | Create a `config/dev.private.json` base on the `config/dev.sample.json` file. Edit it and add your bot App id and password. 20 | 21 | After connecting your bot to this endpoint, run `npm start`. 22 | 23 | 24 | 25 | ## Samples 26 | There are a few sample files in the root of this project: 27 | 28 | ## index.js 29 | This is the default sample which loads the different dialogs from the `bot/scenarios/dialogs.json` file. 30 | It adds each of the scenarios on the bot and binds it to the relevant RegEx as defined in the scenario. 31 | 32 | ## loadOnDemand.js 33 | This file demonstrates how to reload scenarios on-demand in cases that a scenario was modified on the backend. 34 | In this scenario, if a user was in the middle of a dialog that was updated, he would get a message saying the dialog was changed and that he will need to restart the dialog. 35 | 36 | To test this scenario, after starting a dialog such as the `stomachPain` by sending `stomach` to the bot: 37 | 38 | 1. Answer the first question the bot asks. 39 | 2. Change the version of the stomach pain scenario in the `bot/scenarios/stomachPain.json` file, and maybe the text of the first question, then save the file. 40 | 3. Browse the following URL `http://localhost:3978/api/load/stomachPain` to reload the scenario. 41 | 4. Continue the conversation with the bot. You should get a message that the dialog was updated and that you need to start over. You should see the updated question now. 42 | 43 | **Comment** If the `version` field in the root of the dialog object is specified, this will be considered as the scenario version. If the version was not specified, the bot will assign a version by hashing the content of the json file. 44 | 45 | ## router.js 46 | This file demonstrates how to use [LUIS](https://www.luis.ai/) in order to extract a user intent, and then branch to the next scenario based on the result that was received from LUIS. 47 | 48 | # License 49 | [MIT](LICENSE) 50 | 51 | -------------------------------------------------------------------------------- /bot/handlers/save-expense-data.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (session, next) => { 3 | session.dialogData.data.expense = {}; 4 | session.send("expense data saved..."); 5 | return next(); 6 | } -------------------------------------------------------------------------------- /bot/handlers/save-income-data.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (session, next) => { 3 | session.send("income data saved..."); 4 | return next(); 5 | } -------------------------------------------------------------------------------- /bot/handlers/set_alarm.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (session, next, data) => { 3 | 4 | var intent = session.dialogData.data[data.source]; 5 | var alarmTime = null; 6 | if (intent.actions[0].parameters[0].name == "time") { 7 | // alarmTime = intent.entities... 8 | // use intent.entities to extract relevant information 9 | // assuming extracted alarmTime 10 | 11 | alarmTime = '2016-10-10 10:10'; 12 | } 13 | 14 | if (data.target && alarmTime) { 15 | session.dialogData.data[data.target] = alarmTime; 16 | } 17 | 18 | session.send('Alarm set for ' + alarmTime); 19 | return next(); 20 | } -------------------------------------------------------------------------------- /bot/handlers/summary.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (session, next) => { 3 | var summary = "Summary: "; 4 | for (var prop in session.dialogData.data) { 5 | summary += prop + ': [' + session.dialogData.data[prop] + ']; '; 6 | } 7 | session.send(summary); 8 | return next(); 9 | } -------------------------------------------------------------------------------- /bot/scenarios/botGames.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "botGames", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "id": "whatDoYouWantToDo", 7 | "type": "prompt", 8 | "data": { "text": "try to set an alarm or commands like quit, start over, etc..." } 9 | }, 10 | { 11 | "id": "userIntent", 12 | "type": "score", 13 | "data": { 14 | "source": "whatDoYouWantToDo", 15 | "models": [ "bot-common-responses", "Bottle" ] 16 | }, 17 | "scenarios": [ 18 | { 19 | "condition": "userIntent.intent == 'set alarm'", 20 | "steps": [ 21 | { 22 | "type": "handler", 23 | "data": { 24 | "name": "set_alarm.js", 25 | "js": [ 26 | "module.exports = (session, next, data) => { ", 27 | " var intent = session.dialogData.data[data.source]; ", 28 | " var alarmTime = null; ", 29 | " if (intent.actions[0].parameters[0].name == \"time\") { ", 30 | " // alarmTime = intent.entities... ", 31 | " // use intent.entities to extract relevant information ", 32 | " // assuming extracted alarmTime ", 33 | " alarmTime = '2016-10-10 10:10'; ", 34 | " } ", 35 | " if (data.target && alarmTime) { ", 36 | " session.dialogData.data[data.target] = alarmTime; ", 37 | " } ", 38 | " session.send('Alarm set for ' + alarmTime); ", 39 | " return next(); ", 40 | "} " 41 | ], 42 | "js_canBeEitherAStringOrAnArrayAsAbove": "module.exports = function (session, next, data) {}", 43 | "source": "userIntent", 44 | "target": "alarmTime" 45 | } 46 | } 47 | ] 48 | }, 49 | { 50 | "condition": "userIntent.intent == 'start over'", 51 | "nodeId": "whatDoYouWantToDo" 52 | }, 53 | { 54 | "condition": "userIntent.intent == 'quit'", 55 | "steps": [ 56 | { 57 | "type": "end", 58 | "data": { "text": "Thank you, goodbye." } 59 | } 60 | ] 61 | } 62 | ] 63 | }, 64 | { 65 | "type": "text", 66 | "data": { "text": "Done doing '{whatDoYouWantToDo}'." } 67 | } 68 | ], 69 | "models": [ 70 | { 71 | "name": "bot-common-responses", 72 | "url": "https://api.projectoxford.ai/luis/v1/application?id=7ff935f4-fe33-4a2a-b155-b82dbbf456ed&subscription-key=d7b46a6c72bf46c1b67f2c4f21acf960&q=" 73 | }, 74 | { 75 | "name": "Bottle", 76 | "url": "https://api.projectoxford.ai/luis/v1/application?id=0a2cc164-5a19-47b7-b85e-41914d9037ba&subscription-key=d7b46a6c72bf46c1b67f2c4f21acf960&q=" 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /bot/scenarios/dialogs.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogs": [ 3 | { 4 | "path": "/stomachPain", 5 | "regex": "^stomach", 6 | "scenario": "stomachPain" 7 | }, 8 | { 9 | "path": "/smoking", 10 | "regex": "^(smoke|smoking)", 11 | "scenario": "smoking" 12 | }, 13 | { 14 | "path": "/botGames", 15 | "regex": "^(bot|games|commands)", 16 | "scenario": "botGames" 17 | }, 18 | { 19 | "path": "/pegg", 20 | "regex": "^(sage|pegg)", 21 | "scenario": "sage.pegg.router" 22 | }, 23 | { 24 | "path": "/validations", 25 | "regex": "^(validation|carousel)", 26 | "scenario": "validationsAndCarousels" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /bot/scenarios/router.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "router", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "id": "scenarioIntent", 7 | "type": "score", 8 | "data": { 9 | "models": [ "dialog-router" ] 10 | }, 11 | "scenarios": [ 12 | { 13 | "condition": "scenarioIntent.intent == 'stomachPain'", 14 | "steps": [ { "subScenario": "stomachPain" } ] 15 | }, 16 | { 17 | "condition": "scenarioIntent.intent == 'botGames'", 18 | "steps": [ { "subScenario": "botGames" } ] 19 | } 20 | ] 21 | } 22 | ], 23 | "models": [ 24 | { 25 | "name": "dialog-router", 26 | "url": "https://api.projectoxford.ai/luis/v1/application?id=86e0ddab-7309-45e7-937a-ed92725004cf&subscription-key=d7b46a6c72bf46c1b67f2c4f21acf960&q=" 27 | }, 28 | { 29 | "name": "bot-common-responses", 30 | "url": "https://api.projectoxford.ai/luis/v1/application?id=7ff935f4-fe33-4a2a-b155-b82dbbf456ed&subscription-key=d7b46a6c72bf46c1b67f2c4f21acf960&q=" 31 | }, 32 | { 33 | "name": "Bottle", 34 | "url": "https://api.projectoxford.ai/luis/v1/application?id=0a2cc164-5a19-47b7-b85e-41914d9037ba&subscription-key=d7b46a6c72bf46c1b67f2c4f21acf960&q=" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /bot/scenarios/sage.pegg.new-expense.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "sage.pegg.new-expense", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "type": "handler", 7 | "data": { "name": "save-expense-data.js" } 8 | }, 9 | { 10 | "type": "sequence", 11 | "scenarios": [ 12 | { 13 | "condition": "!expense.amount", 14 | "steps": [ 15 | { 16 | "id": "expense.amount", 17 | "type": "prompt", 18 | "data": { "type": "text", "text": "How much did you pay?" } 19 | } 20 | ] 21 | } 22 | ] 23 | }, 24 | { 25 | "type": "sequence", 26 | "scenarios": [ 27 | { 28 | "condition": "!expense.time", 29 | "steps": [ 30 | { 31 | "id": "expense.time", 32 | "type": "prompt", 33 | "data": { "type": "time", "text": "When did you spend it?" } 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | { 40 | "type": "sequence", 41 | "scenarios": [ 42 | { 43 | "condition": "!expense.item", 44 | "steps": [ 45 | { 46 | "id": "expense.item", 47 | "type": "prompt", 48 | "data": { "type": "text", "text": "What was it for?" } 49 | } 50 | ] 51 | } 52 | ] 53 | }, 54 | { 55 | "type": "text", 56 | "data": { "text": "Thank you!" } 57 | } 58 | ] 59 | } -------------------------------------------------------------------------------- /bot/scenarios/sage.pegg.new-income.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "sage.pegg.new-income", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "type": "handler", 7 | "data": { "name": "save-income-data.js" } 8 | }, 9 | { 10 | "scenarios": [ 11 | { 12 | "condition": "!expense.amount", 13 | "steps": [ 14 | { 15 | "id": "expanse.amount", 16 | "type": "prompt", 17 | "data": { "type": "text", "text": "How much did you pay?" } 18 | } 19 | ] 20 | } 21 | ] 22 | }, 23 | { 24 | "scenarios": [ 25 | { 26 | "condition": "!expense.time", 27 | "steps": [ 28 | { 29 | "id": "expanse.time", 30 | "type": "prompt", 31 | "data": { "type": "date", "text": "When did you spend it?" } 32 | } 33 | ] 34 | } 35 | ] 36 | }, 37 | { 38 | "scenarios": [ 39 | { 40 | "condition": "!expense.item", 41 | "steps": [ 42 | { 43 | "id": "expanse.item", 44 | "type": "prompt", 45 | "data": { "type": "text", "text": "What was it for?" } 46 | } 47 | ] 48 | } 49 | ] 50 | }, 51 | { 52 | "type": "text", 53 | "data": { "text": "Thank you!" } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /bot/scenarios/sage.pegg.router.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "sage.pegg.router", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "id": "userIntent", 7 | "type": "prompt", 8 | "data": { "text": "What do you want to do?" } 9 | }, 10 | { 11 | "id": "scoredIntent", 12 | "type": "score", 13 | "data": { 14 | "models": [ "sage-router" ] 15 | }, 16 | "scenarios": [ 17 | { 18 | "condition": "scoredIntent.intent == 'sage.new-expense'", 19 | "steps": [ { "subScenario": "sage.pegg.new-expense" } ] 20 | }, 21 | { 22 | "condition": "scoredIntent.intent == 'sage.new-income'", 23 | "steps": [ { "subScenario": "sage.pegg.new-income" } ] 24 | } 25 | ] 26 | } 27 | ], 28 | "models": [ 29 | { 30 | "name": "sage-router", 31 | "url": "https://api.projectoxford.ai/luis/v1/application?id=65f369d0-f62b-43d9-ac7d-d02404262d76&subscription-key=a7454fa784574b44b9018ce35fcceda8&q=" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /bot/scenarios/smoking.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "smoking", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "id": "isSmoking", 7 | "type": "prompt", 8 | "data": { "type": "confirm", "text": "Do you smoke?" }, 9 | "scenarios": [ 10 | { 11 | "condition": "isSmoking", 12 | "steps": [ 13 | { 14 | "id": "smokeTime", 15 | "type": "prompt", 16 | "data": { "type": "number", "text": "For how many years?" } 17 | } 18 | ] 19 | }, 20 | { 21 | "condition": "!isSmoking", 22 | "steps": [ 23 | { 24 | "id": "sureNotSmoking", 25 | "type": "prompt", 26 | "data": { "type":"confirm", "text": "Are you sure?" }, 27 | "scenarios": [ 28 | { 29 | "condition": "!sureNotSmoking", 30 | "nodeId": "isSmoking" 31 | } 32 | ] 33 | } 34 | ] 35 | }] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /bot/scenarios/stomachPain.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "stomachPain", 3 | "version": "1", 4 | "type": "sequence", 5 | "steps": [ 6 | { 7 | "id": "age", 8 | "type": "prompt", 9 | "data": { "type": "number", "text": "How old are you?" } 10 | }, 11 | { 12 | "id": "whenStart", 13 | "type": "prompt", 14 | "data": { "type": "time", "text": "When did it start?" } 15 | }, 16 | { 17 | "id": "customTypeStepDemo", 18 | "type": "myCustomType", 19 | "data": { "someData": "someData" } 20 | }, 21 | { 22 | "subScenario": "smoking" 23 | }, 24 | { 25 | "id": "workout", 26 | "type": "prompt", 27 | "data": { 28 | "type": "choice", 29 | "text": "What kind of workout do you do?", 30 | "options": [ "None", "Crossfit", "TRX", "Kung-Fu" ], 31 | "config": { 32 | "listStyle": "button" 33 | } 34 | }, 35 | "varname": "workout", 36 | "scenarios": [ 37 | { 38 | "condition": "workout == 'Crossfit'", 39 | "steps": [ 40 | { 41 | "id": "cfWeight", 42 | "type": "prompt", 43 | "data": { "type":"number", "text": "How much do you lift?" }, 44 | "scenarios": [ 45 | { 46 | "condition": "cfWeight <= 100", 47 | "steps": [ 48 | { 49 | "id": "cfWeightSmallReason", 50 | "type": "prompt", 51 | "data": { "text": "Why so lite?" } 52 | } 53 | ] 54 | }, 55 | { 56 | "condition": "cfWeight > 100", 57 | "steps": [ 58 | { 59 | "id": "cfCrazy", 60 | "type": "prompt", 61 | "data": { "type":"confirm", "text": "Are you crazy?" }, 62 | "scenarios": [ 63 | { 64 | "condition": "cfCrazy", 65 | "steps": [ 66 | { 67 | "id": "takePills", 68 | "type": "text", 69 | "data": {"text": "Please start taking your pills" } 70 | } 71 | ] 72 | }, 73 | { 74 | "condition": "!cfCrazy", 75 | "steps": [ 76 | { 77 | "id": "cfHowLong", 78 | "type": "prompt", 79 | "data": { "type":"number", "text": "How many years have you been working out?" } 80 | }, 81 | { 82 | "id": "cfInstructor", 83 | "type": "prompt", 84 | "data": { "text": "Who is your instructor?" } 85 | }, 86 | { 87 | "id": "cfWhere", 88 | "type": "prompt", 89 | "data": { "text": "Where do you work out?" } 90 | } 91 | ] 92 | } 93 | ] 94 | } 95 | ] 96 | } 97 | ] 98 | } 99 | ] 100 | }, 101 | { 102 | "condition": "workout == 'TRX'", 103 | "steps": [ 104 | { 105 | "id": "trxStrength", 106 | "type": "prompt", 107 | "data": { 108 | "type":"choice", 109 | "text": "What is the strength of the rope?", 110 | "options": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 111 | } 112 | } 113 | ] 114 | }, 115 | { 116 | "condition": "workout == 'Kung-Fu'", 117 | "steps": [ 118 | { 119 | "id": "kfYears", 120 | "type": "prompt", 121 | "data": { "type":"number", "text": "How many years have you neen practicing?" } 122 | } 123 | ] 124 | } 125 | ] 126 | }, 127 | { 128 | "type": "text", 129 | "data": { "text": "Please wait while we finalize the data..." } 130 | }, 131 | { 132 | "id": "finalize", 133 | "type": "handler", 134 | "data": { "name": "summary.js" } 135 | } 136 | ] 137 | } -------------------------------------------------------------------------------- /bot/scenarios/validationsAndCarousels.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "validationsAndCarousels", 3 | "type": "sequence", 4 | "steps": [ 5 | { 6 | "id": "myCarousel", 7 | "type": "carousel", 8 | "data": { 9 | "text": "Can you see a carousel of hero cards bellow?", 10 | "cards": [ 11 | { 12 | "id": "myOtherCard", 13 | "type": "heroCard", 14 | "data": { 15 | "title": "Space Needle", 16 | "text": "The Space Needle is an observation tower in Seattle, Washington, a landmark of the Pacific Northwest, and an icon of Seattle.", 17 | "images": [ 18 | "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Seattlenighttimequeenanne.jpg/320px-Seattlenighttimequeenanne.jpg" 19 | ], 20 | "buttons": [ 21 | { 22 | "label": "Wikipedia", 23 | "action": "openUrl", 24 | "value": "https://en.wikipedia.org/wiki/Space_Needle" 25 | }, 26 | { 27 | "label": "Select", 28 | "action": "imBack", 29 | "value": "select:100" 30 | } 31 | ] 32 | } 33 | }, 34 | { 35 | "id": "myOtherOtherCard", 36 | "type": "heroCard", 37 | "data": { 38 | "title": "Pikes Place Market", 39 | "text": "Pike Place Market is a public market overlooking the Elliott Bay waterfront in Seattle, Washington, United States.", 40 | "images": [ 41 | "https://upload.wikimedia.org/wikipedia/en/thumb/2/2a/PikePlaceMarket.jpg/320px-PikePlaceMarket.jpg" 42 | ], 43 | "buttons": [ 44 | { 45 | "label": "Wikipedia", 46 | "action": "openUrl", 47 | "value": "https://en.wikipedia.org/wiki/Pike_Place_Market" 48 | }, 49 | { 50 | "label": "Select", 51 | "action": "imBack", 52 | "value": "select:101" 53 | } 54 | ] 55 | } 56 | } 57 | ] 58 | } 59 | }, 60 | { 61 | "id": "myCard", 62 | "type": "heroCard", 63 | "data": { 64 | "title": "Space Needle", 65 | "subtitle": "Our Subtitle", 66 | "text": "The Space Needle is an observation tower in Seattle, Washington, a landmark of the Pacific Northwest, and an icon of Seattle.", 67 | "images": [ 68 | "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Seattlenighttimequeenanne.jpg/320px-Seattlenighttimequeenanne.jpg" 69 | ], 70 | "tap": { 71 | "action": "openUrl", 72 | "value": "https://en.wikipedia.org/wiki/Space_Needle" 73 | }, 74 | "buttons": [ 75 | { 76 | "label": "Wikipedia", 77 | "action": "openUrl", 78 | "value": "https://en.wikipedia.org/wiki/Space_Needle" 79 | } 80 | ] 81 | } 82 | }, 83 | { 84 | "type": "text", 85 | "data": { 86 | "text": "Lets start!" 87 | } 88 | }, 89 | { 90 | "id": "isTesting", 91 | "type": "prompt", 92 | "data": { 93 | "type": "text", 94 | "text": "What are you doing? (I'll validate using regex: ^test)", 95 | "validation": { 96 | "type": "regex", 97 | "setup": { 98 | "pattern": "^test" 99 | } 100 | } 101 | } 102 | }, 103 | { 104 | "id": "flightDate", 105 | "type": "prompt", 106 | "data": { 107 | "type": "time", 108 | "text": "When would you like to fly?", 109 | "validation": { 110 | "type": "date", 111 | "setup": { 112 | "min_date": "2016-11-15 00:00:00", 113 | "max_date": "2016-11-25 23:59:59", 114 | "invalid_msg": "Oops, wrong date!" 115 | } 116 | } 117 | } 118 | }, 119 | { 120 | "type": "text", 121 | "data": { 122 | "text": "All good :)" 123 | } 124 | } 125 | ] 126 | } -------------------------------------------------------------------------------- /config/dev.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "MICROSOFT_APP_ID": "", 3 | "MICROSOFT_APP_PASSWORD": "" 4 | } -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Please refer to the dev.sample.json file. 3 | * Copy this file and create a new file named "dev.private.json". 4 | * Fill in the details for the features you'de like to support. 5 | * You don't have to fill in all settings, but leave those you're not using blank. 6 | */ 7 | 8 | var nconf = require('nconf'); 9 | var path = require('path'); 10 | 11 | var envFile = path.join(__dirname, 'dev.private.json'); 12 | var config = nconf.env().file({ file: envFile }); 13 | 14 | module.exports = config; -------------------------------------------------------------------------------- /exceed.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var util = require('util'); 3 | var express = require('express'); 4 | var builder = require('botbuilder'); 5 | var GraphDialog = require('bot-graph-dialog'); 6 | var request = require('request-promise'); 7 | var config = require('./config'); 8 | var fs = require('fs'); 9 | 10 | var port = process.env.PORT || 3978; 11 | var app = express(); 12 | 13 | var microsoft_app_id = config.get('MICROSOFT_APP_ID'); 14 | var microsoft_app_password = config.get('MICROSOFT_APP_PASSWORD'); 15 | 16 | var connector = new builder.ChatConnector({ 17 | appId: microsoft_app_id, 18 | appPassword: microsoft_app_password, 19 | }); 20 | 21 | var bot = new builder.UniversalBot(connector); 22 | 23 | // an implementation of the Navigator interface 24 | // which will act as the proxy for the backend API 25 | class ProxyNavigator { 26 | 27 | constructor() { 28 | // backend root URL 29 | this.apiUrl = "http://86d54bb8.ngrok.io/api/msBotFramework"; 30 | } 31 | 32 | // returns the current node of the dialog 33 | async getCurrentNode(session) { 34 | console.log(`getCurrentNode, message: ${JSON.stringify(session.message, true, 2)}`); 35 | 36 | var node; 37 | if (session.privateConversationData._currentNode) { 38 | try { 39 | node = JSON.parse(session.privateConversationData._currentNode); 40 | } 41 | catch(err) { 42 | console.error(`error parsing current node json: ${session.privateConversationData._currentNode}`); 43 | } 44 | } 45 | 46 | if (!node) { 47 | node = await this.getNextNode(session); 48 | } 49 | 50 | // in case of a node with a few steps (internal nodes) 51 | var internalIndex = session.privateConversationData._currInternalIndex; 52 | if (node && node.steps && !isNaN(internalIndex)) { 53 | node = node.steps[internalIndex]; 54 | } 55 | 56 | return node; 57 | }; 58 | 59 | // resolves the next node in the dialog 60 | async getNextNode(session) { 61 | console.log(`getNextNode, message: ${JSON.stringify(session.message, true, 2)}`); 62 | 63 | var node; 64 | if (session.privateConversationData._currentNode) { 65 | try { 66 | node = JSON.parse(session.privateConversationData._currentNode); 67 | } 68 | catch(err) { 69 | console.error(`error parsing current node json: ${session.privateConversationData._currentNode}`); 70 | } 71 | } 72 | 73 | if (node && node.steps) { 74 | var internalIndex = session.privateConversationData._currInternalIndex; 75 | internalIndex = isNaN(internalIndex) ? 0 : internalIndex + 1; 76 | if (internalIndex > node.steps.length - 1) { 77 | // get next node from remote api 78 | return await this.resolveNextRemoteNode(session); 79 | } 80 | 81 | session.privateConversationData._currInternalIndex = internalIndex; 82 | return node.steps[internalIndex]; 83 | } 84 | 85 | return await this.resolveNextRemoteNode(session); 86 | }; 87 | 88 | async resolveNextRemoteNode(session) { 89 | var body = { 90 | message: session.message 91 | }; 92 | 93 | // add the resolved value from the bot-graph-dialog. 94 | // in case of a 'time' prompt- "2 days ago" will be resolved to the 95 | // actual date 2 days ago 96 | if (session.privateConversationData._lastResolvedResult) { 97 | body.resolved = session.privateConversationData._lastResolvedResult; 98 | } 99 | 100 | var node = await this.callApi({ 101 | uri: this.apiUrl + '/message', 102 | body, 103 | method: 'POST', 104 | json: true 105 | }); 106 | 107 | delete session.privateConversationData._currInternalIndex; 108 | 109 | if (!node.steps) { 110 | node = { 111 | type: 'sequence', 112 | steps: [ node ] 113 | } 114 | } 115 | 116 | node.steps[node.steps.length - 1].stop = true; 117 | 118 | session.privateConversationData._currInternalIndex = 0; 119 | session.privateConversationData._currentNode = node ? JSON.stringify(node) : null; 120 | 121 | node = node.steps[0]; 122 | return node; 123 | } 124 | 125 | // calls the remote API 126 | async callApi(opts) { 127 | console.log(`invoking http call: ${util.inspect(opts)}`); 128 | 129 | try { 130 | var result = await request(opts); 131 | } 132 | catch(err) { 133 | console.error(`error invoking request: ${err.message}, opts: ${opts}`); 134 | return; 135 | } 136 | 137 | console.log(`got result: ${util.inspect(result)}`); 138 | return result.response; 139 | } 140 | } 141 | 142 | process.nextTick(async () => { 143 | var navigator = new ProxyNavigator(); 144 | var graphDialog = await GraphDialog.create({ bot, navigator, proxyMode: true }); 145 | bot.dialog('/', graphDialog.getDialog()); 146 | console.log(`proxy graph dialog loaded successfully`); 147 | }); 148 | 149 | app.post('/api/messages', connector.listen()); 150 | 151 | app.listen(port, () => { 152 | console.log('listening on port %s', port); 153 | }); 154 | -------------------------------------------------------------------------------- /images/workout1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/bot-trees/83b1937356d0bb292a3d142003121769c7125cea/images/workout1.png -------------------------------------------------------------------------------- /images/workout2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/bot-trees/83b1937356d0bb292a3d142003121769c7125cea/images/workout2.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var util = require('util'); 3 | var express = require('express'); 4 | var builder = require('botbuilder'); 5 | var GraphDialog = require('bot-graph-dialog'); 6 | var config = require('./config'); 7 | var fs = require('fs'); 8 | 9 | var port = process.env.PORT || 3978; 10 | var app = express(); 11 | 12 | var microsoft_app_id = config.get('MICROSOFT_APP_ID'); 13 | var microsoft_app_password = config.get('MICROSOFT_APP_PASSWORD'); 14 | 15 | var connector = new builder.ChatConnector({ 16 | appId: microsoft_app_id, 17 | appPassword: microsoft_app_password, 18 | }); 19 | 20 | var bot = new builder.UniversalBot(connector); 21 | var intents = new builder.IntentDialog(); 22 | 23 | var scenariosPath = path.join(__dirname, 'bot', 'scenarios'); 24 | var handlersPath = path.join(__dirname, 'bot', 'handlers'); 25 | 26 | bot.dialog('/', intents); 27 | 28 | intents.matches(/^(help|hi|hello)/i, [ 29 | session => { 30 | session.send('Hi, how can I help you?'); 31 | } 32 | ]); 33 | 34 | // dynamically load dialogs from external datasource 35 | // create a GraphDialog for each and bind it to the intents object 36 | process.nextTick(async () => { 37 | 38 | var dialogs = await loadDialogs(); 39 | 40 | dialogs.forEach(async dialog => { 41 | console.log(`loading scenario: ${dialog.scenario} for regex: ${dialog.regex}`); 42 | 43 | var re = new RegExp(dialog.regex, 'i'); 44 | intents.matches(re, [ 45 | function (session) { 46 | session.beginDialog(dialog.path, {}); 47 | } 48 | ]); 49 | 50 | try { 51 | var graphDialog = await GraphDialog.create({ 52 | bot, 53 | scenario: dialog.scenario, 54 | loadScenario, 55 | loadHandler, 56 | customTypeHandlers: getCustomTypeHandlers() 57 | }); 58 | } 59 | catch(err) { 60 | console.error(`error loading dialog: ${err.message}`); 61 | } 62 | 63 | bot.dialog(dialog.path, graphDialog.getDialog()); 64 | 65 | console.log(`graph dialog loaded successfully: scenario ${dialog.scenario} for regExp: ${dialog.regex}`); 66 | 67 | }); 68 | }); 69 | 70 | 71 | // this allows you to extend the json with more custom node types, 72 | // by providing your implementation to processing each custom type. 73 | // in the end of your implemention you should call the next callbacks 74 | // to allow the framework to continue with the dialog. 75 | // refer to the customTypeStepDemo node in the stomachPain.json scenario for an example. 76 | function getCustomTypeHandlers() { 77 | return [ 78 | { 79 | name: 'myCustomType', 80 | execute: (session, next, data) => { 81 | console.log(`in custom node type handler: customTypeStepDemo, data: ${data.someData}`); 82 | return next(); 83 | } 84 | } 85 | ]; 86 | } 87 | 88 | // this is the handler for loading scenarios from external datasource 89 | // in this implementation we're just reading it from a file 90 | // but it can come from any external datasource like a file, db, etc. 91 | function loadScenario(scenario) { 92 | return new Promise((resolve, reject) => { 93 | console.log('loading scenario', scenario); 94 | // implement loadScenario from external datasource. 95 | // in this example we're loading from local file 96 | var scenarioPath = path.join(scenariosPath, scenario + '.json'); 97 | 98 | return fs.readFile(scenarioPath, 'utf8', (err, content) => { 99 | if (err) { 100 | console.error("error loading json: " + scenarioPath); 101 | return reject(err); 102 | } 103 | 104 | var scenarioObj = JSON.parse(content); 105 | 106 | // simulating long load period 107 | setTimeout(() => { 108 | console.log('resolving scenario', scenarioPath); 109 | resolve(scenarioObj); 110 | }, Math.random() * 3000); 111 | }); 112 | }); 113 | } 114 | 115 | // this is the handler for loading handlers from external datasource 116 | // in this implementation we're just reading it from a file 117 | // but it can come from any external datasource like a file, db, etc. 118 | // 119 | // NOTE: handlers can also be embeded in the scenario json. See scenarios/botGames.json for an example. 120 | function loadHandler(handler) { 121 | return new Promise((resolve, reject) => { 122 | console.log('loading handler', handler); 123 | // implement loadHandler from external datasource. 124 | // in this example we're loading from local file 125 | var handlerPath = path.join(handlersPath, handler); 126 | var handlerString = null; 127 | return fs.readFile(handlerPath, 'utf8', (err, content) => { 128 | if (err) { 129 | console.error("error loading handler: " + handlerPath); 130 | return reject(err); 131 | } 132 | // simulating long load period 133 | setTimeout(() => { 134 | console.log('resolving handler', handler); 135 | resolve(content); 136 | }, Math.random() * 3000); 137 | }); 138 | }); 139 | } 140 | 141 | // this is the handler for loading scenarios from external datasource 142 | // in this implementation we're just reading it from the file scnearios/dialogs.json 143 | // but it can come from any external datasource like a file, db, etc. 144 | function loadDialogs() { 145 | return new Promise((resolve, reject) => { 146 | console.log('loading dialogs'); 147 | 148 | var dialogsPath = path.join(scenariosPath, "dialogs.json"); 149 | return fs.readFile(dialogsPath, 'utf8', (err, content) => { 150 | if (err) { 151 | console.error("error loading json: " + dialogsPath); 152 | return reject(err); 153 | } 154 | 155 | var dialogs = JSON.parse(content); 156 | 157 | // simulating long load period 158 | setTimeout(() => { 159 | console.log('resolving dialogs', dialogsPath); 160 | resolve(dialogs.dialogs); 161 | }, Math.random() * 3000); 162 | }); 163 | }); 164 | } 165 | 166 | app.post('/api/messages', connector.listen()); 167 | 168 | app.listen(port, () => { 169 | console.log('listening on port %s', port); 170 | }); 171 | -------------------------------------------------------------------------------- /loadOnDemand.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var express = require('express'); 3 | var builder = require('botbuilder'); 4 | var GraphDialog = require('bot-graph-dialog'); 5 | var config = require('./config'); 6 | var fs = require('fs'); 7 | 8 | var port = process.env.PORT || 3978; 9 | var app = express(); 10 | 11 | var microsoft_app_id = config.get('MICROSOFT_APP_ID'); 12 | var microsoft_app_password = config.get('MICROSOFT_APP_PASSWORD'); 13 | 14 | var connector = new builder.ChatConnector({ 15 | appId: microsoft_app_id, 16 | appPassword: microsoft_app_password, 17 | }); 18 | 19 | var bot = new builder.UniversalBot(connector); 20 | var intents = new builder.IntentDialog(); 21 | 22 | var scenariosPath = path.join(__dirname, 'bot', 'scenarios'); 23 | var handlersPath = path.join(__dirname, 'bot', 'handlers'); 24 | var dialogsMapById = {}; 25 | var dialogsMapByPath = {}; 26 | 27 | bot.dialog('/', intents); 28 | 29 | intents.matches(/^(help|hi|hello)/i, [ 30 | function (session) { 31 | session.send('Hi, how can I help you?'); 32 | } 33 | ]); 34 | 35 | // dynamically load dialog from a remote datasource 36 | // create a GraphDialog instance and bind it on the bot 37 | 38 | async function loadDialog(dialog) { 39 | 40 | console.log(`loading scenario: ${dialog.scenario} for regex: ${dialog.regex}`); 41 | 42 | var re = new RegExp(dialog.regex, 'i'); 43 | 44 | intents.matches(re, [ 45 | function (session) { 46 | session.beginDialog(dialog.path); 47 | } 48 | ]); 49 | 50 | try { 51 | var graphDialog = await GraphDialog.create({ 52 | bot, 53 | scenario: dialog.scenario, 54 | loadScenario, 55 | loadHandler, 56 | customTypeHandlers: getCustomTypeHandlers(), 57 | onBeforeProcessingStep 58 | }); 59 | } 60 | catch(err) { 61 | console.error(`error loading dialog: ${err.message}`); 62 | throw err; 63 | } 64 | 65 | dialog.graphDialog = graphDialog; 66 | dialogsMapById[graphDialog.getDialogId()] = dialog; 67 | dialogsMapByPath[dialog.path] = dialog; 68 | 69 | bot.dialog(dialog.path, graphDialog.getDialog()); 70 | console.log(`graph dialog loaded successfully: scenario ${dialog.scenario} version ${graphDialog.getDialogVersion()} for regExp: ${dialog.regex} on path ${dialog.path}`); 71 | 72 | } 73 | 74 | // trigger dynamic load of the dialogs 75 | loadDialogs() 76 | .then(dialogs => dialogs.forEach(dialog => loadDialog(dialog))) 77 | .catch(err => console.error(`error loading dialogs dynamically: ${err.message}`)); 78 | 79 | // intercept change in scenario version before processing each dialog step 80 | // if there was a change in the version, restart the dialog 81 | // TODO: think about adding this internally to the GraphDialog so users gets this as default behaviour. 82 | function onBeforeProcessingStep(session, args, next) { 83 | 84 | session.sendTyping(); 85 | var dialogVersion = this.getDialogVersion(); 86 | 87 | if (!session.privateConversationData._dialogVersion) { 88 | session.privateConversationData._dialogVersion = dialogVersion; 89 | } 90 | 91 | if (session.privateConversationData._dialogVersion !== dialogVersion) { 92 | session.send("Dialog updated. We'll have to start over."); 93 | return this.restartDialog(session); 94 | } 95 | 96 | return next(); 97 | } 98 | 99 | 100 | // this allows you to extend the json with more custom node types, 101 | // by providing your implementation to processing each custom type. 102 | // in the end of your implemention you should call the next callbacks 103 | // to allow the framework to continue with the dialog. 104 | // refer to the customTypeStepDemo node in the stomachPain.json scenario for an example. 105 | function getCustomTypeHandlers() { 106 | return [ 107 | { 108 | name: 'myCustomType', 109 | execute: (session, next, data) => { 110 | console.log(`in custom node type handler: customTypeStepDemo, data: ${data.someData}`); 111 | return next(); 112 | } 113 | } 114 | ]; 115 | } 116 | 117 | // this is the handler for loading scenarios from external datasource 118 | // in this implementation we're just reading it from a file 119 | // but it can come from any external datasource like a file, db, etc. 120 | function loadScenario(scenario) { 121 | return new Promise((resolve, reject) => { 122 | console.log('loading scenario', scenario); 123 | // implement loadScenario from external datasource. 124 | // in this example we're loading from local file 125 | var scenarioPath = path.join(scenariosPath, scenario + '.json'); 126 | 127 | return fs.readFile(scenarioPath, 'utf8', (err, content) => { 128 | if (err) { 129 | console.error("error loading json: " + scenarioPath); 130 | return reject(err); 131 | } 132 | 133 | var scenarioObj = JSON.parse(content); 134 | 135 | // simulating long load period 136 | setTimeout(() => { 137 | console.log('resolving scenario', scenarioPath); 138 | resolve(scenarioObj); 139 | }, Math.random() * 3000); 140 | }); 141 | }); 142 | } 143 | 144 | // this is the handler for loading handlers from external datasource 145 | // in this implementation we're just reading it from a file 146 | // but it can come from any external datasource like a file, db, etc. 147 | // 148 | // NOTE: handlers can also be embeded in the scenario json. See scenarios/botGames.json for an example. 149 | function loadHandler(handler) { 150 | return new Promise((resolve, reject) => { 151 | console.log('loading handler', handler); 152 | // implement loadHandler from external datasource. 153 | // in this example we're loading from local file 154 | var handlerPath = path.join(handlersPath, handler); 155 | var handlerString = null; 156 | return fs.readFile(handlerPath, 'utf8', (err, content) => { 157 | if (err) { 158 | console.error("error loading handler: " + handlerPath); 159 | return reject(err); 160 | } 161 | // simulating long load period 162 | setTimeout(() => { 163 | console.log('resolving handler', handler); 164 | resolve(content); 165 | }, Math.random() * 3000); 166 | }); 167 | }); 168 | } 169 | 170 | // this is the handler for loading scenarios from external datasource 171 | // in this implementation we're just reading it from the file scnearios/dialogs.json 172 | // but it can come from any external datasource like a file, db, etc. 173 | function loadDialogs() { 174 | return new Promise((resolve, reject) => { 175 | console.log('loading dialogs'); 176 | 177 | var dialogsPath = path.join(scenariosPath, "dialogs.json"); 178 | return fs.readFile(dialogsPath, 'utf8', (err, content) => { 179 | if (err) { 180 | console.error("error loading json: " + dialogsPath); 181 | return reject(err); 182 | } 183 | 184 | var dialogs = JSON.parse(content); 185 | 186 | // simulating long load period 187 | setTimeout(() => { 188 | console.log('resolving dialogs', dialogsPath); 189 | resolve(dialogs.dialogs); 190 | }, Math.random() * 3000); 191 | }); 192 | }); 193 | } 194 | 195 | app.post('/api/messages', connector.listen()); 196 | 197 | // endpoint for reloading scenario on demand 198 | app.get('/api/load/:scenario', async (req, res) => { 199 | var scenario = req.params.scenario; 200 | console.log(`reloading scenario: ${scenario}`); 201 | var dialog = dialogsMapById[scenario]; 202 | 203 | try { 204 | await dialog.graphDialog.reload(); 205 | var msg = `scenario id '${scenario}' reloaded`; 206 | console.log(msg); 207 | return res.end(msg); 208 | } 209 | catch(err) { 210 | return res.end(`error loading dialog: ${err.message}`) 211 | } 212 | 213 | }); 214 | 215 | app.listen(port, () => { 216 | console.log('listening on port %s', port); 217 | }); 218 | 219 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bot-trees", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "async": { 8 | "version": "1.5.2", 9 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 10 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" 11 | }, 12 | "base64url": { 13 | "version": "2.0.0", 14 | "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", 15 | "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" 16 | }, 17 | "bot-graph-dialog": { 18 | "version": "3.8.4", 19 | "resolved": "https://registry.npmjs.org/bot-graph-dialog/-/bot-graph-dialog-3.8.4.tgz", 20 | "integrity": "sha512-rMqT3keAcaPvSGdXGgamvJcfCJMUL9wsF0Aiuil/AAL6RAWIl+W4Sox3OpgQr8pZkJBL9q7XnRx4cPQ6jXt+WA==", 21 | "requires": { 22 | "botbuilder": "3.8.4", 23 | "extend": "3.0.0", 24 | "jsep": "0.3.0", 25 | "request-promise": "4.1.1", 26 | "strformat": "0.0.7", 27 | "underscore": "1.8.3", 28 | "uuid": "2.0.3" 29 | }, 30 | "dependencies": { 31 | "uuid": { 32 | "version": "2.0.3", 33 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", 34 | "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" 35 | } 36 | } 37 | }, 38 | "botbuilder": { 39 | "version": "3.8.4", 40 | "resolved": "https://registry.npmjs.org/botbuilder/-/botbuilder-3.8.4.tgz", 41 | "integrity": "sha1-/O3EK5J+zwUMJL5SDBKAgkQ3pwg=", 42 | "requires": { 43 | "async": "1.5.2", 44 | "base64url": "2.0.0", 45 | "chrono-node": "1.3.4", 46 | "jsonwebtoken": "7.4.1", 47 | "promise": "7.1.1", 48 | "request": "2.74.0", 49 | "rsa-pem-from-mod-exp": "0.8.4", 50 | "sprintf-js": "1.1.1", 51 | "url-join": "1.1.0" 52 | } 53 | }, 54 | "buffer-equal-constant-time": { 55 | "version": "1.0.1", 56 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 57 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 58 | }, 59 | "chrono-node": { 60 | "version": "1.3.4", 61 | "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-1.3.4.tgz", 62 | "integrity": "sha1-/CqSCGNuCdb9exLZSuJECTfeJL0=", 63 | "requires": { 64 | "moment": "2.18.1" 65 | } 66 | }, 67 | "ecdsa-sig-formatter": { 68 | "version": "1.0.9", 69 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", 70 | "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", 71 | "requires": { 72 | "base64url": "2.0.0", 73 | "safe-buffer": "5.1.1" 74 | } 75 | }, 76 | "express": { 77 | "version": "4.14.0", 78 | "resolved": "https://registry.npmjs.org/express/-/express-4.14.0.tgz", 79 | "integrity": "sha1-we4/Qs3Ikfs9xlCoki1R7IR9DWY=", 80 | "requires": { 81 | "accepts": "1.3.3", 82 | "array-flatten": "1.1.1", 83 | "content-disposition": "0.5.1", 84 | "content-type": "1.0.2", 85 | "cookie": "0.3.1", 86 | "cookie-signature": "1.0.6", 87 | "debug": "2.2.0", 88 | "depd": "1.1.0", 89 | "encodeurl": "1.0.1", 90 | "escape-html": "1.0.3", 91 | "etag": "1.7.0", 92 | "finalhandler": "0.5.0", 93 | "fresh": "0.3.0", 94 | "merge-descriptors": "1.0.1", 95 | "methods": "1.1.2", 96 | "on-finished": "2.3.0", 97 | "parseurl": "1.3.1", 98 | "path-to-regexp": "0.1.7", 99 | "proxy-addr": "1.1.2", 100 | "qs": "6.2.0", 101 | "range-parser": "1.2.0", 102 | "send": "0.14.1", 103 | "serve-static": "1.11.1", 104 | "type-is": "1.6.13", 105 | "utils-merge": "1.0.0", 106 | "vary": "1.1.0" 107 | }, 108 | "dependencies": { 109 | "accepts": { 110 | "version": "1.3.3", 111 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", 112 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", 113 | "requires": { 114 | "mime-types": "2.1.11", 115 | "negotiator": "0.6.1" 116 | }, 117 | "dependencies": { 118 | "mime-types": { 119 | "version": "2.1.11", 120 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz", 121 | "integrity": "sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw=", 122 | "requires": { 123 | "mime-db": "1.23.0" 124 | }, 125 | "dependencies": { 126 | "mime-db": { 127 | "version": "1.23.0", 128 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz", 129 | "integrity": "sha1-oxtAcK2uon1zLqMzdApk0OyaZlk=" 130 | } 131 | } 132 | }, 133 | "negotiator": { 134 | "version": "0.6.1", 135 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 136 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 137 | } 138 | } 139 | }, 140 | "array-flatten": { 141 | "version": "1.1.1", 142 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 143 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 144 | }, 145 | "content-disposition": { 146 | "version": "0.5.1", 147 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz", 148 | "integrity": "sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs=" 149 | }, 150 | "content-type": { 151 | "version": "1.0.2", 152 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", 153 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=" 154 | }, 155 | "cookie": { 156 | "version": "0.3.1", 157 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 158 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 159 | }, 160 | "cookie-signature": { 161 | "version": "1.0.6", 162 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 163 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 164 | }, 165 | "debug": { 166 | "version": "2.2.0", 167 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 168 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 169 | "requires": { 170 | "ms": "0.7.1" 171 | }, 172 | "dependencies": { 173 | "ms": { 174 | "version": "0.7.1", 175 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 176 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" 177 | } 178 | } 179 | }, 180 | "depd": { 181 | "version": "1.1.0", 182 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", 183 | "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=" 184 | }, 185 | "encodeurl": { 186 | "version": "1.0.1", 187 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 188 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 189 | }, 190 | "escape-html": { 191 | "version": "1.0.3", 192 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 193 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 194 | }, 195 | "etag": { 196 | "version": "1.7.0", 197 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", 198 | "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=" 199 | }, 200 | "finalhandler": { 201 | "version": "0.5.0", 202 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.0.tgz", 203 | "integrity": "sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc=", 204 | "requires": { 205 | "debug": "2.2.0", 206 | "escape-html": "1.0.3", 207 | "on-finished": "2.3.0", 208 | "statuses": "1.3.0", 209 | "unpipe": "1.0.0" 210 | }, 211 | "dependencies": { 212 | "statuses": { 213 | "version": "1.3.0", 214 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.0.tgz", 215 | "integrity": "sha1-jlV1jLIOdoLB9Pzo3KswvwHR4Ho=" 216 | }, 217 | "unpipe": { 218 | "version": "1.0.0", 219 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 220 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 221 | } 222 | } 223 | }, 224 | "fresh": { 225 | "version": "0.3.0", 226 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", 227 | "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" 228 | }, 229 | "merge-descriptors": { 230 | "version": "1.0.1", 231 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 232 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 233 | }, 234 | "methods": { 235 | "version": "1.1.2", 236 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 237 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 238 | }, 239 | "on-finished": { 240 | "version": "2.3.0", 241 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 242 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 243 | "requires": { 244 | "ee-first": "1.1.1" 245 | }, 246 | "dependencies": { 247 | "ee-first": { 248 | "version": "1.1.1", 249 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 250 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 251 | } 252 | } 253 | }, 254 | "parseurl": { 255 | "version": "1.3.1", 256 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", 257 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=" 258 | }, 259 | "path-to-regexp": { 260 | "version": "0.1.7", 261 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 262 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 263 | }, 264 | "proxy-addr": { 265 | "version": "1.1.2", 266 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.2.tgz", 267 | "integrity": "sha1-tMxfImENlTWCTBI675089zxAujc=", 268 | "requires": { 269 | "forwarded": "0.1.0", 270 | "ipaddr.js": "1.1.1" 271 | }, 272 | "dependencies": { 273 | "forwarded": { 274 | "version": "0.1.0", 275 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", 276 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=" 277 | }, 278 | "ipaddr.js": { 279 | "version": "1.1.1", 280 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.1.1.tgz", 281 | "integrity": "sha1-x5HZX1KynBJH1d+AraObinNkcjA=" 282 | } 283 | } 284 | }, 285 | "qs": { 286 | "version": "6.2.0", 287 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz", 288 | "integrity": "sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs=" 289 | }, 290 | "range-parser": { 291 | "version": "1.2.0", 292 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 293 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 294 | }, 295 | "send": { 296 | "version": "0.14.1", 297 | "resolved": "https://registry.npmjs.org/send/-/send-0.14.1.tgz", 298 | "integrity": "sha1-qVSYQyU5L1FTKndgdg5FlZjIn3o=", 299 | "requires": { 300 | "debug": "2.2.0", 301 | "depd": "1.1.0", 302 | "destroy": "1.0.4", 303 | "encodeurl": "1.0.1", 304 | "escape-html": "1.0.3", 305 | "etag": "1.7.0", 306 | "fresh": "0.3.0", 307 | "http-errors": "1.5.0", 308 | "mime": "1.3.4", 309 | "ms": "0.7.1", 310 | "on-finished": "2.3.0", 311 | "range-parser": "1.2.0", 312 | "statuses": "1.3.0" 313 | }, 314 | "dependencies": { 315 | "destroy": { 316 | "version": "1.0.4", 317 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 318 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 319 | }, 320 | "http-errors": { 321 | "version": "1.5.0", 322 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.0.tgz", 323 | "integrity": "sha1-scs9gmD9jiOGytMYkEWUM3LUghE=", 324 | "requires": { 325 | "inherits": "2.0.1", 326 | "setprototypeof": "1.0.1", 327 | "statuses": "1.3.0" 328 | }, 329 | "dependencies": { 330 | "inherits": { 331 | "version": "2.0.1", 332 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", 333 | "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" 334 | }, 335 | "setprototypeof": { 336 | "version": "1.0.1", 337 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.1.tgz", 338 | "integrity": "sha1-UgCbJ4iMTcSPWRlJwKgnWDTByn4=" 339 | } 340 | } 341 | }, 342 | "mime": { 343 | "version": "1.3.4", 344 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", 345 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" 346 | }, 347 | "ms": { 348 | "version": "0.7.1", 349 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 350 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" 351 | }, 352 | "statuses": { 353 | "version": "1.3.0", 354 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.0.tgz", 355 | "integrity": "sha1-jlV1jLIOdoLB9Pzo3KswvwHR4Ho=" 356 | } 357 | } 358 | }, 359 | "serve-static": { 360 | "version": "1.11.1", 361 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.1.tgz", 362 | "integrity": "sha1-1sznaTUF9zPHWd5Xvvwa92wPCAU=", 363 | "requires": { 364 | "encodeurl": "1.0.1", 365 | "escape-html": "1.0.3", 366 | "parseurl": "1.3.1", 367 | "send": "0.14.1" 368 | } 369 | }, 370 | "type-is": { 371 | "version": "1.6.13", 372 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz", 373 | "integrity": "sha1-boO6e8MM0zp7sLf7AHN6IIW/nQg=", 374 | "requires": { 375 | "media-typer": "0.3.0", 376 | "mime-types": "2.1.11" 377 | }, 378 | "dependencies": { 379 | "media-typer": { 380 | "version": "0.3.0", 381 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 382 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 383 | }, 384 | "mime-types": { 385 | "version": "2.1.11", 386 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz", 387 | "integrity": "sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw=", 388 | "requires": { 389 | "mime-db": "1.23.0" 390 | }, 391 | "dependencies": { 392 | "mime-db": { 393 | "version": "1.23.0", 394 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz", 395 | "integrity": "sha1-oxtAcK2uon1zLqMzdApk0OyaZlk=" 396 | } 397 | } 398 | } 399 | } 400 | }, 401 | "utils-merge": { 402 | "version": "1.0.0", 403 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", 404 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" 405 | }, 406 | "vary": { 407 | "version": "1.1.0", 408 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.0.tgz", 409 | "integrity": "sha1-4eWv+70WrnaN0mdDlLmtMCJlMUA=" 410 | } 411 | } 412 | }, 413 | "extend": { 414 | "version": "3.0.0", 415 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", 416 | "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=" 417 | }, 418 | "hoek": { 419 | "version": "2.16.3", 420 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", 421 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" 422 | }, 423 | "isemail": { 424 | "version": "1.2.0", 425 | "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", 426 | "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" 427 | }, 428 | "joi": { 429 | "version": "6.10.1", 430 | "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", 431 | "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", 432 | "requires": { 433 | "hoek": "2.16.3", 434 | "isemail": "1.2.0", 435 | "moment": "2.18.1", 436 | "topo": "1.1.0" 437 | } 438 | }, 439 | "jsep": { 440 | "version": "0.3.0", 441 | "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.3.0.tgz", 442 | "integrity": "sha1-/wF4E+rGmAnaXFZm1um/UnAOGss=" 443 | }, 444 | "jsonwebtoken": { 445 | "version": "7.4.1", 446 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz", 447 | "integrity": "sha1-fKMk9SFfi+A5zTWmxFu4y3SkSPs=", 448 | "requires": { 449 | "joi": "6.10.1", 450 | "jws": "3.1.4", 451 | "lodash.once": "4.1.1", 452 | "ms": "2.0.0", 453 | "xtend": "4.0.1" 454 | } 455 | }, 456 | "jwa": { 457 | "version": "1.1.5", 458 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", 459 | "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", 460 | "requires": { 461 | "base64url": "2.0.0", 462 | "buffer-equal-constant-time": "1.0.1", 463 | "ecdsa-sig-formatter": "1.0.9", 464 | "safe-buffer": "5.1.1" 465 | } 466 | }, 467 | "jws": { 468 | "version": "3.1.4", 469 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", 470 | "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", 471 | "requires": { 472 | "base64url": "2.0.0", 473 | "jwa": "1.1.5", 474 | "safe-buffer": "5.1.1" 475 | } 476 | }, 477 | "lodash.once": { 478 | "version": "4.1.1", 479 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 480 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 481 | }, 482 | "moment": { 483 | "version": "2.18.1", 484 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", 485 | "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" 486 | }, 487 | "ms": { 488 | "version": "2.0.0", 489 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 490 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 491 | }, 492 | "nconf": { 493 | "version": "0.8.4", 494 | "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.8.4.tgz", 495 | "integrity": "sha1-lQIjT3rWI4yrf5LXwGjCBDTT/5M=", 496 | "requires": { 497 | "async": "1.5.2", 498 | "ini": "1.3.4", 499 | "secure-keys": "1.0.0", 500 | "yargs": "3.32.0" 501 | }, 502 | "dependencies": { 503 | "async": { 504 | "version": "1.5.2", 505 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 506 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" 507 | }, 508 | "ini": { 509 | "version": "1.3.4", 510 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", 511 | "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" 512 | }, 513 | "secure-keys": { 514 | "version": "1.0.0", 515 | "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", 516 | "integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=" 517 | }, 518 | "yargs": { 519 | "version": "3.32.0", 520 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", 521 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", 522 | "requires": { 523 | "camelcase": "2.1.1", 524 | "cliui": "3.2.0", 525 | "decamelize": "1.2.0", 526 | "os-locale": "1.4.0", 527 | "string-width": "1.0.1", 528 | "window-size": "0.1.4", 529 | "y18n": "3.2.1" 530 | }, 531 | "dependencies": { 532 | "camelcase": { 533 | "version": "2.1.1", 534 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 535 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" 536 | }, 537 | "cliui": { 538 | "version": "3.2.0", 539 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 540 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 541 | "requires": { 542 | "string-width": "1.0.1", 543 | "strip-ansi": "3.0.1", 544 | "wrap-ansi": "2.0.0" 545 | }, 546 | "dependencies": { 547 | "strip-ansi": { 548 | "version": "3.0.1", 549 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 550 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 551 | "requires": { 552 | "ansi-regex": "2.0.0" 553 | }, 554 | "dependencies": { 555 | "ansi-regex": { 556 | "version": "2.0.0", 557 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", 558 | "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=" 559 | } 560 | } 561 | }, 562 | "wrap-ansi": { 563 | "version": "2.0.0", 564 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.0.0.tgz", 565 | "integrity": "sha1-fTD4+HP5pbvDpk2ryNF34HGuQm8=", 566 | "requires": { 567 | "string-width": "1.0.1" 568 | } 569 | } 570 | } 571 | }, 572 | "decamelize": { 573 | "version": "1.2.0", 574 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 575 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 576 | }, 577 | "os-locale": { 578 | "version": "1.4.0", 579 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 580 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 581 | "requires": { 582 | "lcid": "1.0.0" 583 | }, 584 | "dependencies": { 585 | "lcid": { 586 | "version": "1.0.0", 587 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 588 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 589 | "requires": { 590 | "invert-kv": "1.0.0" 591 | }, 592 | "dependencies": { 593 | "invert-kv": { 594 | "version": "1.0.0", 595 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 596 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 597 | } 598 | } 599 | } 600 | } 601 | }, 602 | "string-width": { 603 | "version": "1.0.1", 604 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", 605 | "integrity": "sha1-ySEptvHX9SrPmvQkom44ZKBc6wo=", 606 | "requires": { 607 | "code-point-at": "1.0.0", 608 | "is-fullwidth-code-point": "1.0.0", 609 | "strip-ansi": "3.0.1" 610 | }, 611 | "dependencies": { 612 | "code-point-at": { 613 | "version": "1.0.0", 614 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", 615 | "integrity": "sha1-9psZLT99keOC5Lcb3bd4eGGasMY=", 616 | "requires": { 617 | "number-is-nan": "1.0.0" 618 | }, 619 | "dependencies": { 620 | "number-is-nan": { 621 | "version": "1.0.0", 622 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", 623 | "integrity": "sha1-wCD1KcUoKt/dIz2R1LGBw9aG3Es=" 624 | } 625 | } 626 | }, 627 | "is-fullwidth-code-point": { 628 | "version": "1.0.0", 629 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 630 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 631 | "requires": { 632 | "number-is-nan": "1.0.0" 633 | }, 634 | "dependencies": { 635 | "number-is-nan": { 636 | "version": "1.0.0", 637 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", 638 | "integrity": "sha1-wCD1KcUoKt/dIz2R1LGBw9aG3Es=" 639 | } 640 | } 641 | }, 642 | "strip-ansi": { 643 | "version": "3.0.1", 644 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 645 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 646 | "requires": { 647 | "ansi-regex": "2.0.0" 648 | }, 649 | "dependencies": { 650 | "ansi-regex": { 651 | "version": "2.0.0", 652 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", 653 | "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=" 654 | } 655 | } 656 | } 657 | } 658 | }, 659 | "window-size": { 660 | "version": "0.1.4", 661 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", 662 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" 663 | }, 664 | "y18n": { 665 | "version": "3.2.1", 666 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 667 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 668 | } 669 | } 670 | } 671 | } 672 | }, 673 | "promise": { 674 | "version": "7.1.1", 675 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", 676 | "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", 677 | "requires": { 678 | "asap": "2.0.4" 679 | }, 680 | "dependencies": { 681 | "asap": { 682 | "version": "2.0.4", 683 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.4.tgz", 684 | "integrity": "sha1-s5G/f2v7xlcGAi/sj0nEsH/s9Yk=" 685 | } 686 | } 687 | }, 688 | "request": { 689 | "version": "2.74.0", 690 | "resolved": "https://registry.npmjs.org/request/-/request-2.74.0.tgz", 691 | "integrity": "sha1-dpPKdou7DqXIzgjAhKRe+gW4kqs=", 692 | "requires": { 693 | "aws-sign2": "0.6.0", 694 | "aws4": "1.4.1", 695 | "bl": "1.1.2", 696 | "caseless": "0.11.0", 697 | "combined-stream": "1.0.5", 698 | "extend": "3.0.0", 699 | "forever-agent": "0.6.1", 700 | "form-data": "1.0.0-rc4", 701 | "har-validator": "2.0.6", 702 | "hawk": "3.1.3", 703 | "http-signature": "1.1.1", 704 | "is-typedarray": "1.0.0", 705 | "isstream": "0.1.2", 706 | "json-stringify-safe": "5.0.1", 707 | "mime-types": "2.1.11", 708 | "node-uuid": "1.4.7", 709 | "oauth-sign": "0.8.2", 710 | "qs": "6.2.1", 711 | "stringstream": "0.0.5", 712 | "tough-cookie": "2.3.1", 713 | "tunnel-agent": "0.4.3" 714 | }, 715 | "dependencies": { 716 | "aws-sign2": { 717 | "version": "0.6.0", 718 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", 719 | "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" 720 | }, 721 | "aws4": { 722 | "version": "1.4.1", 723 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz", 724 | "integrity": "sha1-/efVKSRm0jDl7g9OA42d+qsI/GE=" 725 | }, 726 | "bl": { 727 | "version": "1.1.2", 728 | "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", 729 | "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", 730 | "requires": { 731 | "readable-stream": "2.0.6" 732 | }, 733 | "dependencies": { 734 | "readable-stream": { 735 | "version": "2.0.6", 736 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", 737 | "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", 738 | "requires": { 739 | "core-util-is": "1.0.2", 740 | "inherits": "2.0.1", 741 | "isarray": "1.0.0", 742 | "process-nextick-args": "1.0.7", 743 | "string_decoder": "0.10.31", 744 | "util-deprecate": "1.0.2" 745 | }, 746 | "dependencies": { 747 | "core-util-is": { 748 | "version": "1.0.2", 749 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 750 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 751 | }, 752 | "inherits": { 753 | "version": "2.0.1", 754 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", 755 | "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" 756 | }, 757 | "isarray": { 758 | "version": "1.0.0", 759 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 760 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 761 | }, 762 | "process-nextick-args": { 763 | "version": "1.0.7", 764 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 765 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" 766 | }, 767 | "string_decoder": { 768 | "version": "0.10.31", 769 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 770 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 771 | }, 772 | "util-deprecate": { 773 | "version": "1.0.2", 774 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 775 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 776 | } 777 | } 778 | } 779 | } 780 | }, 781 | "caseless": { 782 | "version": "0.11.0", 783 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", 784 | "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" 785 | }, 786 | "combined-stream": { 787 | "version": "1.0.5", 788 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", 789 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", 790 | "requires": { 791 | "delayed-stream": "1.0.0" 792 | }, 793 | "dependencies": { 794 | "delayed-stream": { 795 | "version": "1.0.0", 796 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 797 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 798 | } 799 | } 800 | }, 801 | "forever-agent": { 802 | "version": "0.6.1", 803 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 804 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 805 | }, 806 | "form-data": { 807 | "version": "1.0.0-rc4", 808 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz", 809 | "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=", 810 | "requires": { 811 | "async": "1.5.2", 812 | "combined-stream": "1.0.5", 813 | "mime-types": "2.1.11" 814 | }, 815 | "dependencies": { 816 | "async": { 817 | "version": "1.5.2", 818 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 819 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" 820 | } 821 | } 822 | }, 823 | "har-validator": { 824 | "version": "2.0.6", 825 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", 826 | "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", 827 | "requires": { 828 | "chalk": "1.1.3", 829 | "commander": "2.9.0", 830 | "is-my-json-valid": "2.13.1", 831 | "pinkie-promise": "2.0.1" 832 | }, 833 | "dependencies": { 834 | "chalk": { 835 | "version": "1.1.3", 836 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 837 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 838 | "requires": { 839 | "ansi-styles": "2.2.1", 840 | "escape-string-regexp": "1.0.5", 841 | "has-ansi": "2.0.0", 842 | "strip-ansi": "3.0.1", 843 | "supports-color": "2.0.0" 844 | }, 845 | "dependencies": { 846 | "ansi-styles": { 847 | "version": "2.2.1", 848 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 849 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 850 | }, 851 | "escape-string-regexp": { 852 | "version": "1.0.5", 853 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 854 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 855 | }, 856 | "has-ansi": { 857 | "version": "2.0.0", 858 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 859 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 860 | "requires": { 861 | "ansi-regex": "2.0.0" 862 | }, 863 | "dependencies": { 864 | "ansi-regex": { 865 | "version": "2.0.0", 866 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", 867 | "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=" 868 | } 869 | } 870 | }, 871 | "strip-ansi": { 872 | "version": "3.0.1", 873 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 874 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 875 | "requires": { 876 | "ansi-regex": "2.0.0" 877 | }, 878 | "dependencies": { 879 | "ansi-regex": { 880 | "version": "2.0.0", 881 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", 882 | "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=" 883 | } 884 | } 885 | }, 886 | "supports-color": { 887 | "version": "2.0.0", 888 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 889 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 890 | } 891 | } 892 | }, 893 | "commander": { 894 | "version": "2.9.0", 895 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 896 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 897 | "requires": { 898 | "graceful-readlink": "1.0.1" 899 | }, 900 | "dependencies": { 901 | "graceful-readlink": { 902 | "version": "1.0.1", 903 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 904 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" 905 | } 906 | } 907 | }, 908 | "is-my-json-valid": { 909 | "version": "2.13.1", 910 | "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", 911 | "integrity": "sha1-1Vd4qC/rawlj/0vhEdXRaE6JBwc=", 912 | "requires": { 913 | "generate-function": "2.0.0", 914 | "generate-object-property": "1.2.0", 915 | "jsonpointer": "2.0.0", 916 | "xtend": "4.0.1" 917 | }, 918 | "dependencies": { 919 | "generate-function": { 920 | "version": "2.0.0", 921 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", 922 | "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" 923 | }, 924 | "generate-object-property": { 925 | "version": "1.2.0", 926 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", 927 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", 928 | "requires": { 929 | "is-property": "1.0.2" 930 | }, 931 | "dependencies": { 932 | "is-property": { 933 | "version": "1.0.2", 934 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 935 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" 936 | } 937 | } 938 | }, 939 | "jsonpointer": { 940 | "version": "2.0.0", 941 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz", 942 | "integrity": "sha1-OvHdIP6FRjkQ1GmjheMwF9KgMNk=" 943 | }, 944 | "xtend": { 945 | "version": "4.0.1", 946 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 947 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 948 | } 949 | } 950 | }, 951 | "pinkie-promise": { 952 | "version": "2.0.1", 953 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 954 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 955 | "requires": { 956 | "pinkie": "2.0.4" 957 | }, 958 | "dependencies": { 959 | "pinkie": { 960 | "version": "2.0.4", 961 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 962 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 963 | } 964 | } 965 | } 966 | } 967 | }, 968 | "hawk": { 969 | "version": "3.1.3", 970 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", 971 | "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", 972 | "requires": { 973 | "boom": "2.10.1", 974 | "cryptiles": "2.0.5", 975 | "hoek": "2.16.3", 976 | "sntp": "1.0.9" 977 | }, 978 | "dependencies": { 979 | "boom": { 980 | "version": "2.10.1", 981 | "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", 982 | "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", 983 | "requires": { 984 | "hoek": "2.16.3" 985 | } 986 | }, 987 | "cryptiles": { 988 | "version": "2.0.5", 989 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", 990 | "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", 991 | "requires": { 992 | "boom": "2.10.1" 993 | } 994 | }, 995 | "hoek": { 996 | "version": "2.16.3", 997 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", 998 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" 999 | }, 1000 | "sntp": { 1001 | "version": "1.0.9", 1002 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", 1003 | "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", 1004 | "requires": { 1005 | "hoek": "2.16.3" 1006 | } 1007 | } 1008 | } 1009 | }, 1010 | "http-signature": { 1011 | "version": "1.1.1", 1012 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", 1013 | "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", 1014 | "requires": { 1015 | "assert-plus": "0.2.0", 1016 | "jsprim": "1.3.0", 1017 | "sshpk": "1.9.2" 1018 | }, 1019 | "dependencies": { 1020 | "assert-plus": { 1021 | "version": "0.2.0", 1022 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", 1023 | "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" 1024 | }, 1025 | "jsprim": { 1026 | "version": "1.3.0", 1027 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.0.tgz", 1028 | "integrity": "sha1-zi4b74NSBLTzCZkoxgL4tq5hVlA=", 1029 | "requires": { 1030 | "extsprintf": "1.0.2", 1031 | "json-schema": "0.2.2", 1032 | "verror": "1.3.6" 1033 | }, 1034 | "dependencies": { 1035 | "extsprintf": { 1036 | "version": "1.0.2", 1037 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", 1038 | "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" 1039 | }, 1040 | "json-schema": { 1041 | "version": "0.2.2", 1042 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz", 1043 | "integrity": "sha1-UDVPGfYDkXxpX3C4Wvp3w7DyNQY=" 1044 | }, 1045 | "verror": { 1046 | "version": "1.3.6", 1047 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", 1048 | "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", 1049 | "requires": { 1050 | "extsprintf": "1.0.2" 1051 | } 1052 | } 1053 | } 1054 | }, 1055 | "sshpk": { 1056 | "version": "1.9.2", 1057 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.9.2.tgz", 1058 | "integrity": "sha1-O0E1G7rVw03fS9gRmTfv7jGkZ2U=", 1059 | "requires": { 1060 | "asn1": "0.2.3", 1061 | "assert-plus": "1.0.0", 1062 | "dashdash": "1.14.0", 1063 | "ecc-jsbn": "0.1.1", 1064 | "getpass": "0.1.6", 1065 | "jodid25519": "1.0.2", 1066 | "jsbn": "0.1.0", 1067 | "tweetnacl": "0.13.3" 1068 | }, 1069 | "dependencies": { 1070 | "asn1": { 1071 | "version": "0.2.3", 1072 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 1073 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" 1074 | }, 1075 | "assert-plus": { 1076 | "version": "1.0.0", 1077 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 1078 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 1079 | }, 1080 | "dashdash": { 1081 | "version": "1.14.0", 1082 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz", 1083 | "integrity": "sha1-KeSGxUGL8PNWA0qZPVFoajPoQUE=", 1084 | "requires": { 1085 | "assert-plus": "1.0.0" 1086 | } 1087 | }, 1088 | "ecc-jsbn": { 1089 | "version": "0.1.1", 1090 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", 1091 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", 1092 | "optional": true, 1093 | "requires": { 1094 | "jsbn": "0.1.0" 1095 | } 1096 | }, 1097 | "getpass": { 1098 | "version": "0.1.6", 1099 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", 1100 | "integrity": "sha1-KD/9n8ElaECHUxHBtg6MQBhxEOY=", 1101 | "requires": { 1102 | "assert-plus": "1.0.0" 1103 | } 1104 | }, 1105 | "jodid25519": { 1106 | "version": "1.0.2", 1107 | "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", 1108 | "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", 1109 | "optional": true, 1110 | "requires": { 1111 | "jsbn": "0.1.0" 1112 | } 1113 | }, 1114 | "jsbn": { 1115 | "version": "0.1.0", 1116 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz", 1117 | "integrity": "sha1-ZQmH2g3XT06/WhE3eiqi0nPpff0=", 1118 | "optional": true 1119 | }, 1120 | "tweetnacl": { 1121 | "version": "0.13.3", 1122 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz", 1123 | "integrity": "sha1-1ii1bzvMPVrnS6nUwacE3vWrS1Y=", 1124 | "optional": true 1125 | } 1126 | } 1127 | } 1128 | } 1129 | }, 1130 | "is-typedarray": { 1131 | "version": "1.0.0", 1132 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 1133 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 1134 | }, 1135 | "isstream": { 1136 | "version": "0.1.2", 1137 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 1138 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 1139 | }, 1140 | "json-stringify-safe": { 1141 | "version": "5.0.1", 1142 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1143 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 1144 | }, 1145 | "mime-types": { 1146 | "version": "2.1.11", 1147 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz", 1148 | "integrity": "sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw=", 1149 | "requires": { 1150 | "mime-db": "1.23.0" 1151 | }, 1152 | "dependencies": { 1153 | "mime-db": { 1154 | "version": "1.23.0", 1155 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz", 1156 | "integrity": "sha1-oxtAcK2uon1zLqMzdApk0OyaZlk=" 1157 | } 1158 | } 1159 | }, 1160 | "node-uuid": { 1161 | "version": "1.4.7", 1162 | "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz", 1163 | "integrity": "sha1-baWhdmjEs91ZYjvaEc9/pMH2Cm8=" 1164 | }, 1165 | "oauth-sign": { 1166 | "version": "0.8.2", 1167 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 1168 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 1169 | }, 1170 | "qs": { 1171 | "version": "6.2.1", 1172 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.1.tgz", 1173 | "integrity": "sha1-zgPF/wk1vB2daanxTL0Y5WjWdiU=" 1174 | }, 1175 | "stringstream": { 1176 | "version": "0.0.5", 1177 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", 1178 | "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" 1179 | }, 1180 | "tough-cookie": { 1181 | "version": "2.3.1", 1182 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.1.tgz", 1183 | "integrity": "sha1-mcd9+7fYBCSeiimdTLD9gf7wg/0=" 1184 | }, 1185 | "tunnel-agent": { 1186 | "version": "0.4.3", 1187 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", 1188 | "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" 1189 | } 1190 | } 1191 | }, 1192 | "request-promise": { 1193 | "version": "4.1.1", 1194 | "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.1.1.tgz", 1195 | "integrity": "sha1-JgIeT29W/Uwwn2vx69jJepWsH7U=", 1196 | "requires": { 1197 | "bluebird": "3.4.1", 1198 | "request-promise-core": "1.1.1", 1199 | "stealthy-require": "1.0.0" 1200 | }, 1201 | "dependencies": { 1202 | "bluebird": { 1203 | "version": "3.4.1", 1204 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz", 1205 | "integrity": "sha1-tzHd9I4t077awudeEhWhG8uR+gc=" 1206 | }, 1207 | "request-promise-core": { 1208 | "version": "1.1.1", 1209 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", 1210 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", 1211 | "requires": { 1212 | "lodash": "4.14.2" 1213 | }, 1214 | "dependencies": { 1215 | "lodash": { 1216 | "version": "4.14.2", 1217 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.14.2.tgz", 1218 | "integrity": "sha1-u8zOY3OkAPv9CoxnykL20e9BZDI=" 1219 | } 1220 | } 1221 | }, 1222 | "stealthy-require": { 1223 | "version": "1.0.0", 1224 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.0.0.tgz", 1225 | "integrity": "sha1-Go7Y/BmotWJo929aGj44MrDCYgA=" 1226 | } 1227 | } 1228 | }, 1229 | "rsa-pem-from-mod-exp": { 1230 | "version": "0.8.4", 1231 | "resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.4.tgz", 1232 | "integrity": "sha1-NipCxtMEBW1JOz8SvOq7LGV2ptQ=" 1233 | }, 1234 | "safe-buffer": { 1235 | "version": "5.1.1", 1236 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 1237 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 1238 | }, 1239 | "sprintf-js": { 1240 | "version": "1.1.1", 1241 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", 1242 | "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" 1243 | }, 1244 | "strformat": { 1245 | "version": "0.0.7", 1246 | "resolved": "https://registry.npmjs.org/strformat/-/strformat-0.0.7.tgz", 1247 | "integrity": "sha1-i2O+wZlXaLuaW8YAdPTN3/BEpMQ=" 1248 | }, 1249 | "topo": { 1250 | "version": "1.1.0", 1251 | "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", 1252 | "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", 1253 | "requires": { 1254 | "hoek": "2.16.3" 1255 | } 1256 | }, 1257 | "underscore": { 1258 | "version": "1.8.3", 1259 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", 1260 | "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" 1261 | }, 1262 | "url-join": { 1263 | "version": "1.1.0", 1264 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz", 1265 | "integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=" 1266 | }, 1267 | "uuid": { 1268 | "version": "3.0.0", 1269 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", 1270 | "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" 1271 | }, 1272 | "xtend": { 1273 | "version": "4.0.1", 1274 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1275 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 1276 | } 1277 | } 1278 | } 1279 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bot-trees", 3 | "version": "1.0.0", 4 | "description": "A sample app for using the bot-graph-dialog node module", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/CatalystCode/bot-trees.git" 13 | }, 14 | "author": "", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/CatalystCode/bot-trees/issues" 18 | }, 19 | "homepage": "https://github.com/CatalystCode/bot-trees#readme", 20 | "dependencies": { 21 | "bot-graph-dialog": "^3.8.4", 22 | "botbuilder": "^3.8.4", 23 | "express": "^4.14.0", 24 | "extend": "^3.0.0", 25 | "jsep": "^0.3.0", 26 | "nconf": "^0.8.4", 27 | "promise": "^7.1.1", 28 | "request": "^2.74.0", 29 | "request-promise": "^4.1.1", 30 | "strformat": "0.0.7", 31 | "underscore": "^1.8.3", 32 | "uuid": "^3.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /proxyMode.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var util = require('util'); 3 | var express = require('express'); 4 | var builder = require('botbuilder'); 5 | var GraphDialog = require('bot-graph-dialog'); 6 | var config = require('./config'); 7 | var fs = require('fs'); 8 | 9 | var port = process.env.PORT || 3978; 10 | var app = express(); 11 | 12 | var microsoft_app_id = config.get('MICROSOFT_APP_ID'); 13 | var microsoft_app_password = config.get('MICROSOFT_APP_PASSWORD'); 14 | 15 | var connector = new builder.ChatConnector({ 16 | appId: microsoft_app_id, 17 | appPassword: microsoft_app_password, 18 | }); 19 | 20 | var bot = new builder.UniversalBot(connector); 21 | var intents = new builder.IntentDialog(); 22 | 23 | var scenariosPath = path.join(__dirname, 'bot', 'scenarios'); 24 | var handlersPath = path.join(__dirname, 'bot', 'handlers'); 25 | 26 | 27 | class ProxyNavigator { 28 | 29 | constructor() { 30 | 31 | this.nodes = [ 32 | { 33 | "type": "prompt", 34 | "data": { "type": "number", "text": "How old are you?" } 35 | }, 36 | { 37 | "type": "prompt", 38 | "data": { "type": "number", "text": "what's your height?" } 39 | }, 40 | { 41 | "type": "sequence", 42 | "steps": [ 43 | { 44 | "type": "text", 45 | "data": { "text": "message 1..." } 46 | }, 47 | { 48 | "type": "text", 49 | "data": { "text": "message 2..." } 50 | }, 51 | { 52 | "type": "text", 53 | "data": { "text": "message 3..." } 54 | }, 55 | { 56 | "type": "prompt", 57 | "data": { "type": "time", "text": "When did it start?" } 58 | } 59 | ] 60 | } 61 | ]; 62 | } 63 | 64 | // returns the current node of the dialog 65 | async getCurrentNode(session) { 66 | console.log(`getCurrentNode, message: ${JSON.stringify(session.message, true, 2)}`); 67 | 68 | var index = session.privateConversationData._currIndex || 0; 69 | var internalIndex = session.privateConversationData._currInternalIndex; 70 | var node = this.nodes[index]; 71 | 72 | if (!isNaN(internalIndex)) { 73 | node = node.steps[internalIndex]; 74 | } 75 | 76 | return node; 77 | }; 78 | 79 | // resolves the next node in the dialog 80 | async getNextNode(session) { 81 | console.log(`getNextNode, message: ${util.inspect(session.message)}`); 82 | //console.log(`result from previous call: ${session.dialogData.data[varname]}`); 83 | 84 | var index = session.privateConversationData._currIndex || 0; 85 | var node = this.nodes[index]; 86 | var internalIndex = session.privateConversationData._currInternalIndex; 87 | if (isNaN(internalIndex) || (node.steps && internalIndex + 1 > node.steps.length - 1)) { 88 | internalIndex = undefined; 89 | index++; 90 | } 91 | 92 | if (index > this.nodes.length -1) { 93 | index = 0; 94 | } 95 | 96 | node = this.nodes[index]; 97 | session.privateConversationData._currIndex = index; 98 | 99 | if (node.steps) { 100 | if (isNaN(internalIndex)) 101 | internalIndex = 0; 102 | else 103 | internalIndex++; 104 | 105 | if (internalIndex > node.steps.length - 1) { 106 | internalIndex = 0; 107 | } 108 | session.privateConversationData._currInternalIndex = internalIndex; 109 | node = node.steps[internalIndex]; 110 | } 111 | else { 112 | delete session.privateConversationData._currInternalIndex; 113 | } 114 | 115 | return node; 116 | }; 117 | } 118 | 119 | process.nextTick(async () => { 120 | var navigator = new ProxyNavigator(); 121 | var graphDialog = await GraphDialog.create({ bot, navigator }); 122 | bot.dialog('/', graphDialog.getDialog()); 123 | console.log(`proxy graph dialog loaded successfully`); 124 | }); 125 | 126 | 127 | app.post('/api/messages', connector.listen()); 128 | 129 | app.listen(port, () => { 130 | console.log('listening on port %s', port); 131 | }); 132 | -------------------------------------------------------------------------------- /router.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var express = require('express'); 3 | var builder = require('botbuilder'); 4 | var GraphDialog = require('bot-graph-dialog'); 5 | var config = require('./config'); 6 | var fs = require('fs'); 7 | 8 | var port = process.env.PORT || 3978; 9 | var app = express(); 10 | 11 | var microsoft_app_id = config.get('MICROSOFT_APP_ID'); 12 | var microsoft_app_password = config.get('MICROSOFT_APP_PASSWORD'); 13 | 14 | var connector = new builder.ChatConnector({ 15 | appId: microsoft_app_id, 16 | appPassword: microsoft_app_password, 17 | }); 18 | 19 | var bot = new builder.UniversalBot(connector); 20 | var intents = new builder.IntentDialog(); 21 | 22 | var scenariosPath = path.join(__dirname, 'bot', 'scenarios'); 23 | var handlersPath = path.join(__dirname, 'bot', 'handlers'); 24 | 25 | bot.dialog('/', intents); 26 | 27 | intents.matches(/^(help|hi|hello)/i, [ 28 | function (session) { 29 | session.send('Hi, how can I help you?'); 30 | } 31 | ]); 32 | 33 | process.nextTick(async () => { 34 | try { 35 | var graphDialog = await GraphDialog.create({ 36 | bot, 37 | scenario: 'router', 38 | loadScenario, 39 | loadHandler, 40 | customTypeHandlers: getCustomTypeHandlers() 41 | }); 42 | 43 | intents.onDefault(graphDialog.getDialog()); 44 | } 45 | catch(err) { 46 | console.error(`error loading dialog: ${err.message}`) 47 | } 48 | }); 49 | 50 | // this allows you to extend the json with more custom node types, 51 | // by providing your implementation to processing each custom type. 52 | // in the end of your implemention you should call the next callbacks 53 | // to allow the framework to continue with the dialog. 54 | // refer to the customTypeStepDemo node in the stomachPain.json scenario for an example. 55 | function getCustomTypeHandlers() { 56 | return [ 57 | { 58 | name: 'myCustomType', 59 | execute: (session, next, data) => { 60 | console.log(`in custom node type handler: customTypeStepDemo, data: ${data.someData}`); 61 | return next(); 62 | } 63 | } 64 | ]; 65 | } 66 | 67 | // this is the handler for loading scenarios from external datasource 68 | // in this implementation we're just reading it from a file 69 | // but it can come from any external datasource like a file, db, etc. 70 | function loadScenario(scenario) { 71 | return new Promise((resolve, reject) => { 72 | console.log('loading scenario', scenario); 73 | // implement loadScenario from external datasource. 74 | // in this example we're loading from local file 75 | var scenarioPath = path.join(scenariosPath, scenario + '.json'); 76 | 77 | return fs.readFile(scenarioPath, 'utf8', (err, content) => { 78 | if (err) { 79 | console.error("error loading json: " + scenarioPath); 80 | return reject(err); 81 | } 82 | 83 | var scenarioObj = JSON.parse(content); 84 | 85 | // simulating long load period 86 | setTimeout(() => { 87 | console.log('resolving scenario', scenarioPath); 88 | resolve(scenarioObj); 89 | }, Math.random() * 3000); 90 | }); 91 | }); 92 | } 93 | 94 | // this is the handler for loading handlers from external datasource 95 | // in this implementation we're just reading it from a file 96 | // but it can come from any external datasource like a file, db, etc. 97 | // 98 | // NOTE: handlers can also be embeded in the scenario json. See scenarios/botGames.json for an example. 99 | function loadHandler(handler) { 100 | return new Promise((resolve, reject) => { 101 | console.log('loading handler', handler); 102 | // implement loadHandler from external datasource. 103 | // in this example we're loading from local file 104 | var handlerPath = path.join(handlersPath, handler); 105 | var handlerString = null; 106 | return fs.readFile(handlerPath, 'utf8', (err, content) => { 107 | if (err) { 108 | console.error("error loading handler: " + handlerPath); 109 | return reject(err); 110 | } 111 | // simulating long load period 112 | setTimeout(() => { 113 | console.log('resolving handler', handler); 114 | resolve(content); 115 | }, Math.random() * 3000); 116 | }); 117 | }); 118 | } 119 | 120 | 121 | app.post('/api/messages', connector.listen()); 122 | 123 | app.listen(port, () => { 124 | console.log('listening on port %s', port); 125 | }); 126 | --------------------------------------------------------------------------------