├── .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 |
--------------------------------------------------------------------------------