diff --git a/.gitignore b/.gitignore index d8b6f209..e9430015 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ /packages/default.gbui/node_modules /tmp /work +/packages/default.gbdialog/bot.js +/packages/default.gbdialog/bot.ts diff --git a/.prettierrc b/.prettierrc index 847c9dba..c1e1114c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { - "trailingComma": "all", + "trailingComma": "none", "tabWidth": 2, - "printWidth": 80, + "printWidth": 120, "arrowParens": "avoid", "semi": true, "singleQuote": true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8725974..1014fb0f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,13 +22,11 @@ When logging a bug, please be sure to include the following: We also accept suggestions in the issue tracker. - In general, things we find useful when reviewing suggestions are: * A description of the problem you're trying to solve * An overview of the suggested solution * Examples of how the suggestion would work in various places - # Instructions for Contributing Code ## Contributing bug fixes diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/LOCALIZATION.md b/LOCALIZATION.md new file mode 100644 index 00000000..4ab1f387 --- /dev/null +++ b/LOCALIZATION.md @@ -0,0 +1,7 @@ + +# Localization in General Bots + +## .gbapp + +The localization is done by adding a strings.ts file to the root of the .gbapp package. + diff --git a/SAMPLES.md b/SAMPLES.md new file mode 100644 index 00000000..e69de29b diff --git a/package-lock.json b/package-lock.json index 4d26d5c4..97c7eda6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1059,40 +1059,56 @@ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, - "@octokit/rest": { - "version": "15.17.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.17.0.tgz", - "integrity": "sha512-tN16FJOGBPxt9QtPfl8yVbbuik3bQ7EI66zcX2XDh05Wcs8t+7mVEE3SWtCeK/Qm0RTLCeFQgGzuvkbD2J6cEg==", + "@octokit/endpoint": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-3.1.1.tgz", + "integrity": "sha512-KPkoTvKwCTetu/UqonLs1pfwFO5HAqTv/Ksp9y4NAg//ZgUCpvJsT4Hrst85uEzJvkB8+LxKyR4Bfv2X8O4cmQ==", "dev": true, "requires": { - "before-after-hook": "^1.1.0", - "btoa-lite": "^1.0.0", - "debug": "^3.1.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.0", - "lodash": "^4.17.4", - "node-fetch": "^2.1.1", - "universal-user-agent": "^2.0.0", + "deepmerge": "3.0.0", + "is-plain-object": "^2.0.4", + "universal-user-agent": "^2.0.1", "url-template": "^2.0.8" + } + }, + "@octokit/request": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-2.2.0.tgz", + "integrity": "sha512-4P9EbwKZ4xfyupVMb3KVuHmM+aO2fye3nufjGKz/qDssvdJj9Rlx44O0FdFvUp4kIzToy3AHLTOulEIDAL+dpg==", + "dev": true, + "requires": { + "@octokit/endpoint": "^3.0.0", + "is-plain-object": "^2.0.4", + "node-fetch": "^2.3.0", + "universal-user-agent": "^2.0.1" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "node-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.1.tgz", - "integrity": "sha512-ObXBpNCD3A/vYQiQtEWl7DuqjAXjfptYFuGHLdPl5U19/6kJuZV+8uMHLrkj3wJrJoyfg4nhgyFixZdaZoAiEQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", + "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==", "dev": true } } }, + "@octokit/rest": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.1.0.tgz", + "integrity": "sha512-/D1XokSycOE+prxxI2r9cxssiLMqcr+BsEUjdruC67puEEjNJjJoRIkuA1b20jOkX5Ue3Rz99Mu9rTnNmjetUA==", + "dev": true, + "requires": { + "@octokit/request": "2.2.0", + "before-after-hook": "^1.2.0", + "btoa-lite": "^1.0.0", + "lodash.get": "^4.4.2", + "lodash.pick": "^4.4.0", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "universal-user-agent": "^2.0.0", + "url-template": "^2.0.8" + } + }, "@semantic-release/changelog": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-3.0.1.tgz", @@ -1243,12 +1259,12 @@ } }, "@semantic-release/github": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.2.1.tgz", - "integrity": "sha512-EVh5MCMOSl5WfOIum+k7fb7ZaDBcZAepPvtMrJOn8HKa9MERK6PgT76OKro+tReWjT1PnGiaKjofjyRC4BhN6Q==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.2.5.tgz", + "integrity": "sha512-myO00q84CyfyzaEZ4OdA7GOMCQKd+juZd5g2Cloh4jV6CyiMyWflZ629RH99wjAVUiwMKnvX2SQ5XPFvO1+FCw==", "dev": true, "requires": { - "@octokit/rest": "^15.13.1", + "@octokit/rest": "^16.0.1", "@semantic-release/error": "^2.2.0", "aggregate-error": "^1.0.0", "bottleneck": "^2.0.1", @@ -1277,9 +1293,9 @@ } }, "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true } } @@ -1638,9 +1654,9 @@ } }, "@types/sequelize": { - "version": "4.27.31", - "resolved": "https://registry.npmjs.org/@types/sequelize/-/sequelize-4.27.31.tgz", - "integrity": "sha512-Toqr7ETevM3uewzG3ALHcnFuKOlj1xSsAebQpBDwIdysGSqc5rRPM3EhKVfUc8/aMWTsHnFySGZ7r3JugLDkMg==", + "version": "4.27.32", + "resolved": "https://registry.npmjs.org/@types/sequelize/-/sequelize-4.27.32.tgz", + "integrity": "sha512-M4iFjZLXDdjqnyKaxz9I5XlVFr4/WBvFYyd8oLU5YsKcXGeppMl6nNo9EBsdDgOOirJcyH2JkJEUhbhegZ6MqA==", "dev": true, "requires": { "@types/bluebird": "*", @@ -2966,9 +2982,9 @@ } }, "bluebird": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", - "integrity": "sha1-G+CQjgVKdRdUVJwnBInBUF1KsVo=" + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" }, "body-parser": { "version": "1.18.3", @@ -2996,37 +3012,107 @@ } }, "botbuilder": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/botbuilder/-/botbuilder-4.1.5.tgz", - "integrity": "sha512-7OZqUqS3jhse2xq9wdG4jaE1mpPcsgQid+wPE2gv17/KwChHAZ5ZyXEmjoc6LP3Twmsapj6h0R4ni36bcst/xg==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botbuilder/-/botbuilder-4.1.7.tgz", + "integrity": "sha512-AlZhvjeqiCpeWGN1TkqBi09l6f0spYIh0Xzc4rJYF8feCFi4k2FEYC1IpiiOAtYhEBeQ9SOGFcUUwPaLmsI3Xg==", "requires": { "@types/filenamify": "^2.0.1", "@types/node": "^9.3.0", "async-file": "^2.0.2", - "botbuilder-core": "^4.1.5", - "botframework-connector": "^4.1.5", + "botbuilder-core": "^4.1.7", + "botframework-connector": "^4.1.7", "filenamify": "^2.0.0", "rimraf": "^2.6.2" }, "dependencies": { + "@types/jsonwebtoken": { + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz", + "integrity": "sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw==", + "requires": { + "@types/node": "*" + } + }, "@types/node": { - "version": "9.6.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.36.tgz", - "integrity": "sha512-Fbw+AdRLL01vv7Rk7bYaNPecqmKoinJHGbpKnDpbUZmUj/0vj3nLqPQ4CNBzr3q2zso6Cq/4jHoCAdH78fvJrw==" + "version": "9.6.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.40.tgz", + "integrity": "sha512-M3HHoXXndsho/sTbQML2BJr7/uwNhMg8P0D4lb+UsM65JQZx268faiz9hKpY4FpocWqpwlLwa8vevw8hLtKjOw==" + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, + "botbuilder-core": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botbuilder-core/-/botbuilder-core-4.1.7.tgz", + "integrity": "sha512-kfNOOpHVDLNdpYVMAefWjETXI4VsnDHgucEfKgANcCUrXmsYETlioHOCngUWLrFcaeVMJodeZvafIYl5NTgy0A==", + "requires": { + "assert": "^1.4.1", + "botframework-schema": "^4.1.7" + } + }, + "botframework-connector": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botframework-connector/-/botframework-connector-4.1.7.tgz", + "integrity": "sha512-aIGY0acc/cMxmg1o+06HqReOjNC8qxbmPJDg+wDgaAwr85bSrDZE1CbyITcj2OqPG/QQ7VM3YzpPAZPlKkDWoQ==", + "requires": { + "@types/jsonwebtoken": "7.2.8", + "@types/node": "^9.3.0", + "@types/request": "^2.47.0", + "base64url": "^3.0.0", + "botframework-schema": "^4.1.7", + "jsonwebtoken": "8.0.1", + "ms-rest-azure-js": "1.0.176", + "ms-rest-js": "1.0.455", + "request": "^2.87.0", + "rsa-pem-from-mod-exp": "^0.8.4" + } + }, + "botframework-schema": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botframework-schema/-/botframework-schema-4.1.7.tgz", + "integrity": "sha512-vPb5gHldmTIpUFx5uCdv/4XEsouMkXvSfQS2zsAC3VqAo29YESHYzNbr5HecRaUveb48NZ27+Djm0U0mLFxe9Q==", + "requires": { + "@types/node": "^9.3.0", + "ms-rest-js": "1.0.455" + } + }, + "ms-rest-azure-js": { + "version": "1.0.176", + "resolved": "https://registry.npmjs.org/ms-rest-azure-js/-/ms-rest-azure-js-1.0.176.tgz", + "integrity": "sha512-qtEBpSf/1nJ0/m1jGLkHISRnpOeHUp5n4SvzZRdFeYnGF4SQx9v/fl8a8ZwEmyujmgbUwyLNM9qKpH5PmW7pZg==", + "requires": { + "ms-rest-js": "^1.0.443", + "tslib": "^1.9.2" + } + }, + "ms-rest-js": { + "version": "1.0.455", + "resolved": "https://registry.npmjs.org/ms-rest-js/-/ms-rest-js-1.0.455.tgz", + "integrity": "sha512-RUDnFFNhk4ZdvOACg0yfaxmp5OzNwUcTIwgh/rVBeuNzgA7hOoVh5zFW06XmOtaBHXL2Bu/vWoQtzloEUlv9tw==", + "requires": { + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + } } } }, "botbuilder-ai": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/botbuilder-ai/-/botbuilder-ai-4.1.5.tgz", - "integrity": "sha512-m9NGPsJ12dHey+/Or7516I4tj8EID1U8W2rnVwn3T9OpRMrL4XM9TloItNBqHhpAy1q7YfglxdsZP5721TMwvg==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botbuilder-ai/-/botbuilder-ai-4.1.7.tgz", + "integrity": "sha512-TYYe7GrR9nZ55gJ+UBkbJnHea/LkJSwCjDuBncmRMsARGwxhYsBhLkkigY1Hzbl6+OuuPwZP/6OR0Xk9JzwgHA==", "requires": { "@microsoft/recognizers-text-date-time": "1.1.2", "@types/html-entities": "^1.2.16", "@types/node": "^9.3.0", "@types/request-promise-native": "^1.0.10", "azure-cognitiveservices-luis-runtime": "^1.0.0", - "botbuilder": "^4.1.5", + "botbuilder": "^4.1.7", "html-entities": "^1.2.1", "moment": "^2.20.1", "ms-rest": "^2.3.6", @@ -3036,29 +3122,29 @@ }, "dependencies": { "@types/node": { - "version": "9.6.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.36.tgz", - "integrity": "sha512-Fbw+AdRLL01vv7Rk7bYaNPecqmKoinJHGbpKnDpbUZmUj/0vj3nLqPQ4CNBzr3q2zso6Cq/4jHoCAdH78fvJrw==" + "version": "9.6.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.40.tgz", + "integrity": "sha512-M3HHoXXndsho/sTbQML2BJr7/uwNhMg8P0D4lb+UsM65JQZx268faiz9hKpY4FpocWqpwlLwa8vevw8hLtKjOw==" } } }, "botbuilder-azure": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/botbuilder-azure/-/botbuilder-azure-4.1.5.tgz", - "integrity": "sha512-3iyapL4wTm3D6QtkkQl4NbV8CtGzZjCetrWPLIrB04QBd6MM1AWIigAyo66KRl52O5/7k0KRpz0ZtIqwK4nRwA==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botbuilder-azure/-/botbuilder-azure-4.1.7.tgz", + "integrity": "sha512-wdq+8Fgm8EscZqsg2lB+JJ4mtukQHOVm+mRpsgjFrYB84/SB0k3Z2wT4PHVNdNdvnSHAWOAXzigIJhPSIv9Hcw==", "requires": { "@types/node": "^9.3.0", "azure-storage": "^2.10.2", - "botbuilder": "^4.1.5", + "botbuilder": "^4.1.7", "documentdb": "1.14.5", "flat": "^4.0.0", "semaphore": "^1.1.0" }, "dependencies": { "@types/node": { - "version": "9.6.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.36.tgz", - "integrity": "sha512-Fbw+AdRLL01vv7Rk7bYaNPecqmKoinJHGbpKnDpbUZmUj/0vj3nLqPQ4CNBzr3q2zso6Cq/4jHoCAdH78fvJrw==" + "version": "9.6.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.40.tgz", + "integrity": "sha512-M3HHoXXndsho/sTbQML2BJr7/uwNhMg8P0D4lb+UsM65JQZx268faiz9hKpY4FpocWqpwlLwa8vevw8hLtKjOw==" } } }, @@ -3227,16 +3313,16 @@ } }, "botbuilder-dialogs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/botbuilder-dialogs/-/botbuilder-dialogs-4.1.5.tgz", - "integrity": "sha512-tLBe+clYg6VWsarR3a6NOzK8RqjJ3mXA4IBsQNSiqxIbEu/jjQTZ8OoZceye5rWfa67cbm2aihShqFB5WZX3nA==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botbuilder-dialogs/-/botbuilder-dialogs-4.1.7.tgz", + "integrity": "sha512-XDR6HT5xJFMrepT5TOjuvDIz0/jpuemi4hGNjW7a0klucfXroSB3yFvg6aVinbjVE5uAejnEwoRAXevFZRg+RQ==", "requires": { "@microsoft/recognizers-text-choice": "1.1.2", "@microsoft/recognizers-text-date-time": "1.1.2", "@microsoft/recognizers-text-number": "1.1.2", "@microsoft/recognizers-text-suite": "1.1.2", "@types/node": "^9.3.0", - "botbuilder-core": "^4.1.5" + "botbuilder-core": "^4.1.7" }, "dependencies": { "@microsoft/recognizers-text-choice": { @@ -3274,9 +3360,40 @@ } }, "@types/node": { - "version": "9.6.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.36.tgz", - "integrity": "sha512-Fbw+AdRLL01vv7Rk7bYaNPecqmKoinJHGbpKnDpbUZmUj/0vj3nLqPQ4CNBzr3q2zso6Cq/4jHoCAdH78fvJrw==" + "version": "9.6.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.40.tgz", + "integrity": "sha512-M3HHoXXndsho/sTbQML2BJr7/uwNhMg8P0D4lb+UsM65JQZx268faiz9hKpY4FpocWqpwlLwa8vevw8hLtKjOw==" + }, + "botbuilder-core": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botbuilder-core/-/botbuilder-core-4.1.7.tgz", + "integrity": "sha512-kfNOOpHVDLNdpYVMAefWjETXI4VsnDHgucEfKgANcCUrXmsYETlioHOCngUWLrFcaeVMJodeZvafIYl5NTgy0A==", + "requires": { + "assert": "^1.4.1", + "botframework-schema": "^4.1.7" + } + }, + "botframework-schema": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/botframework-schema/-/botframework-schema-4.1.7.tgz", + "integrity": "sha512-vPb5gHldmTIpUFx5uCdv/4XEsouMkXvSfQS2zsAC3VqAo29YESHYzNbr5HecRaUveb48NZ27+Djm0U0mLFxe9Q==", + "requires": { + "@types/node": "^9.3.0", + "ms-rest-js": "1.0.455" + } + }, + "ms-rest-js": { + "version": "1.0.455", + "resolved": "https://registry.npmjs.org/ms-rest-js/-/ms-rest-js-1.0.455.tgz", + "integrity": "sha512-RUDnFFNhk4ZdvOACg0yfaxmp5OzNwUcTIwgh/rVBeuNzgA7hOoVh5zFW06XmOtaBHXL2Bu/vWoQtzloEUlv9tw==", + "requires": { + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + } } } }, @@ -3730,9 +3847,9 @@ } }, "bottleneck": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.13.0.tgz", - "integrity": "sha512-9YmZ0aiKta2OAxTujKCS/INjGWCIGWK4Ff1nQpgHnR4CTjlk9jcnpaHOjPnMZPtqRXkqwKdtxZgvJ9udsXylaw==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.13.2.tgz", + "integrity": "sha512-DVS4Uv7xr4Ql0w9valPBaueLRnEtBepeoevDhWO0LBhyihICJ7RySyzPfyvPswanrXAAbWaF8Zx4QpxmIxHa/g==", "dev": true }, "boxen": { @@ -4012,7 +4129,7 @@ "dependencies": { "callsites": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true } @@ -4541,9 +4658,9 @@ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, "commitizen": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-3.0.4.tgz", - "integrity": "sha512-djR5F7RBsGALyUEm/B1H/85nsN4L1F5DhWN+9/efSwqHDSyhw2MK6MF2VRuD26PUqGkQbcUlYO61btkTWjcjVw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-3.0.5.tgz", + "integrity": "sha512-WB9sz7qudArOsW1ninU8YGLNoXLQ5lJBZf538iQ7i96SXAkqVMZdmPtSyN4WFPM5PjQR7rWxDa+hzfGIJfrXUg==", "dev": true, "requires": { "cachedir": "2.1.0", @@ -4657,7 +4774,7 @@ }, "shelljs": { "version": "0.7.6", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.6.tgz", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.7.6.tgz", "integrity": "sha1-N5zM+1a5HIYB5HkzVutTgpJN6a0=", "dev": true, "requires": { @@ -5061,9 +5178,9 @@ } }, "csv-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.0.0.tgz", - "integrity": "sha512-M+cHI0SCXFGR0ZZWs7a6P14lXjrje5TOUpjkRQF9+Rauji8C4XOZpCyR/ENzH6uVqs+MTls00YE9IiI6Ao9Vrw==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.0.1.tgz", + "integrity": "sha512-ehkwejEj05wwO7Q9JD+YSI6dNMIauHIroNU1RALrmRrqPoZIwRnfBtgq5GkU6i2RxZOJqjo3dtI1NrVSXvaimA==" }, "csv-parser": { "version": "1.12.1", @@ -5332,6 +5449,12 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "deepmerge": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.0.0.tgz", + "integrity": "sha512-a8z8bkgHsAML+uHLqmMS83HHlpy3PvZOOuiTQqaa3wu8ZVg3h0hqHk6aCsGdOnZV2XMM/FRimNGjUh0KCcmHBw==", + "dev": true + }, "defer-to-connect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.1.tgz", @@ -5841,9 +5964,9 @@ } }, "env-ci": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-3.1.0.tgz", - "integrity": "sha512-+yFT8QX8W9bee0/fuzKvqt2vEhWvU3FXZ1P5xbveDq/EqcYLh4e0QNE16okUS3pawALDGXE9eCJPVeWY0/ilQA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-3.1.2.tgz", + "integrity": "sha512-qJ+ug5OEHEK6HyjhEB0z2tPJCmdvemQE3WUUYEe7qj7teZIJGjZK9elWB4kxE8qRdVHWl4aBvyVmX0Y5xlMbBw==", "dev": true, "requires": { "execa": "^1.0.0", @@ -6362,7 +6485,7 @@ }, "expand-range": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "resolved": "http://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { @@ -8164,9 +8287,9 @@ } }, "hook-std": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-1.1.0.tgz", - "integrity": "sha512-aIyBZbZl3NS8XoSwIDQ+ZaiBuPOhhPWoBFA3QX0Q8hOMO8Tx4xGRTDnn/nl/LAtZWdieXzFC9ohAtTSnWrlHCQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-1.2.0.tgz", + "integrity": "sha512-yntre2dbOAjgQ5yoRykyON0D9T96BfshR8IuiL/r3celeHD8I/76w4qo8m3z99houR4Z678jakV3uXrQdSvW/w==", "dev": true }, "hosted-git-info": { @@ -9009,9 +9132,9 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "issue-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-3.0.0.tgz", - "integrity": "sha512-VWIhBdy0eOhlvpxOOMecBCHMpjx7lWVZcYpSzjD4dSdxptzI9TBR/cQEh057HL8+7jQKTLs+uCtezY/9VoveCA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-3.0.1.tgz", + "integrity": "sha512-5wdT3EE8Kq38x/hJD8QZCJ9scGoOZ5QnzwXyClkviSWTS+xOCE6hJ0qco3H5n5jCsFqpbofZCcMWqlXJzF72VQ==", "dev": true, "requires": { "lodash.capitalize": "^4.2.1", @@ -9553,6 +9676,12 @@ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -9641,6 +9770,18 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -9668,6 +9809,12 @@ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", "dev": true }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, "lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -9781,9 +9928,9 @@ } }, "macos-release": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-1.1.0.tgz", - "integrity": "sha512-mmLbumEYMi5nXReB9js3WGsB8UE6cDBWyIO62Z4DNx6GbRhDxHNjA1MlzSpJ2S2KM1wyiPRA0d19uHWYYvMHjA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.0.0.tgz", + "integrity": "sha512-iCM3ZGeqIzlrH7KxYK+fphlJpCCczyHXc+HhRVbEu9uNTCrzYJjvvtefzeKTCVHd5AP/aD/fzC80JZ4ZP+dQ/A==", "dev": true }, "make-dir": { @@ -14928,6 +15075,12 @@ "isobject": "^3.0.1" } }, + "octokit-pagination-methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", + "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -15020,13 +15173,13 @@ } }, "os-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-2.0.1.tgz", - "integrity": "sha1-uaOGNhwXrjohc27wWZQFyajF3F4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.0.0.tgz", + "integrity": "sha512-7c74tib2FsdFbQ3W+qj8Tyd1R3Z6tuVRNNxXjJcZ4NgjIEQU9N/prVMqcW29XZPXGACqaXN3jq58/6hoaoXH6g==", "dev": true, "requires": { - "macos-release": "^1.0.0", - "win-release": "^1.0.0" + "macos-release": "^2.0.0", + "windows-release": "^3.1.0" } }, "os-tmpdir": { @@ -16407,9 +16560,9 @@ "integrity": "sha1-Az1go60g7PLgCUDRT5eCNGV3QzU=" }, "semantic-release": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-15.12.0.tgz", - "integrity": "sha512-s8JQ0twxSTat4aIKy8AVPm6gP+2OlkMyyTGeeWFopOXQVa3Bg0LPNjdhsUL90MHvh9aVy9k0/ym4DWmOcGxOMQ==", + "version": "15.12.4", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-15.12.4.tgz", + "integrity": "sha512-po30Te9E26v3Qb/G9pXFO6lCTFO07zvliqH00vmfuCoAjl1Wpg9SKb9dXVFM7BTdg5Fr/KqbdOsqHkT7kR4FeQ==", "dev": true, "requires": { "@semantic-release/commit-analyzer": "^6.1.0", @@ -16657,9 +16810,9 @@ } }, "yargs": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.4.tgz", - "integrity": "sha512-f5esswlPO351AnejaO2A1ZZr0zesz19RehQKwiRDqWtrraWrJy16tsUIKgDXFMVytvNOHPVmTiaTh3wO67I0fQ==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { "cliui": "^4.0.0", @@ -16673,13 +16826,13 @@ "string-width": "^2.0.0", "which-module": "^2.0.0", "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.0" + "yargs-parser": "^11.1.1" } }, "yargs-parser": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.0.tgz", - "integrity": "sha512-lGA5HsbjkpCfekDBHAhgE5OE8xEoqiUDylowr+BvhRCwG1xVYTsd8hx2CYC0NY4k9RIgJeybFTG2EZW4P2aN1w==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -18006,9 +18159,9 @@ } }, "tapable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz", - "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", "dev": true }, "tar": { @@ -18589,9 +18742,9 @@ "integrity": "sha1-pZUhTHKY24M57u7gg+TRC9jLjdk=" }, "ts-loader": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.0.tgz", - "integrity": "sha512-lGSNs7szRFj/rK9T1EQuayE3QNLg6izDUxt5jpmq0RG1rU2bapAt7E7uLckLCUPeO1jwxCiet2oRaWovc53UAg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.1.tgz", + "integrity": "sha512-fDDgpBH3SR8xlt2MasLdz3Yy611PQ/UY/KGyo7TgXhTRU/6sS8uGG0nJYnU1OdFBNKcoYbId1UTNaAOUn+i41g==", "dev": true, "requires": { "chalk": "^2.3.0", @@ -18760,6 +18913,11 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz", "integrity": "sha1-mtLCp6F5HxCoUuARL3e1cdzhDGY=" + }, + "typescript": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==" } } }, @@ -18774,17 +18932,17 @@ "integrity": "sha512-/VMawTW4NnUUsgq0o8O37y9MmXFaOCDrH1dvDg7SZUS5ZSpUPSILVWwGJP+7g4I8vKZ5bBKZKHfPIEA4xUC+PQ==" }, "typedoc-plugin-markdown": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-1.1.18.tgz", - "integrity": "sha512-hE/2wM3NzMY/6K2wLtW7koVALLvVYm7w0mx+GFH3Er5gTioEMrUtd70Yf6Mb88fG6IvUWx1mX10ZD/ZR0AXS2w==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-1.1.19.tgz", + "integrity": "sha512-dk1J9NHW3c1cMmcmTrw55U7ke6zadB3hZL7y7tkLQ6FeJGkz6SEMuexjVOq5rltuW7JeLqt1ngNKAwG9dXiMgQ==", "requires": { "turndown": "^5.0.1" } }, "typescript": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", - "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.1.tgz", + "integrity": "sha512-jw7P2z/h6aPT4AENXDGjcfHTu5CSqzsbZc6YlUIebTyBAq8XaKp78x7VcSh30xwSCcsu5irZkYZUSFP1MrAMbg==" }, "typescript-eslint-parser": { "version": "16.0.1", @@ -18933,12 +19091,12 @@ } }, "universal-user-agent": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.0.1.tgz", - "integrity": "sha512-vz+heWVydO0iyYAa65VHD7WZkYzhl7BeNVy4i54p4TF8OMiLSXdbuQe4hm+fmWAsL+rVibaQHXfhvkw3c1Ws2w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.0.2.tgz", + "integrity": "sha512-nOwvHWLH3dBazyuzbECPA5uVFNd7AlgviXRHgR4yf48QqitIvpdncRrxMbZNMpPPEfgz30I9ubd1XmiJiqsTrg==", "dev": true, "requires": { - "os-name": "^2.0.1" + "os-name": "^3.0.0" } }, "universalify": { @@ -19169,6 +19327,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "vbscript-to-typescript": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/vbscript-to-typescript/-/vbscript-to-typescript-1.0.8.tgz", + "integrity": "sha512-vjElTpd4EVxUoxeGrRId4hetHdlyxIRicxkZxdErFfLJ9X7xyw0HEboXZO+s8prIC7s1eh0ulkuVbencrv6fAQ==" + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -19261,9 +19424,9 @@ "integrity": "sha1-/IBORYzEYACbGiuWa8iBfSV4rvs=" }, "whatwg-mimetype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", - "integrity": "sha512-5YSO1nMd5D1hY3WzAQV3PzZL83W3YeyR1yW9PcH26Weh1t+Vzh9B6XkDh7aXm83HBZ4nSMvkjvN2H2ySWIvBgw==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" }, "whatwg-url": { "version": "6.5.0", @@ -19338,13 +19501,30 @@ } } }, - "win-release": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz", - "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=", + "windows-release": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.1.0.tgz", + "integrity": "sha512-hBb7m7acFgQPQc222uEQTmdcGLeBmQLNLFIh0rDk3CwFOBrfjefLzEfEfmpMq8Af/n/GnFf3eYf203FY1PmudA==", "dev": true, "requires": { - "semver": "^5.0.1" + "execa": "^0.10.0" + }, + "dependencies": { + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "winston": { diff --git a/package.json b/package.json index 07434066..1ae57a87 100644 --- a/package.json +++ b/package.json @@ -58,19 +58,20 @@ "azure-arm-search": "^1.3.0-preview", "azure-arm-sql": "5.6.0", "azure-arm-website": "5.7.0", + "bluebird": "^3.5.3", "body-parser": "1.18.3", - "botbuilder": "^4.1.5", - "botbuilder-ai": "^4.1.5", - "botbuilder-azure": "^4.1.5", + "botbuilder": "^4.1.7", + "botbuilder-ai": "^4.1.7", + "botbuilder-azure": "^4.1.7", "botbuilder-choices": "^4.0.0-preview1.2", - "botbuilder-dialogs": "^4.1.5", + "botbuilder-dialogs": "^4.1.7", "botbuilder-prompts": "^4.0.0-preview1.2", "botlib": "^0.1.8", "chai": "4.2.0", "child_process": "^1.0.2", "chokidar": "2.0.4", "cli-spinner": "^0.2.8", - "csv-parse": "4.0.0", + "csv-parse": "4.0.1", "dotenv-extended": "2.3.0", "express": "4.16.4", "express-promise-router": "3.0.3", @@ -104,9 +105,10 @@ "ts-node": "7.0.1", "typedoc": "0.13.0", "typedoc-plugin-external-module-name": "^1.1.3", - "typedoc-plugin-markdown": "^1.1.18", - "typescript": "3.1.6", + "typedoc-plugin-markdown": "^1.1.19", + "typescript": "3.2.1", "url-join": "4.0.0", + "vbscript-to-typescript": "^1.0.8", "wait-until": "0.0.2", "walk-promise": "0.2.0", "winston": "3.1.0" @@ -115,12 +117,12 @@ "@semantic-release/changelog": "^3.0.1", "@semantic-release/commit-analyzer": "^6.1.0", "@semantic-release/git": "^7.0.5", - "@semantic-release/github": "^5.2.1", + "@semantic-release/github": "^5.2.5", "@semantic-release/npm": "^5.1.1", "@semantic-release/release-notes-generator": "^7.1.4", "@types/chai": "4.1.7", "@types/mocha": "5.2.5", - "@types/sequelize": "4.27.31", + "@types/sequelize": "4.27.32", "@types/url-join": "0.8.2", "@types/winston": "2.4.4", "ban-sensitive-files": "1.9.2", @@ -134,10 +136,10 @@ "nsp": "3.2.1", "pre-git": "3.17.1", "prettier-standard": "8.0.1", - "semantic-release": "^15.12.0", + "semantic-release": "^15.12.4", "standard": "12.0.1", "travis-deploy-once": "5.0.9", - "ts-loader": "^5.3.0", + "ts-loader": "^5.3.1", "tslint": "^5.11.0", "tslint-microsoft-contrib": "^6.0.0" }, diff --git a/packages/admin.gbapp/dialogs/AdminDialog.ts b/packages/admin.gbapp/dialogs/AdminDialog.ts index eb81edef..6b98bcec 100644 --- a/packages/admin.gbapp/dialogs/AdminDialog.ts +++ b/packages/admin.gbapp/dialogs/AdminDialog.ts @@ -141,7 +141,6 @@ export class AdminDialog extends IGBDialog { return await step.replaceDialog('/admin', { firstRun: false }); } else if (cmdName === 'addConnection') { await AdminDialog.addConnectionCommand(min, text); - return await step.replaceDialog('/admin', { firstRun: false }); } else if (cmdName === 'undeployPackage') { await AdminDialog.undeployPackageCommand(text, min); diff --git a/packages/analytics.gblib/index.ts b/packages/analytics.gblib/index.ts index 63ba890b..1c29bb77 100644 --- a/packages/analytics.gblib/index.ts +++ b/packages/analytics.gblib/index.ts @@ -43,22 +43,11 @@ import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { Sequelize } from 'sequelize-typescript'; export class GBAnalyticsPackage implements IGBPackage { - public sysPackages: IGBPackage[] = null; - public loadPackage(core: IGBCoreService, sequelize: Sequelize): void { - - } - public unloadPackage(core: IGBCoreService): void { - - } - public loadBot(min: GBMinInstance): void { - - } - public unloadBot(min: GBMinInstance): void { - - } - public onNewSession(min: GBMinInstance, step: any): void { - - } + public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {} + public unloadPackage(core: IGBCoreService): void {} + public loadBot(min: GBMinInstance): void {} + public unloadBot(min: GBMinInstance): void {} + public onNewSession(min: GBMinInstance, step: any): void {} } diff --git a/packages/boot.gbot/package.json b/packages/boot.gbot/package.json new file mode 100644 index 00000000..ef064f8a --- /dev/null +++ b/packages/boot.gbot/package.json @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "theme": "default.gbtheme", + "ui": "default.gbui", + "kb": "default.gbkb", + "title": "Default General Bot", + "description": "Default General Bot", + "whoAmIVideo": "TODO.mp4", + "author": "pragmatismo.io", + "license": "AGPL", + "engineName": "guaribas-1.0.0" +} \ No newline at end of file diff --git a/packages/boot.gbot/security.json b/packages/boot.gbot/security.json new file mode 100644 index 00000000..41ce2ba0 --- /dev/null +++ b/packages/boot.gbot/security.json @@ -0,0 +1,3 @@ +{ + "groups": [{}] +} diff --git a/packages/boot.gbot/services.json b/packages/boot.gbot/services.json new file mode 100644 index 00000000..2c63c085 --- /dev/null +++ b/packages/boot.gbot/services.json @@ -0,0 +1,2 @@ +{ +} diff --git a/packages/boot.gbot/settings.json b/packages/boot.gbot/settings.json new file mode 100644 index 00000000..493a65bf --- /dev/null +++ b/packages/boot.gbot/settings.json @@ -0,0 +1,6 @@ +{ + "enabledAdmin": "true", + "searchScore": ".15", + "nlpScore": ".15", + "nlpVsSearch": ".4" +} diff --git a/packages/core.gbapp/dialogs/WelcomeDialog.ts b/packages/core.gbapp/dialogs/WelcomeDialog.ts index b4dcfe75..ced210bb 100644 --- a/packages/core.gbapp/dialogs/WelcomeDialog.ts +++ b/packages/core.gbapp/dialogs/WelcomeDialog.ts @@ -80,6 +80,7 @@ export class WelcomeDialog extends IGBDialog { await step.replaceDialog('/answer', { query: step.context.activity.text }); } } + return await step.next(); } ])); diff --git a/packages/core.gbapp/dialogs/WhoAmIDialog.ts b/packages/core.gbapp/dialogs/WhoAmIDialog.ts index d9e599b2..20d7162e 100644 --- a/packages/core.gbapp/dialogs/WhoAmIDialog.ts +++ b/packages/core.gbapp/dialogs/WhoAmIDialog.ts @@ -64,6 +64,7 @@ export class WhoAmIDialog extends IGBDialog { } await step.replaceDialog('/ask', { isReturning: true }); + return await step.next(); } ])); diff --git a/packages/core.gbapp/services/GBAPIService.ts b/packages/core.gbapp/services/GBAPIService.ts new file mode 100644 index 00000000..118724e8 --- /dev/null +++ b/packages/core.gbapp/services/GBAPIService.ts @@ -0,0 +1,75 @@ +/*****************************************************************************\ +| ( )_ _ | +| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ | +| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ | +| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) | +| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' | +| | | ( )_) | | +| (_) \___/' | +| | +| General Bots Copyright (c) Pragmatismo.io. All rights reserved. | +| Licensed under the AGPL-3.0. | +| | +| According to our dual licensing model, this program can be used either | +| under the terms of the GNU Affero General Public License, version 3, | +| or under a proprietary license. | +| | +| The texts of the GNU Affero General Public License with an additional | +| permission and of our proprietary license can be found at and | +| in the LICENSE file you have received along with this program. | +| | +| This program is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY, without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| GNU Affero General Public License for more details. | +| | +| "General Bots" is a registered trademark of Pragmatismo.io. | +| The licensing of the program under the AGPLv3 does not imply a | +| trademark license. Therefore any rights, title and interest in | +| our trademarks remain entirely with us. | +| | +\*****************************************************************************/ + +'use strict'; + +import { TurnContext } from 'botbuilder'; +import { WaterfallStepContext } from 'botbuilder-dialogs'; +import { GBMinInstance } from 'botlib'; +const WaitUntil = require('wait-until'); + +/** + * @fileoverview General Bots server core. + */ + +export class DialogClass { + public min: GBMinInstance; + public context: TurnContext; + public step: WaterfallStepContext; + + constructor(min: GBMinInstance) { + this.min = min; + } + + public async hear(cb) { + let idCallback = Math.floor(Math.random() * 1000000000000); + this.min.cbMap[idCallback] = cb; + await this.step.beginDialog('/hear', { id: idCallback}); + } + + public async talk(text: string) { + return await this.context.sendActivity(text); + } + + /** + * Generic function to call any REST API. + */ + public sendEmail(to, subject, body) { + // tslint:disable-next-line:no-console + console.log(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`); + } + + /** + * Generic function to call any REST API. + */ + public post(url: string, data) {} +} diff --git a/packages/core.gbapp/services/GBConversationalService.ts b/packages/core.gbapp/services/GBConversationalService.ts index 0eda2815..d062353a 100644 --- a/packages/core.gbapp/services/GBConversationalService.ts +++ b/packages/core.gbapp/services/GBConversationalService.ts @@ -31,18 +31,15 @@ \*****************************************************************************/ /** - * @fileoverview General Bots server core. + * @fileoverview Conversation handling and external service calls. */ 'use strict'; const logger = require('../../../src/logger'); - -import { any } from 'bluebird'; import { MessageFactory } from 'botbuilder'; import { LuisRecognizer } from 'botbuilder-ai'; -import { IGBConversationalService } from 'botlib'; -import { GBMinInstance } from 'botlib'; +import { GBMinInstance, IGBConversationalService } from 'botlib'; import { AzureText } from 'pragmatismo-io-framework'; import { Messages } from '../strings'; import { GBCoreService } from './GBCoreService'; @@ -70,33 +67,27 @@ export class GBConversationalService implements IGBConversationalService { msg.value = value; msg.type = 'event'; msg.name = name; + return step.context.sendActivity(msg); } } - public async sendSms( - min: GBMinInstance, - mobile: string, - text: string - ): Promise { - return new Promise((resolve: any, reject: any): any => { - const nexmo = new Nexmo({ - apiKey: min.instance.smsKey, - apiSecret: min.instance.smsSecret - }); - nexmo.message.sendSms( - min.instance.smsServiceNumber, - mobile, - text, - (err, data) => { + public async sendSms(min: GBMinInstance, mobile: string, text: string): Promise { + return new Promise( + (resolve: any, reject: any): any => { + const nexmo = new Nexmo({ + apiKey: min.instance.smsKey, + apiSecret: min.instance.smsSecret + }); + nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, (err, data) => { if (err) { reject(err); } else { resolve(data); } - } - ); - }); + }); + } + ); } public async routeNLP(step: any, min: GBMinInstance, text: string): Promise { @@ -112,15 +103,17 @@ export class GBConversationalService implements IGBConversationalService { try { nlp = await model.recognize(step.context); } catch (error) { - if (error.statusCode == 404) { - logger.warn ('NLP application still not publish and there are no other options for answering.'); + if (error.statusCode === 404) { + logger.warn('NLP application still not publish and there are no other options for answering.'); + return Promise.resolve(false); } else { - const msg = `Error calling NLP server, check if you have a published model and assigned keys on the service. Error: ${ - error.statusCode ? error.statusCode : '' - } ${error.message}`; - return Promise.reject(new Error(msg)); } + const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${ + error.statusCode ? error.statusCode : '' + } ${error.message}`; + return Promise.reject(new Error(msg)); + } } // Resolves intents returned from LUIS. @@ -128,37 +121,31 @@ export class GBConversationalService implements IGBConversationalService { const topIntent = LuisRecognizer.topIntent(nlp); if (topIntent) { const intent = topIntent; - const entity = - nlp.entities && nlp.entities.length > 0 - ? nlp.entities[0].entity.toUpperCase() - : null; + const entity = nlp.entities && nlp.entities.length > 0 ? nlp.entities[0].entity.toUpperCase() : null; if (intent === 'None') { return Promise.resolve(false); } - logger.info('NLP called:' + intent + ', ' + entity); + logger.info(`NLP called: ${intent}, ${entity}`); try { - await step.replace('/' + intent, nlp.entities); + await step.replace(`/${intent}`, nlp.entities); + return Promise.resolve(true); } catch (error) { - const msg = `Error finding dialog associated to NLP event: ${intent}: ${ - error.message - }`; + const msg = `Error finding dialog associated to NLP event: ${intent}: ${error.message}`; + return Promise.reject(new Error(msg)); } } + return Promise.resolve(false); } public async checkLanguage(step, min, text) { - const locale = await AzureText.getLocale( - min.instance.textAnalyticsKey, - min.instance.textAnalyticsEndpoint, - text - ); - if (locale != step.context.activity.locale.split('-')[0]) { + const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, text); + if (locale !== step.context.activity.locale.split('-')[0]) { switch (locale) { case 'pt': step.context.activity.locale = 'pt-BR'; diff --git a/packages/core.gbapp/services/GBCoreService.ts b/packages/core.gbapp/services/GBCoreService.ts index 6043c892..95b4cf86 100644 --- a/packages/core.gbapp/services/GBCoreService.ts +++ b/packages/core.gbapp/services/GBCoreService.ts @@ -36,14 +36,24 @@ 'use strict'; -import { IGBCoreService, IGBInstance } from 'botlib'; +import { IGBCoreService, IGBInstance, IGBPackage } from 'botlib'; import * as fs from 'fs'; -import processExists = require('process-exists'); import { Sequelize } from 'sequelize-typescript'; -const logger = require('../../../src/logger'); +import { GBAdminPackage } from '../../admin.gbapp/index'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; +import { GBAnalyticsPackage } from '../../analytics.gblib'; +import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService'; +import { GBCorePackage } from '../../core.gbapp'; +import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp'; +import { GBKBPackage } from '../../kb.gbapp'; +import { GBSecurityPackage } from '../../security.gblib'; +import { GBWhatsappPackage } from '../../whatsapp.gblib/index'; import { GuaribasInstance } from '../models/GBModel'; import { GBConfigService } from './GBConfigService'; +import { GBImporter } from './GBImporterService'; + +const logger = require('../../../src/logger'); +const opn = require('opn'); /** * Core service layer. @@ -67,11 +77,7 @@ export class GBCoreService implements IGBCoreService { /** * Custom create table query. */ - private createTableQuery: ( - tableName: string, - attributes: any, - options: any - ) => string; + private createTableQuery: (tableName: string, attributes: any, options: any) => string; /** * Custom change column query. @@ -93,78 +99,79 @@ export class GBCoreService implements IGBCoreService { /** * Gets database config and connect to storage. */ - public async initDatabase(): Promise { - return new Promise( - (resolve: any, reject: any): any => { - try { - this.dialect = GBConfigService.get('STORAGE_DIALECT'); - let host: string | undefined; - let database: string | undefined; - let username: string | undefined; - let password: string | undefined; - let storage: string | undefined; + public async initStorage(): Promise { + this.dialect = GBConfigService.get('STORAGE_DIALECT'); - if (this.dialect === 'mssql') { - host = GBConfigService.get('STORAGE_SERVER'); - database = GBConfigService.get('STORAGE_NAME'); - username = GBConfigService.get('STORAGE_USERNAME'); - password = GBConfigService.get('STORAGE_PASSWORD'); - } else if (this.dialect === 'sqlite') { - storage = GBConfigService.get('STORAGE_STORAGE'); - } else { - reject(`Unknown dialect: ${this.dialect}.`); + let host: string | undefined; + let database: string | undefined; + let username: string | undefined; + let password: string | undefined; + let storage: string | undefined; + + if (this.dialect === 'mssql') { + host = GBConfigService.get('STORAGE_SERVER'); + database = GBConfigService.get('STORAGE_NAME'); + username = GBConfigService.get('STORAGE_USERNAME'); + password = GBConfigService.get('STORAGE_PASSWORD'); + } else if (this.dialect === 'sqlite') { + storage = GBConfigService.get('STORAGE_STORAGE'); + } else { + throw new Error(`Unknown dialect: ${this.dialect}.`); + } + + const logging: any = + GBConfigService.get('STORAGE_LOGGING') === 'true' + ? (str: string): void => { + logger.info(str); } + : false; - const logging: any = - GBConfigService.get('STORAGE_LOGGING') === 'true' - ? (str: string): void => { - logger.info(str); - } - : false; + const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true'; - const encrypt: boolean = - GBConfigService.get('STORAGE_ENCRYPT') === 'true'; - - this.sequelize = new Sequelize({ - host: host, - database: database, - username: username, - password: password, - logging: logging, - operatorsAliases: false, - dialect: this.dialect, - storage: storage, - dialectOptions: { - encrypt: encrypt - }, - pool: { - max: 32, - min: 8, - idle: 40000, - evict: 40000, - acquire: 40000 - } - }); - - if (this.dialect === 'mssql') { - this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator; - this.createTableQuery = this.queryGenerator.createTableQuery; - this.queryGenerator.createTableQuery = ( - tableName, - attributes, - options - ) => this.createTableQueryOverride(tableName, attributes, options); - this.changeColumnQuery = this.queryGenerator.changeColumnQuery; - this.queryGenerator.changeColumnQuery = (tableName, attributes) => - this.changeColumnQueryOverride(tableName, attributes); - } - resolve(); - } catch (error) { - reject(error); - } + this.sequelize = new Sequelize({ + host: host, + database: database, + username: username, + password: password, + logging: logging, + operatorsAliases: false, + dialect: this.dialect, + storage: storage, + dialectOptions: { + encrypt: encrypt + }, + pool: { + max: 32, + min: 8, + idle: 40000, + evict: 40000, + acquire: 40000 } - ); + }); + + if (this.dialect === 'mssql') { + this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator; + this.createTableQuery = this.queryGenerator.createTableQuery; + this.queryGenerator.createTableQuery = (tableName, attributes, options) => + this.createTableQueryOverride(tableName, attributes, options); + this.changeColumnQuery = this.queryGenerator.changeColumnQuery; + this.queryGenerator.changeColumnQuery = (tableName, attributes) => + this.changeColumnQueryOverride(tableName, attributes); + } + } + + public async checkStorage(azureDeployer: AzureDeployerService) { + try { + await this.sequelize.authenticate(); + } catch (error) { + logger.info('Opening storage firewall on infrastructure...'); + if (error.parent.code === 'ELOGIN') { + await this.openStorageFrontier(azureDeployer); + } else { + throw error; + } + } } public async syncDatabaseStructure() { @@ -172,12 +179,13 @@ export class GBCoreService implements IGBCoreService { const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true'; const force = GBConfigService.get('STORAGE_SYNC_FORCE') === 'true'; logger.info('Syncing database...'); + return this.sequelize.sync({ alter: alter, force: force }); } else { - const msg = 'Database synchronization is disabled.'; + const msg = `Database synchronization is disabled.`; logger.info(msg); } } @@ -194,6 +202,7 @@ export class GBCoreService implements IGBCoreService { */ public async loadInstanceById(instanceId: string): Promise { const options = { where: { instanceId: instanceId } }; + return GuaribasInstance.findOne(options); } @@ -202,63 +211,183 @@ export class GBCoreService implements IGBCoreService { */ public async loadInstance(botId: string): Promise { const options = { where: {} }; + options.where = { botId: botId }; - if (botId !== '[default]') { - options.where = { botId: botId }; - } - - return GuaribasInstance.findOne(options); + return await GuaribasInstance.findOne(options); } public async writeEnv(instance: IGBInstance) { - const env = - `ADDITIONAL_DEPLOY_PATH=\n` + - `ADMIN_PASS=${instance.adminPass}\n` + - `CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}\n` + - `CLOUD_LOCATION=${instance.cloudLocation}\n` + - `CLOUD_GROUP=${instance.botId}\n` + - `CLOUD_USERNAME=${instance.cloudUsername}\n` + - `CLOUD_PASSWORD=${instance.cloudPassword}\n` + - `MARKETPLACE_ID=${instance.marketplaceId}\n` + - `MARKETPLACE_SECRET=${instance.marketplacePassword}\n` + - `NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}\n` + - `STORAGE_DIALECT=${instance.storageDialect}\n` + - `STORAGE_SERVER=${instance.storageServer}.database.windows.net\n` + - `STORAGE_NAME=${instance.storageName}\n` + - `STORAGE_USERNAME=${instance.storageUsername}\n` + - `STORAGE_PASSWORD=${instance.storagePassword}\n` + - `STORAGE_SYNC=true\n`; + const env = `ADDITIONAL_DEPLOY_PATH= + ADMIN_PASS=${instance.adminPass} + CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId} + CLOUD_LOCATION=${instance.cloudLocation} + CLOUD_GROUP=${instance.botId} + CLOUD_USERNAME=${instance.cloudUsername} + CLOUD_PASSWORD=${instance.cloudPassword} + MARKETPLACE_ID=${instance.marketplaceId} + MARKETPLACE_SECRET=${instance.marketplacePassword} + NLP_AUTHORING_KEY=${instance.nlpAuthoringKey} + STORAGE_DIALECT=${instance.storageDialect} + STORAGE_SERVER=${instance.storageServer}.database.windows.net + STORAGE_NAME=${instance.storageName} + STORAGE_USERNAME=${instance.storageUsername} + STORAGE_PASSWORD=${instance.storagePassword} + STORAGE_SYNC=true`; fs.writeFileSync('.env', env); } public async ensureProxy(port): Promise { - let proxyAddress: string; - const ngrok = require('ngrok'); - return await ngrok.connect({ port: port }); + try { + const ngrok = require('ngrok'); + return await ngrok.connect({ port: port }); + } catch (error) { + // There are false positive from ngrok regarding to no memory, but it's just + // lack of connection. + logger.verbose(error); + throw new Error('Error connecting to remote ngrok server, please check network connection.'); + } + } + + public async saveInstance(fullInstance: any) { + const options = { where: {} }; + options.where = { botId: fullInstance.botId }; + let instance = await GuaribasInstance.findOne(options); + // tslint:disable-next-line:prefer-object-spread + instance = Object.assign(instance, fullInstance); + + return await instance.save(); + } + + /** + * Loads all bot instances from object storage, if it's formatted. + * + * @param core + * @param azureDeployer + * @param proxyAddress + */ + public async loadAllInstances(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) { + logger.info(`Loading instances from storage...`); + let instances: GuaribasInstance[]; + try { + instances = await core.loadInstances(); + const instance = instances[0]; + if (process.env.NODE_ENV === 'development') { + logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`); + await azureDeployer.updateBotProxy( + instance.botId, + instance.botId, + `${proxyAddress}/api/messages/${instance.botId}` + ); + } + } catch (error) { + // Check if storage is empty and needs formatting. + const isInvalidObject = error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE. + if (isInvalidObject) { + if (GBConfigService.get('STORAGE_SYNC') != 'true') { + throw new Error( + `Operating storage is out of sync or there is a storage connection error. + Try setting STORAGE_SYNC to true in .env file. Error: ${error.message}.` + ); + } else { + logger.info(`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`); + } + } else { + throw new Error(`Cannot connect to operating storage: ${error.message}.`); + } + } + + return instances; + } + + /** + * If instances is undefined here it's because storage has been formatted. + * Load all instances from .gbot found on deploy package directory. + * @param instances + * @param bootInstance + * @param core + */ + public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) { + if (!instances) { + const saveInstance = new GuaribasInstance(bootInstance); + await saveInstance.save(); + instances = await core.loadInstances(); + } + + return instances; + } + + public loadSysPackages(core: GBCoreService) { + // NOTE: if there is any code before this line a semicolon + // will be necessary before this line. + // Loads all system packages. + + [ + GBAdminPackage, + GBAnalyticsPackage, + GBCorePackage, + GBSecurityPackage, + GBKBPackage, + GBCustomerSatisfactionPackage, + GBWhatsappPackage + ].forEach(e => { + logger.info(`Loading sys package: ${e.name}...`); + const p = Object.create(e.prototype) as IGBPackage; + p.loadPackage(core, core.sequelize); + }); + } + + public ensureAdminIsSecured() { + const password = GBConfigService.get('ADMIN_PASS'); + if (!GBAdminService.StrongRegex.test(password)) { + throw new Error( + 'Please, define a really strong password in ADMIN_PASS environment variable before running the server.' + ); + } + } + + public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) { + let instance: IGBInstance; + logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`); + try { + instance = await azureDeployer.deployFarm(proxyAddress); + } catch (error) { + logger.warn( + `In case of error, please cleanup any infrastructure objects + created during this procedure and .env before running again.` + ); + throw error; + } + core.writeEnv(instance); + logger.info(`File .env written, starting General Bots...`); + GBConfigService.init(); + + return instance; + } + + public openBrowserInDevelopment() { + if (process.env.NODE_ENV === 'development') { + opn('http://localhost:4242'); + } } /** * SQL: * * // let sql: string = '' + - * // 'IF OBJECT_ID(\'[UserGroup]\', \'U\') IS NULL\n' + - * // 'CREATE TABLE [UserGroup] (\n' + - * // ' [id] INTEGER NOT NULL IDENTITY(1,1),\n' + - * // ' [userId] INTEGER NULL,\n' + - * // ' [groupId] INTEGER NULL,\n' + - * // ' [instanceId] INTEGER NULL,\n' + - * // ' PRIMARY KEY ([id1], [id2]),\n' + - * // ' FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,\n' + - * // ' FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId1]) ON DELETE NO ACTION,\n' + + * // 'IF OBJECT_ID(\'[UserGroup]\', \'U\') IS NULL' + + * // 'CREATE TABLE [UserGroup] (' + + * // ' [id] INTEGER NOT NULL IDENTITY(1,1),' + + * // ' [userId] INTEGER NULL,' + + * // ' [groupId] INTEGER NULL,' + + * // ' [instanceId] INTEGER NULL,' + + * // ' PRIMARY KEY ([id1], [id2]),' + + * // ' FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,' + + * // ' FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId1]) ON DELETE NO ACTION,' + * // ' FOREIGN KEY ([instanceId]) REFERENCES [Instance] ([instanceId]) ON DELETE NO ACTION)' */ private createTableQueryOverride(tableName, attributes, options): string { - let sql: string = this.createTableQuery.apply(this.queryGenerator, [ - tableName, - attributes, - options - ]); + let sql: string = this.createTableQuery.apply(this.queryGenerator, [tableName, attributes, options]); const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/; const matches = re1.exec(sql); if (matches) { @@ -292,16 +421,13 @@ export class GBCoreService implements IGBCoreService { /** * SQL: * let sql = '' + - * 'ALTER TABLE [UserGroup]\n' + - * ' ADD CONSTRAINT [invalid1] FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,\n' + - * ' CONSTRAINT [invalid2] FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId2]) ON DELETE NO ACTION, \n' + - * ' CONSTRAINT [invalid3] FOREIGN KEY ([instanceId1]) REFERENCES [Instance] ([instanceId1]) ON DELETE NO ACTION\n' + * 'ALTER TABLE [UserGroup]' + + * ' ADD CONSTRAINT [invalid1] FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,' + + * ' CONSTRAINT [invalid2] FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId2]) ON DELETE NO ACTION, ' + + * ' CONSTRAINT [invalid3] FOREIGN KEY ([instanceId1]) REFERENCES [Instance] ([instanceId1]) ON DELETE NO ACTION' */ private changeColumnQueryOverride(tableName, attributes): string { - let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [ - tableName, - attributes - ]); + let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [tableName, attributes]); const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/; const matches = re1.exec(sql); if (matches) { @@ -318,17 +444,21 @@ export class GBCoreService implements IGBCoreService { fkname += '_' + matches[1]; matches = re3.exec(fkcols); } - return ( - (args[0] ? args[0] : '') + - 'CONSTRAINT [' + - fkname + - '_fk] FOREIGN KEY (' + - fkcols + - ')' - ); + return (args[0] ? args[0] : '') + 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')'; } ); } return sql; } + + /** + * Opens storage firewall. + * + * @param azureDeployer Infrastructure Deployer instance. + */ + private async openStorageFrontier(deployer: AzureDeployerService) { + const group = GBConfigService.get('CLOUD_GROUP'); + const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0]; + await deployer.openStorageFirewall(group, serverName); + } } diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts index 5312d6f4..da535e02 100644 --- a/packages/core.gbapp/services/GBDeployer.ts +++ b/packages/core.gbapp/services/GBDeployer.ts @@ -42,8 +42,9 @@ const UrlJoin = require('url-join'); const Fs = require('fs'); const WaitUntil = require('wait-until'); const express = require('express'); +const child_process = require('child_process'); -import { IGBCoreService, IGBInstance } from 'botlib'; +import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib'; import { GBError } from 'botlib'; import { IGBPackage } from 'botlib'; import { AzureSearch } from 'pragmatismo-io-framework'; @@ -51,7 +52,8 @@ import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDe import { GuaribasInstance, GuaribasPackage } from '../models/GBModel'; import { KBService } from './../../kb.gbapp/services/KBService'; import { GBConfigService } from './GBConfigService'; -import { GBImporter } from './GBImporter'; +import { GBImporter } from './GBImporterService'; +import { GBVMService } from './GBVMService'; /** Deployer service for bots, themes, ai and more. */ export class GBDeployer { @@ -66,13 +68,9 @@ export class GBDeployer { } public static getConnectionStringFromInstance(instance: GuaribasInstance) { - return `Server=tcp:${ - instance.storageServer - }.database.windows.net,1433;Database=${instance.storageName};User ID=${ + return `Server=tcp:${instance.storageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${ instance.storageUsername - };Password=${ - instance.storagePassword - };Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`; + };Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`; } /** @@ -80,163 +78,152 @@ export class GBDeployer { * Performs package deployment in all .gbai or default. * * */ - public deployPackages( - core: IGBCoreService, - server: any, - appPackages: IGBPackage[] - ) { + public deployPackages(core: IGBCoreService, server: any, appPackages: IGBPackage[]) { const _this = this; - return new Promise((resolve: any, reject: any): any => { - let totalPackages = 0; - const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH'); - let paths = [GBDeployer.deployFolder]; - if (additionalPath) { - paths = paths.concat(additionalPath.toLowerCase().split(';')); - } - const botPackages = new Array(); - const gbappPackages = new Array(); - let generalPackages = new Array(); - - function doIt(path) { - const isDirectory = source => Fs.lstatSync(source).isDirectory(); - const getDirectories = source => - Fs.readdirSync(source) - .map(name => Path.join(source, name)) - .filter(isDirectory); - - const dirs = getDirectories(path); - dirs.forEach(element => { - if (element.startsWith('.')) { - logger.info(`Ignoring ${element}...`); - } else { - if (element.endsWith('.gbot')) { - botPackages.push(element); - } else if (element.endsWith('.gbapp')) { - gbappPackages.push(element); - } else { - generalPackages.push(element); - } - } - }); - } - - logger.info( - `Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...` - ); - paths.forEach(e => { - logger.info(`Looking in: ${e}...`); - doIt(e); - }); - - /** Deploys all .gbapp files first. */ - - let appPackagesProcessed = 0; - - gbappPackages.forEach(e => { - // Skips .gbapp inside deploy folder. - if (!e.startsWith('packages')) { - logger.info(`Deploying app: ${e}...`); - import(e) - .then(m => { - const p = new m.Package(); - p.loadPackage(core, core.sequelize); - appPackages.push(p); - logger.info(`App (.gbapp) deployed: ${e}.`); - appPackagesProcessed++; - }) - .catch(err => { - logger.error(`Error deploying App (.gbapp): ${e}: ${err}`); - appPackagesProcessed++; - }); - } else { - appPackagesProcessed++; + return new Promise( + (resolve: any, reject: any): any => { + let totalPackages = 0; + const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH'); + let paths = [GBDeployer.deployFolder]; + if (additionalPath) { + paths = paths.concat(additionalPath.toLowerCase().split(';')); } - }); + const botPackages = new Array(); + const gbappPackages = new Array(); + let generalPackages = new Array(); - WaitUntil() - .interval(1000) - .times(10) - .condition(function(cb) { - logger.info(`Waiting for app package deployment...`); - cb(appPackagesProcessed == gbappPackages.length); - }) - .done(async result => { - logger.info(`App Package deployment done.`); + function doIt(path) { + const isDirectory = source => Fs.lstatSync(source).isDirectory(); + const getDirectories = source => + Fs.readdirSync(source) + .map(name => Path.join(source, name)) + .filter(isDirectory); - try { - await core.syncDatabaseStructure(); - } catch (e) { - throw e; - } - - /** Deploys all .gbot files first. */ - - botPackages.forEach(e => { - logger.info(`Deploying bot: ${e}...`); - _this.deployBot(e); - logger.info(`Bot: ${e} deployed...`); - }); - - /** Then all remaining generalPackages are loaded. */ - - generalPackages = generalPackages.filter(p => !p.endsWith('.git')); - - generalPackages.forEach(filename => { - const filenameOnly = Path.basename(filename); - logger.info(`Deploying package: ${filename}...`); - - /** Handles apps for general bots - .gbapp must stay out of deploy folder. */ - - if ( - Path.extname(filename) === '.gbapp' || - Path.extname(filename) === '.gblib' - ) { - /** Themes for bots. */ - } else if (Path.extname(filename) === '.gbtheme') { - server.use('/themes/' + filenameOnly, express.static(filename)); - logger.info( - `Theme (.gbtheme) assets accessible at: ${'/themes/' + - filenameOnly}.` - ); - - /** Knowledge base for bots. */ - } else if (Path.extname(filename) === '.gbkb') { - server.use( - '/kb/' + filenameOnly + '/subjects', - express.static(UrlJoin(filename, 'subjects')) - ); - logger.info( - `KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.` - ); - } else if (Path.extname(filename) === '.gbui') { - // Already Handled + const dirs = getDirectories(path); + dirs.forEach(element => { + if (element.startsWith('.')) { + logger.info(`Ignoring ${element}...`); } else { - /** Unknown package format. */ - const err = new Error(`Package type not handled: ${filename}.`); - reject(err); - } - totalPackages++; - }); - - WaitUntil() - .interval(100) - .times(5) - .condition(function(cb) { - logger.info(`Waiting for package deployment...`); - cb(totalPackages == generalPackages.length); - }) - .done(function(result) { - if (botPackages.length === 0) { - logger.info( - 'No external packages to load, please use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder.' - ); + if (element.endsWith('.gbot')) { + botPackages.push(element); + } else if (element.endsWith('.gbapp')) { + gbappPackages.push(element); } else { - logger.info(`Package deployment done.`); + generalPackages.push(element); } - resolve(); - }); + } + }); + } + + logger.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`); + paths.forEach(e => { + logger.info(`Looking in: ${e}...`); + doIt(e); }); - }); + + /** Deploys all .gbapp files first. */ + + let appPackagesProcessed = 0; + + gbappPackages.forEach(e => { + // Skips .gbapp inside deploy folder. + if (!e.startsWith('packages')) { + logger.info(`Deploying app: ${e}...`); + import(e) + .then(m => { + const p = new m.Package(); + p.loadPackage(core, core.sequelize); + appPackages.push(p); + logger.info(`App (.gbapp) deployed: ${e}.`); + appPackagesProcessed++; + }) + .catch(err => { + logger.error(`Error deploying App (.gbapp): ${e}: ${err}`); + appPackagesProcessed++; + }); + } else { + appPackagesProcessed++; + } + }); + + WaitUntil() + .interval(1000) + .times(10) + .condition(function(cb) { + logger.info(`Waiting for app package deployment...`); + cb(appPackagesProcessed == gbappPackages.length); + }) + .done(async result => { + logger.info(`App Package deployment done.`); + + try { + await core.syncDatabaseStructure(); + } catch (e) { + throw e; + } + + /** Deploys all .gbot files first. */ + + botPackages.forEach(e => { + if (e !== 'packages\\boot.gbot') { + logger.info(`Deploying bot: ${e}...`); + _this.deployBot(e); + logger.info(`Bot: ${e} deployed...`); + } + }); + + /** Then all remaining generalPackages are loaded. */ + + generalPackages = generalPackages.filter(p => !p.endsWith('.git')); + + generalPackages.forEach(filename => { + const filenameOnly = Path.basename(filename); + logger.info(`Deploying package: ${filename}...`); + + /** Handles apps for general bots - .gbapp must stay out of deploy folder. */ + + if (Path.extname(filename) === '.gbapp' || Path.extname(filename) === '.gblib') { + /** Themes for bots. */ + } else if (Path.extname(filename) === '.gbtheme') { + server.use('/themes/' + filenameOnly, express.static(filename)); + logger.info(`Theme (.gbtheme) assets accessible at: ${'/themes/' + filenameOnly}.`); + + /** Knowledge base for bots. */ + } else if (Path.extname(filename) === '.gbkb') { + server.use('/kb/' + filenameOnly + '/subjects', express.static(UrlJoin(filename, 'subjects'))); + logger.info(`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`); + } else if (Path.extname(filename) === '.gbui') { + // Already Handled + } else if (Path.extname(filename) === '.gbdialog') { + // Already Handled + } else { + /** Unknown package format. */ + const err = new Error(`Package type not handled: ${filename}.`); + reject(err); + } + totalPackages++; + }); + + WaitUntil() + .interval(100) + .times(5) + .condition(function(cb) { + logger.info(`Waiting for package deployment...`); + cb(totalPackages == generalPackages.length); + }) + .done(function(result) { + if (botPackages.length === 0) { + logger.info( + 'No external packages to load, please use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder.' + ); + } else { + logger.info(`Package deployment done.`); + } + resolve(); + }); + }); + } + ); } /** @@ -246,23 +233,20 @@ export class GBDeployer { public async deployBot(localPath: string): Promise { const packageType = Path.extname(localPath); const packageName = Path.basename(localPath); - const instance = await this.importer.importIfNotExistsBotPackage( - packageName, - localPath - ); + const instance = await this.importer.importIfNotExistsBotPackage(null, packageName, localPath); + return instance; } - public async deployPackageToStorage( - instanceId: number, - packageName: string - ): Promise { + public async deployPackageToStorage(instanceId: number, packageName: string): Promise { return GuaribasPackage.create({ packageName: packageName, instanceId: instanceId }); } + public deployScriptToStorage(instanceId: number, localPath: string) {} + public deployTheme(localPath: string) { // DISABLED: Until completed, "/ui/public". // FsExtra.copy(localPath, this.workDir + packageName) @@ -275,7 +259,7 @@ export class GBDeployer { // }) } - public async deployPackageFromLocalPath(localPath: string) { + public async deployPackageFromLocalPath(min: GBMinInstance, localPath: string) { const packageType = Path.extname(localPath); switch (packageType) { @@ -293,10 +277,12 @@ export class GBDeployer { case '.gbui': break; + case '.gbdialog': + const vm = new GBVMService(); + return vm.loadJS(localPath, min, this.core, this, localPath); + default: - const err = GBError.create( - `GuaribasBusinessError: Unknow package type: ${packageType}.` - ); + const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`); Promise.reject(err); break; } @@ -324,10 +310,11 @@ export class GBDeployer { case '.gbui': break; + case '.gbdialog': + break; + default: - const err = GBError.create( - `GuaribasBusinessError: Unknown package type: ${packageType}.` - ); + const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`); Promise.reject(err); break; } @@ -353,13 +340,7 @@ export class GBDeployer { } } - await search.createDataSource( - dsName, - dsName, - 'GuaribasQuestion', - 'azuresql', - connectionString - ); + await search.createDataSource(dsName, dsName, 'GuaribasQuestion', 'azuresql', connectionString); try { await search.deleteIndex(); @@ -369,37 +350,22 @@ export class GBDeployer { throw err; } } - await search.createIndex( - AzureDeployerService.getKBSearchSchema(instance.searchIndex), - dsName - ); + await search.createIndex(AzureDeployerService.getKBSearchSchema(instance.searchIndex), dsName); } - public async getPackageByName( - instanceId: number, - packageName: string - ): Promise { + public async getPackageByName(instanceId: number, packageName: string): Promise { const where = { packageName: packageName, instanceId: instanceId }; return GuaribasPackage.findOne({ where: where }); } - /** - * - * Hot deploy processing. - * - */ - public async scanBootPackage() { - const deployFolder = 'packages'; - const bootPackage = GBConfigService.get('BOOT_PACKAGE'); - - if (bootPackage === 'none') { - return Promise.resolve(true); - } else { - return this.deployPackageFromLocalPath( - UrlJoin(deployFolder, bootPackage) - ); + public installDefaultGBUI() { + const root = 'packages/default.gbui'; + if (!Fs.existsSync(`${root}/build`)) { + Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true'); + child_process.execSync('npm install', { cwd: root }); + child_process.execSync('npm run build', { cwd: root }); } } } diff --git a/packages/core.gbapp/services/GBImporter.ts b/packages/core.gbapp/services/GBImporterService.ts similarity index 75% rename from packages/core.gbapp/services/GBImporter.ts rename to packages/core.gbapp/services/GBImporterService.ts index 1ad9b0e6..65745089 100644 --- a/packages/core.gbapp/services/GBImporter.ts +++ b/packages/core.gbapp/services/GBImporterService.ts @@ -50,44 +50,29 @@ export class GBImporter { this.core = core; } - public async importIfNotExistsBotPackage( - packageName: string, - localPath: string) { - - const packageJson = JSON.parse( - fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8') - ); - - const botId = packageJson.botId; - + public async importIfNotExistsBotPackage(botId: string, packageName: string, localPath: string) { + const packageJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8')); + if (!botId) { + botId = packageJson.botId; + } const instance = await this.core.loadInstance(botId); if (instance) { - return Promise.resolve(instance); + return instance; } else { - return this.createInstanceInternal(packageName, localPath, packageJson); + return await this.createInstanceInternal(botId, packageName, localPath, packageJson); } } - private async createInstanceInternal( - packageName: string, - localPath: string, - packageJson: any - ) { - const settings = JSON.parse( - fs.readFileSync(UrlJoin(localPath, 'settings.json'), 'utf8') - ); - const servicesJson = JSON.parse( - fs.readFileSync(UrlJoin(localPath, 'services.json'), 'utf8') - ); + private async createInstanceInternal(botId: string, packageName: string, localPath: string, packageJson: any) { + const settings = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'settings.json'), 'utf8')); + const servicesJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'services.json'), 'utf8')); - packageJson = {...packageJson, ...settings, ...servicesJson}; + packageJson = { ...packageJson, ...settings, ...servicesJson }; - GuaribasInstance.create(packageJson).then((instance: IGBInstance) => { + if (botId){ + packageJson.botId = botId; + } - const service = new SecService(); - // TODO: service.importSecurityFile(localPath, instance) - - Promise.resolve(instance); - }); + return GuaribasInstance.create(packageJson); } } diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index d99361af..6f520fdd 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -43,21 +43,9 @@ const logger = require('../../../src/logger'); const request = require('request-promise-native'); const AuthenticationContext = require('adal-node').AuthenticationContext; -import { - AutoSaveStateMiddleware, - BotFrameworkAdapter, - ConversationState, - MemoryStorage, - UserState -} from 'botbuilder'; +import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder'; -import { - GBMinInstance, - IGBAdminService, - IGBConversationalService, - IGBCoreService, - IGBPackage -} from 'botlib'; +import { GBMinInstance, IGBAdminService, IGBConversationalService, IGBCoreService, IGBPackage } from 'botlib'; import { GBAnalyticsPackage } from '../../analytics.gblib'; import { GBCorePackage } from '../../core.gbapp'; import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp'; @@ -88,7 +76,7 @@ export class GBMinService { core: IGBCoreService, conversationalService: IGBConversationalService, adminService: IGBAdminService, - deployer: GBDeployer, + deployer: GBDeployer ) { this.core = core; this.conversationalService = conversationalService; @@ -108,17 +96,16 @@ export class GBMinService { * */ public async buildMin( + bootInstance: GuaribasInstance, server: any, appPackages: IGBPackage[], instances: GuaribasInstance[], + deployer: GBDeployer ): Promise { // Serves default UI on root address '/'. const uiPackage = 'default.gbui'; - server.use( - '/', - express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')), - ); + server.use('/', express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build'))); Promise.all( instances.map(async instance => { @@ -133,7 +120,11 @@ export class GBMinService { (async () => { // Returns the instance object to clients requesting bot info. - const botId = req.params.botId; + let botId = req.params.botId; + if (botId === '[default]') { + botId = bootInstance.botId; + } + const instance = await this.core.loadInstance(botId); if (instance) { @@ -152,8 +143,8 @@ export class GBMinService { speechToken: speechToken, conversationId: webchatToken.conversationId, authenticatorTenant: instance.authenticatorTenant, - authenticatorClientId: instance.authenticatorClientId, - }), + authenticatorClientId: instance.authenticatorClientId + }) ); } else { const error = `Instance not found: ${botId}.`; @@ -165,9 +156,11 @@ export class GBMinService { // Build bot adapter. - const { min, adapter, conversationState } = await this.buildBotAdapter( - instance, - ); + const { min, adapter, conversationState } = await this.buildBotAdapter(instance); + + // Install default VBA module. + + deployer.deployPackageFromLocalPath(min, 'packages/default.gbdialog'); // Call the loadBot context.activity for all packages. @@ -177,32 +170,17 @@ export class GBMinService { const url = `/api/messages/${instance.botId}`; server.post(url, async (req, res) => { - return this.receiver( - adapter, - req, - res, - conversationState, - min, - instance, - appPackages, - ); + return await this.receiver(adapter, req, res, conversationState, min, instance, appPackages); }); - logger.info( - `GeneralBots(${instance.engineName}) listening on: ${url}.`, - ); + logger.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`); // Serves individual URL for each bot user interface. const uiUrl = `/${instance.botId}`; - server.use( - uiUrl, - express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')), - ); + server.use(uiUrl, express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build'))); logger.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`); - const state = `${instance.instanceId}${Math.floor( - Math.random() * 1000000000, - )}`; + const state = `${instance.instanceId}${Math.floor(Math.random() * 1000000000)}`; // Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD. // There they will authenticate and give their consent to allow this app access to @@ -211,13 +189,11 @@ export class GBMinService { let authorizationUrl = UrlJoin( min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant, - '/oauth2/authorize', + '/oauth2/authorize' ); authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${ min.instance.authenticatorClientId - }&redirect_uri=${min.instance.botEndpoint}/${ - min.instance.botId - }/token`; + }&redirect_uri=${min.instance.botEndpoint}/${min.instance.botId}/token`; res.redirect(authorizationUrl); }); @@ -227,23 +203,16 @@ export class GBMinService { // access token that can be used to access the user owned resource. server.get(`/${min.instance.botId}/token`, async (req, res) => { - const state = await min.adminService.getValue( - min.instance.instanceId, - 'AntiCSRFAttackState', - ); + const state = await min.adminService.getValue(min.instance.instanceId, 'AntiCSRFAttackState'); if (req.query.state !== state) { - const msg = - 'WARNING: state field was not provided as anti-CSRF token'; + const msg = 'WARNING: state field was not provided as anti-CSRF token'; logger.error(msg); throw new Error(msg); } const authenticationContext = new AuthenticationContext( - UrlJoin( - min.instance.authenticatorAuthorityHostUrl, - min.instance.authenticatorTenant, - ), + UrlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant) ); const resource = 'https://graph.microsoft.com'; @@ -260,48 +229,17 @@ export class GBMinService { logger.error(msg); res.send(msg); } else { - await this.adminService.setValue( - instance.instanceId, - 'refreshToken', - token.refreshToken, - ); - await this.adminService.setValue( - instance.instanceId, - 'accessToken', - token.accessToken, - ); - await this.adminService.setValue( - instance.instanceId, - 'expiresOn', - token.expiresOn.toString(), - ); - await this.adminService.setValue( - instance.instanceId, - 'AntiCSRFAttackState', - null, - ); + await this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken); + await this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken); + await this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString()); + await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', null); res.redirect(min.instance.botEndpoint); } - }, + } ); }); - - // Setups handlers. - // send: function (context.activity, next) { - // logger.info( - // `[SND]: ChannelID: ${context.activity.address.channelId}, ConversationID: ${context.activity.address.conversation}, - // Type: ${context.activity.type} `) - // this.core.createMessage( - // this.min.conversation, - // this.min.conversation.startedBy, - // context.activity.source, - // (data, err) => { - // logger.info(context.activity.source) - // } - // ) - // next() - }), + }) ); } @@ -316,8 +254,8 @@ export class GBMinService { url: 'https://directline.botframework.com/v3/directline/tokens/generate', method: 'POST', headers: { - Authorization: `Bearer ${instance.webchatKey}`, - }, + Authorization: `Bearer ${instance.webchatKey}` + } }; try { @@ -344,8 +282,8 @@ export class GBMinService { url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken', method: 'POST', headers: { - 'Ocp-Apim-Subscription-Key': instance.speechKey, - }, + 'Ocp-Apim-Subscription-Key': instance.speechKey + } }; try { @@ -359,7 +297,7 @@ export class GBMinService { private async buildBotAdapter(instance: any) { const adapter = new BotFrameworkAdapter({ appId: instance.marketplaceId, - appPassword: instance.marketplacePassword, + appPassword: instance.marketplacePassword }); const storage = new MemoryStorage(); @@ -377,8 +315,10 @@ export class GBMinService { min.conversationalService = this.conversationalService; min.adminService = this.adminService; min.instance = await this.core.loadInstance(min.botId); + min.cbMap = {}; min.userProfile = conversationState.createProperty('userProfile'); const dialogState = conversationState.createProperty('dialogState'); + min.dialogs = new DialogSet(dialogState); min.dialogs.add(new TextPrompt('textPrompt')); @@ -395,7 +335,7 @@ export class GBMinService { GBKBPackage, GBAnalyticsPackage, GBCustomerSatisfactionPackage, - GBWhatsappPackage, + GBWhatsappPackage ].forEach(sysPackage => { const p = Object.create(sysPackage.prototype) as IGBPackage; p.loadBot(min); @@ -417,17 +357,18 @@ export class GBMinService { /** * Bot Service hook method. */ - private receiver( + private async receiver( adapter: BotFrameworkAdapter, req: any, res: any, conversationState: ConversationState, min: any, instance: any, - appPackages: any[], + appPackages: any[] ) { - return adapter.processActivity(req, res, async context => { - const state = conversationState.get(context); + return await adapter.processActivity(req, res, async context => { + // Get loaded user state + const state = await conversationState.get(context); const step = await min.dialogs.createContext(context, state); step.context.activity.locale = 'en-US'; // TODO: Make dynamic. @@ -439,22 +380,20 @@ export class GBMinService { instanceId: instance.instanceId, botId: instance.botId, theme: instance.theme ? instance.theme : 'default.gbtheme', - secret: instance.webchatKey, + secret: instance.webchatKey }); user.loaded = true; user.subjects = []; + user.cb = null; await min.userProfile.set(step.context, user); } logger.info( - `User>: ${context.activity.text} (${context.activity.type}, ${ - context.activity.name - }, ${context.activity.channelId}, {context.activity.value})`, + `User>: ${context.activity.text} (${context.activity.type}, ${context.activity.name}, ${ + context.activity.channelId + }, {context.activity.value})` ); - if ( - context.activity.type === 'conversationUpdate' && - context.activity.membersAdded.length > 0 - ) { + if (context.activity.type === 'conversationUpdate' && context.activity.membersAdded.length > 0) { const member = context.activity.membersAdded[0]; if (member.name === 'GeneralBots') { logger.info(`Bot added to conversation, starting chat...`); @@ -471,23 +410,29 @@ export class GBMinService { // Processes messages. } else if (context.activity.type === 'message') { // Checks for /admin request. - - if (context.activity.text === 'admin') { + if (context.activity.text === 'alpha-vba') { + min.sandbox.context = context; + min.sandbox.step = step; + min.sandbox['bot'].bind(min.sandbox); + await min.sandbox['bot'](); + } else if (context.activity.text === 'admin') { await step.beginDialog('/admin'); // Checks for /menu JSON signature. } else if (context.activity.text.startsWith('{"title"')) { await step.beginDialog('/menu', { - data: JSON.parse(context.activity.text), + data: JSON.parse(context.activity.text) }); // Otherwise, continue to the active dialog in the stack. } else { + const user = await min.userProfile.get(context, {}); + if (step.activeDialog) { await step.continueDialog(); } else { await step.beginDialog('/answer', { - query: context.activity.text, + query: context.activity.text }); } } @@ -504,18 +449,18 @@ export class GBMinService { await step.beginDialog('/menu'); } else if (context.activity.name === 'giveFeedback') { await step.beginDialog('/feedback', { - fromMenu: true, + fromMenu: true }); } else if (context.activity.name === 'showFAQ') { await step.beginDialog('/faq'); } else if (context.activity.name === 'answerEvent') { await step.beginDialog('/answerEvent', { questionId: (context.activity as any).data, - fromFaq: true, + fromFaq: true }); } else if (context.activity.name === 'quality') { await step.beginDialog('/quality', { - score: (context.activity as any).data, + score: (context.activity as any).data }); } else if (context.activity.name === 'updateToken') { const token = (context.activity as any).data; @@ -524,13 +469,12 @@ export class GBMinService { await step.continueDialog(); } } + await conversationState.saveChanges(context, true); } catch (error) { const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`; logger.error(msg); - await step.context.sendActivity( - Messages[step.context.activity.locale].very_sorry_about_error, - ); + await step.context.sendActivity(Messages[step.context.activity.locale].very_sorry_about_error); await step.beginDialog('/ask', { isReturning: true }); } }); diff --git a/packages/core.gbapp/services/GBVMService.ts b/packages/core.gbapp/services/GBVMService.ts new file mode 100644 index 00000000..30cd2c8d --- /dev/null +++ b/packages/core.gbapp/services/GBVMService.ts @@ -0,0 +1,195 @@ +/*****************************************************************************\ +| ( )_ _ | +| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ | +| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ | +| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) | +| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' | +| | | ( )_) | | +| (_) \___/' | +| | +| General Bots Copyright (c) Pragmatismo.io. All rights reserved. | +| Licensed under the AGPL-3.0. | +| | +| According to our dual licensing model, this program can be used either | +| under the terms of the GNU Affero General Public License, version 3, | +| or under a proprietary license. | +| | +| The texts of the GNU Affero General Public License with an additional | +| permission and of our proprietary license can be found at and | +| in the LICENSE file you have received along with this program. | +| | +| This program is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY, without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| GNU Affero General Public License for more details. | +| | +| "General Bots" is a registered trademark of Pragmatismo.io. | +| The licensing of the program under the AGPLv3 does not imply a | +| trademark license. Therefore any rights, title and interest in | +| our trademarks remain entirely with us. | +| | +\*****************************************************************************/ + +'use strict'; + +import { GBMinInstance, IGBCoreService } from 'botlib'; +import * as fs from 'fs'; +import { DialogClass } from './GBAPIService'; +import { GBDeployer } from './GBDeployer'; +import { TSCompiler } from './TSCompiler'; +import { WaterfallDialog } from 'botbuilder-dialogs'; +const util = require('util'); +const logger = require('../../../src/logger'); +const vm = require('vm'); +const UrlJoin = require('url-join'); +const vb2ts = require('vbscript-to-typescript/dist/converter'); + +/** + * @fileoverview Virtualization services for emulation of BASIC. + */ + +export class GBVMService implements IGBCoreService { + private script = new vm.Script(); + + public async loadJS( + filename: string, + min: GBMinInstance, + core: IGBCoreService, + deployer: GBDeployer, + localPath: string + ): Promise { + const path = 'packages/default.gbdialog'; + const file = 'bot.vbs'; + const source = UrlJoin(path, file); + + // Example when handled through fs.watch() listener + fs.watchFile(source, async (curr, prev) => { + await this.run(source, path, min, deployer, filename); + }); + await this.run(source, path, min, deployer, filename); + this.addHearDialog(min); + } + + public async run(source: any, path: string, min: any, deployer: GBDeployer, filename: string) { + // Converts VBS into TS. + + vb2ts.convertFile(source); + + // Convert TS into JS. + const tsfile = `bot.ts`; + const tsc = new TSCompiler(); + tsc.compile([UrlJoin(path, tsfile)]); + + // Run JS into the GB context. + const jsfile = `bot.js`; + let localPath = UrlJoin(path, jsfile); + + if (fs.existsSync(localPath)) { + let code: string = fs.readFileSync(localPath, 'utf8'); + code = code.replace(/^.*exports.*$/gm, ''); + + // Finds all hear calls. + + let parsedCode = code; + let hearExp = /(\w+).*hear.*\(\)/; + + let match1; + + while ((match1 = hearExp.exec(code))) { + + let pos = 0; + + // Writes async body. + + const variable = match1[1]; // variable = hear(); + + parsedCode = code.substring(pos, pos + match1.index); + parsedCode += `hear (async (${variable}) => {\n`; + + // Skips old construction and point to the async block. + + pos = pos + match1.index; + let tempCode = code.substring(pos + match1[0].length + 1); + let start = pos; + + // Balances code blocks and checks for exits. + + let right = 0; + let left = 1; + let match2; + while ((match2 = /\{|\}/.exec(tempCode))) { + const c = tempCode.substring(match2.index, match2.index + 1); + + if (c === '}') { + right++; + } else if (c === '{') { + left++; + } + + tempCode = tempCode.substring(match2.index + 1); + pos += match2.index + 1; + + if (left === right) { + break; + } + } + + parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length); + parsedCode += '});\n'; + parsedCode += code.substring(pos + match1[0].length); + + // A interaction will be made for each hear. + + code = parsedCode; + } + + parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\btalk\b/g, function($0, $1) { + return $1 == undefined ? 'this.talk' : $1; + }); + + parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bhear\b/g, function($0, $1) { + return $1 == undefined ? 'this.hear' : $1; + }); + + parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, function($0, $1) { + return $1 == undefined ? 'this.sendEmail' : $1; + }); + + parsedCode = parsedCode.replace(/this\./gm, 'await this.'); + parsedCode = parsedCode.replace(/function/gm, 'async function'); + + fs.writeFileSync(localPath, parsedCode); + + const sandbox: DialogClass = new DialogClass(min); + const context = vm.createContext(sandbox); + vm.runInContext(parsedCode, context); + min.sandbox = sandbox; + await deployer.deployScriptToStorage(min.instanceId, filename); + logger.info(`[GBVMService] Finished loading of ${filename}`); + } + } + + private addHearDialog(min) { + min.dialogs.add( + new WaterfallDialog('/hear', [ + async step => { + step.activeDialog.state.cbId = step.options['id']; + + return await step.prompt('textPrompt', {}); + }, + async step => { + min.sandbox.context = step.context; + min.sandbox.step = step; + + const cbId = step.activeDialog.state.cbId; + const cb = min.cbMap[cbId]; + cb.bind({ step: step, context: step.context }); // TODO: Necessary or min.sandbox + + await step.endDialog(); + + return await cb(step.result); + } + ]) + ); + } +} diff --git a/packages/core.gbapp/services/TSCompiler.ts b/packages/core.gbapp/services/TSCompiler.ts new file mode 100644 index 00000000..4cb7e63a --- /dev/null +++ b/packages/core.gbapp/services/TSCompiler.ts @@ -0,0 +1,75 @@ +/*****************************************************************************\ +| ( )_ _ | +| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ | +| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ | +| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) | +| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' | +| | | ( )_) | | +| (_) \___/' | +| | +| General Bots Copyright (c) Pragmatismo.io. All rights reserved. | +| Licensed under the AGPL-3.0. | +| | +| According to our dual licensing model, this program can be used either | +| under the terms of the GNU Affero General Public License, version 3, | +| or under a proprietary license. | +| | +| The texts of the GNU Affero General Public License with an additional | +| permission and of our proprietary license can be found at and | +| in the LICENSE file you have received along with this program. | +| | +| This program is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY, without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| GNU Affero General Public License for more details. | +| | +| "General Bots" is a registered trademark of Pragmatismo.io. | +| The licensing of the program under the AGPLv3 does not imply a | +| trademark license. Therefore any rights, title and interest in | +| our trademarks remain entirely with us. | +| | +\*****************************************************************************/ + +/** + * @fileoverview General Bots server core. + */ + +'use strict'; + +import * as ts from 'typescript'; +const logger = require('../../../src/logger'); + +export class TSCompiler { + public compile( + fileNames: string[], + options: ts.CompilerOptions = { + noStrictGenericChecks: true, + noImplicitUseStrict: true, + noEmitOnError: false, + noImplicitAny: true, + target: ts.ScriptTarget.ES5, + module: ts.ModuleKind.None, + moduleResolution: ts.ModuleResolutionKind.Classic, + noEmitHelpers: true, + maxNodeModuleJsDepth: 0, + esModuleInterop: false + } + ) { + const program = ts.createProgram(fileNames, options); + const emitResult = program.emit(); + + const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + + allDiagnostics.forEach(diagnostic => { + if (diagnostic.file) { + const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + logger.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); + } else { + logger.error(`${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`); + } + }); + + return emitResult; + } +} diff --git a/packages/core.gbapp/tests/core.test.ts b/packages/core.gbapp/tests/core.test.ts index 9ec963d8..838d5f2d 100644 --- a/packages/core.gbapp/tests/core.test.ts +++ b/packages/core.gbapp/tests/core.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import 'mocha'; -import {GBImporter} from '../services/GBImporter'; +import {GBImporter} from '../services/GBImporterService'; describe('Hello function', () => { diff --git a/packages/core.gbapp/tests/vm.test.ts b/packages/core.gbapp/tests/vm.test.ts new file mode 100644 index 00000000..acade433 --- /dev/null +++ b/packages/core.gbapp/tests/vm.test.ts @@ -0,0 +1,51 @@ +/*****************************************************************************\ +| ( )_ _ | +| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ | +| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ | +| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) | +| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' | +| | | ( )_) | | +| (_) \___/' | +| | +| General Bots Copyright (c) Pragmatismo.io. All rights reserved. | +| Licensed under the AGPL-3.0. | +| | +| According to our dual licensing model, this program can be used either | +| under the terms of the GNU Affero General Public License, version 3, | +| or under a proprietary license. | +| | +| The texts of the GNU Affero General Public License with an additional | +| permission and of our proprietary license can be found at and | +| in the LICENSE file you have received along with this program. | +| | +| This program is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY, without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| GNU Affero General Public License for more details. | +| | +| "General Bots" is a registered trademark of Pragmatismo.io. | +| The licensing of the program under the AGPLv3 does not imply a | +| trademark license. Therefore any rights, title and interest in | +| our trademarks remain entirely with us. | +| | +\*****************************************************************************/ + +'use strict'; + +/** + * @fileoverview Tests for General Bots VM. + */ + +import { expect } from 'chai'; +import { GBVMService } from '../services/GBVMService'; + +describe('Load function', () => { + it('should fail on invalid file', () => { + try { + const service = new GBVMService(); + service.loadJS('invalid.file', null, null, null, null); + } catch (error) { + expect(error).to.equal(0); + } + }); +}); diff --git a/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts b/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts index ca62c7fc..d0ee93c3 100644 --- a/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts +++ b/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts @@ -78,35 +78,37 @@ export class FeedbackDialog extends IGBDialog { ]) ); - min.dialogs.add(new WaterfallDialog('/feedback', [ - async step => { - const locale = step.context.activity.locale; - if (step.result.fromMenu) { + min.dialogs.add( + new WaterfallDialog('/feedback', [ + async step => { + const locale = step.context.activity.locale; + await step.context.sendActivity(Messages[locale].about_suggestions); - } + step.activeDialog.state.cbId = step.options['id']; - await step.prompt('textPrompt', Messages[locale].what_about_service); - return await step.next(); - }, - async step => { - const locale = step.context.activity.locale; - const rate = await AzureText.getSentiment( - min.instance.textAnalyticsKey, - min.instance.textAnalyticsEndpoint, - min.conversationalService.getCurrentLanguage(step), - step.result - ); + return await step.prompt('textPrompt', Messages[locale].what_about_service); + }, + async step => { - if (rate > 0.5) { - await step.context.sendActivity(Messages[locale].glad_you_liked); - } else { - await step.context.sendActivity(Messages[locale].we_will_improve); + const locale = step.context.activity.locale; + const rate = await AzureText.getSentiment( + min.instance.textAnalyticsKey, + min.instance.textAnalyticsEndpoint, + min.conversationalService.getCurrentLanguage(step), + step.result + ); + + if (rate > 0.5) { + await step.context.sendActivity(Messages[locale].glad_you_liked); + } else { + await step.context.sendActivity(Messages[locale].we_will_improve); // TODO: Record. + } + return await step.replaceDialog('/ask', { isReturning: true }); + } - await step.replaceDialog('/ask', { isReturning: true }); - return await step.next(); - } - ])); + ]) + ); } } diff --git a/packages/default.gbdialog/bot.vbs b/packages/default.gbdialog/bot.vbs new file mode 100644 index 00000000..d5ddca66 --- /dev/null +++ b/packages/default.gbdialog/bot.vbs @@ -0,0 +1,114 @@ +<% +'**************************************************************************** +' ( )_ _ +' _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ +' ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ +' | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) +' | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' +' | | ( )_) | +' (_) \___/' +' +' General Bots Copyright (c) Pragmatismo.io. All rights reserved. +' Licensed under the AGPL-3.0. +' +' This BASIC file is based on this JavaScript file by Rodrigo Ruotolo: +' -> http://jsfiddle.net/roderick/dym05hsy +' +' According to our dual licensing model, this program can be used either +' under the terms of the GNU Affero General Public License, version 3, +' or under a proprietary license. +' +' The texts of the GNU Affero General Public License with an additional +' permission and of our proprietary license can be found at and +' in the LICENSE file you have received along with this program. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY, without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU Affero General Public License for more details. +' +' "General Bots" is a registered trademark of Pragmatismo.io. +' The licensing of the program under the AGPLv3 does not imply a +' trademark license. Therefore any rights, title and interest in +' our trademarks remain entirely with us. +' +'**************************************************************************** + + + +talk ("How many installments do you want to pay your Credit?") +installments = hear () + +if installments > 60 then + talk ("The maximum number of payments is 60") +else + talk ("What is the amount requested?") + ammount = hear () + + if ammount >100000 then + talk ("We are sorry, we can only accept proposals bellow 100k") + else + + talk ("What is the best due date?") + dueDate = hear () + + interestRate = 0 + adjustment = 0 + + if installments < 12 then + interestRate = 1.60 + adjustment = 0.09748 + end if + + if installments > 12 and installments < 18 then + interestRate = 1.66 + adjustment = 0.06869 + end if + + if installments > 18 and installments < 36 then + interestRate = 1.64 + adjustment = 0.05397 + end if + + if installments > 36 and installments < 48 then + interestRate = 1.62 + adjustment = 0.03931 + end if + + if installments > 48 and installments < 60 then + interestRate = 1.70 + adjustment = 0.03270 + end if + + if installments = 60 then + interestRate = 1.79 + adjustment = 0.02916 + end if + + if installments > 60 then + talk ("The maximum number of payments is 60") + end if + + + ' TODO: This must be reviewed in terms of financing logic. + + nInstallments = parseInt(installments) + vAmmount = parseFloat(ammount) + initialPayment = parseFloat(vAmmount) * 0.3 ' 30% of the value + tac = 800 + adjustment = 1.3 + + totalValue = ammount - initialPayment + tac + paymentValue = totalValue * adjustment + finalValue = paymentValue * nInstallments + initialPayment + + talk("Congratulations! Your credit analysis is **done**:") + talk("First payment: **" + initialPayment + "**") + talk("Payment value: **" + paymentValue + "**") + talk("Interest Rate: **" + interestRate + "%**") + talk("Total Value: **" + totalValue + "**") + talk("Final Value: **" + finalValue + "**") + + end if +end if +%> \ No newline at end of file diff --git a/packages/default.gbkb/draft.md b/packages/default.gbkb/draft.md new file mode 100644 index 00000000..3e56b0b6 --- /dev/null +++ b/packages/default.gbkb/draft.md @@ -0,0 +1,3 @@ + Últimas notícias + Contato + Ofertas diff --git a/packages/default.gbkb/package.json b/packages/default.gbkb/package.json new file mode 100644 index 00000000..6d8fb717 --- /dev/null +++ b/packages/default.gbkb/package.json @@ -0,0 +1,6 @@ +{ + "botId":"pragmatismo-ai-prd", + "version": "1.0.0", + "description": "Bot pragmatismo.", + "license": "Private" +} \ No newline at end of file diff --git a/packages/default.gbkb/pages/about.md b/packages/default.gbkb/pages/about.md new file mode 100644 index 00000000..f8f61594 --- /dev/null +++ b/packages/default.gbkb/pages/about.md @@ -0,0 +1,16 @@ +#Desenvolvimento Personalizado + +General Bots usa linguagem natural para entender o que as pessoas querem, facilitando o desenvolvimento de código. Quando alguém diz: "Preciso do relatório mensal" ou "Imprima o relatório do mês", General Bots entende o mesmo. Utilize o nosso desenvolvimento para estender a conversa com suas próprias regras e intenções. + +#Descoberta + +General Bots pode pró-ativamente sugerir suas habilidades para os usuários baseada no contexto, como solicitação de um pedido, envio de uma mensagem, agendamento de uma conferência telefônica ou qualquer ação definida na sua aplicação. + +#Torne pessoal + +Entregue experiências únicas através do conhecimento que a General Bots possui sobre seus usuários e preferências, para ajudar a tomar decisões e apresentar sempre o melhor cenário. + +#Sem downloads adicionais + +A interface da sua aplicação é automaticamente integrada à General Bots, de modo que não seja necessário realizar download ou instalações. + diff --git a/packages/default.gbkb/subjects.json b/packages/default.gbkb/subjects.json new file mode 100644 index 00000000..23471402 --- /dev/null +++ b/packages/default.gbkb/subjects.json @@ -0,0 +1,58 @@ +{ + "children": [ + { + "title": "Bots & AI", + "description": "Bots & inteligência artificial.", + "id": "bots-ai", + "children": [ + { + "title": "General Bots", + "description": "Plataforma de bots da Pragmatismo.io.", + "id": "general-bots" + }, + { + "title": "Cortana Intelligence Suite", + "description": "Suite de Big Data da Microsoft.", + "id": "cortana" + } + ] + }, + { + "title": "Produtividade", + "description": "Artigos sobre sistemas Internos.", + "id": "produtividade", + "children": [ + { + "title": "Microsoft Project Online", + "description": "Artigos sobre o Microsoft Project Online.", + "id": "msproject" + }, + { + "title": "SharePoint", + "description": "SharePoint, sites e serviços.", + "id": "sharepoint" + }, + { + "title": "Office 365", + "description": "Plataforma colaborativa moderna da Microsoft.", + "id": "office365" + }, + { + "title": "Microsoft Dynamics", + "description": "Artigos sobre plataforma de CRM da Microsoft.", + "id": "msdynamics" + }, + { + "title": "Power BI", + "description": "Dashboards modernos e intuitivos.", + "id": "powerbi" + } + ] + }, + { + "title": "Sobre", + "description": "Artigos sobre o Bot da Pragmatismo.io", + "id": "sobre" + } + ] +} \ No newline at end of file diff --git a/packages/default.gbkb/subjects/bots-ai.png b/packages/default.gbkb/subjects/bots-ai.png new file mode 100644 index 00000000..5c9197f7 Binary files /dev/null and b/packages/default.gbkb/subjects/bots-ai.png differ diff --git a/packages/default.gbkb/subjects/cortana.png b/packages/default.gbkb/subjects/cortana.png new file mode 100644 index 00000000..96445174 Binary files /dev/null and b/packages/default.gbkb/subjects/cortana.png differ diff --git a/packages/default.gbkb/subjects/general-bots.png b/packages/default.gbkb/subjects/general-bots.png new file mode 100644 index 00000000..1a68418e Binary files /dev/null and b/packages/default.gbkb/subjects/general-bots.png differ diff --git a/packages/default.gbkb/subjects/msdynamics.png b/packages/default.gbkb/subjects/msdynamics.png new file mode 100644 index 00000000..6076c093 Binary files /dev/null and b/packages/default.gbkb/subjects/msdynamics.png differ diff --git a/packages/default.gbkb/subjects/msproject.png b/packages/default.gbkb/subjects/msproject.png new file mode 100644 index 00000000..7df4e3d1 Binary files /dev/null and b/packages/default.gbkb/subjects/msproject.png differ diff --git a/packages/default.gbkb/subjects/office365.png b/packages/default.gbkb/subjects/office365.png new file mode 100644 index 00000000..f9aae394 Binary files /dev/null and b/packages/default.gbkb/subjects/office365.png differ diff --git a/packages/default.gbkb/subjects/powerbi.png b/packages/default.gbkb/subjects/powerbi.png new file mode 100644 index 00000000..f9aae394 Binary files /dev/null and b/packages/default.gbkb/subjects/powerbi.png differ diff --git a/packages/default.gbkb/subjects/produtividade.png b/packages/default.gbkb/subjects/produtividade.png new file mode 100644 index 00000000..8e4d6c1a Binary files /dev/null and b/packages/default.gbkb/subjects/produtividade.png differ diff --git a/packages/default.gbkb/subjects/sharepoint.png b/packages/default.gbkb/subjects/sharepoint.png new file mode 100644 index 00000000..a1423ae9 Binary files /dev/null and b/packages/default.gbkb/subjects/sharepoint.png differ diff --git a/packages/default.gbkb/subjects/skype.png b/packages/default.gbkb/subjects/skype.png new file mode 100644 index 00000000..7ee1402e Binary files /dev/null and b/packages/default.gbkb/subjects/skype.png differ diff --git a/packages/default.gbkb/subjects/sobre.png b/packages/default.gbkb/subjects/sobre.png new file mode 100644 index 00000000..9e0f1b76 Binary files /dev/null and b/packages/default.gbkb/subjects/sobre.png differ diff --git a/packages/default.gbkb/tabular/common-goodbye.tsv b/packages/default.gbkb/tabular/common-goodbye.tsv new file mode 100644 index 00000000..68056eb8 Binary files /dev/null and b/packages/default.gbkb/tabular/common-goodbye.tsv differ diff --git a/packages/default.gbkb/tabular/common-hello.tsv b/packages/default.gbkb/tabular/common-hello.tsv new file mode 100644 index 00000000..97062e0d Binary files /dev/null and b/packages/default.gbkb/tabular/common-hello.tsv differ diff --git a/packages/default.gbkb/tabular/common-persona.tsv b/packages/default.gbkb/tabular/common-persona.tsv new file mode 100644 index 00000000..363d71a8 Binary files /dev/null and b/packages/default.gbkb/tabular/common-persona.tsv differ diff --git a/packages/default.gbkb/tabular/min.tsv b/packages/default.gbkb/tabular/min.tsv new file mode 100644 index 00000000..7eca9c7a Binary files /dev/null and b/packages/default.gbkb/tabular/min.tsv differ diff --git a/packages/default.gbkb/videos/placeholder b/packages/default.gbkb/videos/placeholder new file mode 100644 index 00000000..e69de29b diff --git a/packages/default.gbui/package-lock.json b/packages/default.gbui/package-lock.json index c787988b..3dec0118 100644 --- a/packages/default.gbui/package-lock.json +++ b/packages/default.gbui/package-lock.json @@ -1134,9 +1134,9 @@ "integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg==" }, "adaptivecards": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/adaptivecards/-/adaptivecards-1.1.0.tgz", - "integrity": "sha512-n4vRIjo0vKvjMMnTKqRZm3TXhq+8saN1s17Z0pQEtDpBWjrGbfn8e+Qx/xvgNjqFYzWIme7uZAX3DBDhSOLu/A==" + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/adaptivecards/-/adaptivecards-1.0.0.tgz", + "integrity": "sha1-96HxdpJRYmirQMpU8Z/aP6WJdjQ=" }, "address": { "version": "1.0.3", @@ -2043,15 +2043,6 @@ } } }, - "babel-runtime": { - "version": "7.0.0-beta.3", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-7.0.0-beta.3.tgz", - "integrity": "sha512-jlzZ8RACjt0QGxq+wqsw5bCQE9RcUyWpw987mDY3GYxTpOQT2xoyNoG++oVCHzr/nACLBIprfVBNvv/If1ZYcg==", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, "babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", @@ -2338,86 +2329,48 @@ } }, "botframework-webchat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/botframework-webchat/-/botframework-webchat-4.1.0.tgz", - "integrity": "sha512-ZvmY2iYN+VPp/myuqVRHGehgx+xtHXnv7cwn1c2nCz+/ak0WAR4D2sakxEhX4vB09t5MNUbx8GYq9GPxTI+lCA==", + "version": "0.14.3-master.72bfef9", + "resolved": "https://registry.npmjs.org/botframework-webchat/-/botframework-webchat-0.14.3-master.72bfef9.tgz", + "integrity": "sha512-rgppVWeVd43FT5O2HpoPeVkowPtGN74cAhC+ZSGGd7HiDq5UyHtR4+PISLvR9XtRy/dLTF2afubOOThPvW+44w==", "requires": { - "@babel/runtime": "^7.0.0", - "adaptivecards": "^1.0.0", - "babel-runtime": "^7.0.0-beta.3", - "botframework-directlinejs": "^0.10.0", - "botframework-webchat-component": "4.1.0", - "botframework-webchat-core": "4.1.0", - "core-js": "^2.5.7", - "markdown-it": "^8.4.2", - "markdown-it-for-inline": "^0.1.1", - "memoize-one": "^4.0.2", - "microsoft-speech-browser-sdk": "^0.0.12", - "react": "^16.5.0", - "react-dom": "^16.5.0", - "react-redux": "^5.0.7", - "redux": "^4.0.0", - "sanitize-html": "^1.19.0", - "store": "^2.0.12", - "url-search-params-polyfill": "^5.0.0", - "web-speech-cognitive-services": "^3.0.0", - "whatwg-fetch": "^3.0.0" + "adaptivecards": "1.0.0", + "bluebird": "^3.5.1", + "botframework-directlinejs": "0.9.17", + "core-js": "2.4.1", + "jspeech": "^0.1.1", + "markdown-it": "8.3.1", + "microsoft-speech-browser-sdk": "0.0.12", + "react-redux": "5.0.5", + "redux": "3.7.2", + "redux-observable": "0.13.0", + "rxjs": "5.4.3", + "simple-update-in": "^1.2.0", + "tslib": "1.7.1" }, "dependencies": { - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + "botframework-directlinejs": { + "version": "0.9.17", + "resolved": "https://registry.npmjs.org/botframework-directlinejs/-/botframework-directlinejs-0.9.17.tgz", + "integrity": "sha512-Ib2BhalcxbKi5rtCO1OX5VhWkfb/rN/voq5823hCIvd/p+GCI/G9jEnxzRcfFWmLUk6hWYjwUnKarIeg93YiMA==", + "requires": { + "rxjs": "^5.0.3" + } + }, + "core-js": { + "version": "2.4.1", + "resolved": "http://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=" + }, + "rxjs": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz", + "integrity": "sha512-fSNi+y+P9ss+EZuV0GcIIqPUK07DEaMRUtLJvdcvMyFjc9dizuDjere+A4V7JrLGnm9iCc+nagV/4QdMTkqC4A==", + "requires": { + "symbol-observable": "^1.0.1" + } } } }, - "botframework-webchat-component": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/botframework-webchat-component/-/botframework-webchat-component-4.1.0.tgz", - "integrity": "sha512-kTgAxA1zMwblg/3/cE2GjWX9ff+NodCnVMcPm7Daas5iik74orqoPUJKARcKNH+iRihlfE/2oeoj06Gl0CB1Gw==", - "requires": { - "adaptivecards": "^1.0.0", - "botframework-webchat-core": "4.1.0", - "bytes": "^3.0.0", - "classnames": "^2.2.6", - "glamor": "^2.20.40", - "memoize-one": "^3.1.1", - "react-dictate-button": "^1.1.3", - "react-film": "^1.1.1", - "react-redux": "^5.0.7", - "react-say": "^1.1.1", - "react-scroll-to-bottom": "^1.2.0", - "redux": "^4.0.0", - "sanitize-html": "^1.18.2", - "simple-update-in": "^1.3.0" - }, - "dependencies": { - "memoize-one": { - "version": "3.1.1", - "resolved": "http://registry.npmjs.org/memoize-one/-/memoize-one-3.1.1.tgz", - "integrity": "sha512-YqVh744GsMlZu6xkhGslPSqSurOv6P+kLN2J3ysBZfagLcL5FdRK/0UpgLoL8hwjjEvvAVkjJZyFP+1T6p1vgA==" - } - } - }, - "botframework-webchat-core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/botframework-webchat-core/-/botframework-webchat-core-4.1.0.tgz", - "integrity": "sha512-mPw92k0Pq2ATsx1rE3oi73oy7lqrCfzV/XFK+XjXYeiSp6PMi1Mn6B4eIrnGKZe8vLQ8Y92jt0Zgq7HUl2jqLg==", - "requires": { - "@babel/runtime": "^7.0.0-rc.2", - "jsonwebtoken": "^8.3.0", - "mime": "^2.3.1", - "redux": "^4.0.0", - "redux-promise-middleware": "^5.1.1", - "redux-saga": "^0.16.0", - "simple-update-in": "^1.3.0" - } - }, - "bowser": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", - "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==" - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2555,11 +2508,6 @@ "isarray": "^1.0.0" } }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -2883,11 +2831,6 @@ } } }, - "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" - }, "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", @@ -3234,6 +3177,16 @@ "sha.js": "^2.4.8" } }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -3290,15 +3243,6 @@ } } }, - "css-in-js-utils": { - "version": "2.0.1", - "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", - "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", - "requires": { - "hyphenate-style-name": "^1.0.2", - "isobject": "^3.0.1" - } - }, "css-loader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.0.tgz", @@ -3912,14 +3856,6 @@ "webidl-conversions": "^4.0.2" } }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", @@ -3988,14 +3924,6 @@ "safer-buffer": "^2.1.0" } }, - "ecdsa-sig-formatter": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", - "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4503,21 +4431,11 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-as-promise": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/event-as-promise/-/event-as-promise-1.0.5.tgz", - "integrity": "sha512-z/WIlyou7oTvXBjm5YYjfklr2d8gUWtx8b5GAcrIs1n1D35f7NIK0CrcYSXbY3VYikG9bUan+wScPyGXL/NH4A==" - }, "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=" }, - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" - }, "eventsource": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", @@ -5703,11 +5621,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5720,15 +5640,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5831,7 +5754,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5841,6 +5765,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5853,17 +5778,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5880,6 +5808,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5952,7 +5881,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5962,6 +5892,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6067,6 +5998,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6168,18 +6100,6 @@ "assert-plus": "^1.0.0" } }, - "glamor": { - "version": "2.20.40", - "resolved": "https://registry.npmjs.org/glamor/-/glamor-2.20.40.tgz", - "integrity": "sha512-DNXCd+c14N9QF8aAKrfl4xakPk5FdcFwmH7sD0qnC0Pr7xoZ5W9yovhUrY/dJc3psfGGXC58vqQyRtuskyUJxA==", - "requires": { - "fbjs": "^0.8.12", - "inline-style-prefixer": "^3.0.6", - "object-assign": "^4.1.1", - "prop-types": "^15.5.10", - "through": "^2.3.8" - } - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -6504,12 +6424,9 @@ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" }, "hoist-non-react-statics": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.1.0.tgz", - "integrity": "sha512-MYcYuROh7SBM69xHGqXEwQqDux34s9tz+sCnxJmN18kgWh6JFdTw/5YdZtqsOdZJXddE/wUpCzfEdDrJj8p0Iw==", - "requires": { - "react-is": "^16.3.2" - } + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" }, "home-or-tmp": { "version": "2.0.0", @@ -6628,19 +6545,6 @@ "util.promisify": "1.0.0" } }, - "htmlparser2": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", - "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==", - "requires": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.0.6" - } - }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -6977,11 +6881,6 @@ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, - "hyphenate-style-name": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", - "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" - }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -7097,15 +6996,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=" }, - "inline-style-prefixer": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz", - "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=", - "requires": { - "bowser": "^1.7.3", - "css-in-js-utils": "^2.0.0" - } - }, "inquirer": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", @@ -8280,21 +8170,10 @@ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, - "jsonwebtoken": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", - "integrity": "sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag==", - "requires": { - "jws": "^3.1.5", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1" - } + "jspeech": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jspeech/-/jspeech-0.1.1.tgz", + "integrity": "sha1-n+wcnRGeFJBajeqCpQWvBs+Sg1k=" }, "jsprim": { "version": "1.4.1", @@ -8315,25 +8194,6 @@ "array-includes": "^3.0.3" } }, - "jwa": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", - "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", - "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", - "requires": { - "jwa": "^1.1.5", - "safe-buffer": "^5.0.1" - } - }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -8394,9 +8254,9 @@ } }, "linkify-it": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz", - "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.1.0.tgz", + "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", "requires": { "uc.micro": "^1.0.1" } @@ -8503,6 +8363,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" }, + "lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -8513,66 +8378,16 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, - "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -8682,22 +8497,17 @@ } }, "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.3.1.tgz", + "integrity": "sha1-L0tiKUjM3Bk9ZvPKLUMSWsSscyM=", "requires": { "argparse": "^1.0.7", "entities": "~1.1.1", "linkify-it": "^2.0.0", "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "uc.micro": "^1.0.3" } }, - "markdown-it-for-inline": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/markdown-it-for-inline/-/markdown-it-for-inline-0.1.1.tgz", - "integrity": "sha1-Q18jFvW15o4UUM+iJC8rjVmtx18=" - }, "math-random": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", @@ -8736,11 +8546,6 @@ "mimic-fn": "^1.0.0" } }, - "memoize-one": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.3.tgz", - "integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==" - }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -11569,16 +11374,6 @@ } } }, - "react-dictate-button": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/react-dictate-button/-/react-dictate-button-1.1.3.tgz", - "integrity": "sha512-4Od7sTAEIvPKpZbpy1tCv7qqfRNwLnZom9hrLnNvr1FIWjSOYC94ekAP8S5kevqnVaFvSqwFisCmjd3uLmvsrA==", - "requires": { - "classnames": "^2.2.6", - "glamor": "^2.20.40", - "memoize-one": "^4.0.0" - } - }, "react-dom": { "version": "16.6.3", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz", @@ -11606,15 +11401,6 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.0.tgz", "integrity": "sha512-akMy/BQT5m1J3iJIHkSb4qycq2wzllWsmmolaaFVnb+LPV9cIJ/nTud40ZsiiT0H3P+/wXIdbjx2fzF61OaeOQ==" }, - "react-film": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/react-film/-/react-film-1.1.1.tgz", - "integrity": "sha512-el6Uw1yDeiu7cKyyuL0eupAKO6vmmTX8un+5btvyYlzD15J1JWLp+Q4rKR3JfcJ2ZM8LKPhAHMfCUGV8t9lVMQ==", - "requires": { - "classnames": "^2.2.6", - "glamor": "^2.20.40" - } - }, "react-helmet": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.0.tgz", @@ -11626,11 +11412,6 @@ "react-side-effect": "^1.1.0" } }, - "react-is": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.1.tgz", - "integrity": "sha512-wOKsGtvTMYs7WAscmwwdM8sfRRvE17Ym30zFj3n37Qx5tHRfhenPKEPILHaHob6WoLFADmQm1ZNrE5xMCM6sCw==" - }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -11655,28 +11436,17 @@ } }, "react-redux": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.1.tgz", - "integrity": "sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.5.tgz", + "integrity": "sha1-+OjHsjlCJXblLWt9sGQ5RpvphGo=", "requires": { - "@babel/runtime": "^7.1.2", - "hoist-non-react-statics": "^3.1.0", - "invariant": "^2.2.4", + "create-react-class": "^15.5.3", + "hoist-non-react-statics": "^1.0.3", + "invariant": "^2.0.0", + "lodash": "^4.2.0", + "lodash-es": "^4.2.0", "loose-envify": "^1.1.0", - "prop-types": "^15.6.1", - "react-is": "^16.6.0", - "react-lifecycles-compat": "^3.0.0" - } - }, - "react-say": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/react-say/-/react-say-1.1.1.tgz", - "integrity": "sha512-R+XfFQjpwlD48miC0AAb6DZDq4h5DkKFnqQFi/83wpO73isfYx+wuPU6PodzRGP4rF+/6nibnKc2V328ifiALA==", - "requires": { - "classnames": "^2.2.6", - "event-as-promise": "^1.0.3", - "glamor": "^2.20.40", - "memoize-one": "^4.0.0" + "prop-types": "^15.5.10" } }, "react-scripts": { @@ -11749,17 +11519,6 @@ } } }, - "react-scroll-to-bottom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/react-scroll-to-bottom/-/react-scroll-to-bottom-1.2.0.tgz", - "integrity": "sha512-/tAaVVnSpnMtv4PVIxmxOFh45g6PcUihIK6zH17WTPkqreRFRrbCBnC7ieVu2isV68Zjee98cmBMGgKapI9fCw==", - "requires": { - "classnames": "^2.2.6", - "glamor": "^2.20.40", - "memoize-one": "^4.0.2", - "simple-update-in": "^1.4.0" - } - }, "react-side-effect": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz", @@ -11837,16 +11596,6 @@ } } }, - "readable-stream": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz", - "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -12146,22 +11895,16 @@ } }, "redux": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", - "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", + "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" + "lodash": "^4.2.1", + "lodash-es": "^4.2.1", + "loose-envify": "^1.1.0", + "symbol-observable": "^1.0.3" }, "dependencies": { - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -12169,15 +11912,10 @@ } } }, - "redux-promise-middleware": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/redux-promise-middleware/-/redux-promise-middleware-5.1.1.tgz", - "integrity": "sha512-YC1tiheU28Hgmtu5HHMLiuveLgjL1aCJWsSnwquMiZBcj5i/J9qVLt6vgOnb0Gz37y4deJ/rjiNt7l6Dh+Z8lA==" - }, - "redux-saga": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-0.16.2.tgz", - "integrity": "sha512-iIjKnRThI5sKPEASpUvySemjzwqwI13e3qP7oLub+FycCRDysLSAOwt958niZW6LhxfmS6Qm1BzbU70w/Koc4w==" + "redux-observable": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/redux-observable/-/redux-observable-0.13.0.tgz", + "integrity": "sha1-NbJsLNu3HkmbMcqZYdoFgcKXOQk=" }, "regenerate": { "version": "1.4.0", @@ -12556,20 +12294,18 @@ } }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" - } + "symbol-observable": "1.0.1" } }, + "rxjs-compat": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.3.3.tgz", + "integrity": "sha512-caGN7ixiabHpOofginKEquuHk7GgaCrC7UpUQ9ZqGp80tMc68msadOeP/2AKy2R4YJsT1+TX5GZCtxO82qWkyA==" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12867,23 +12603,6 @@ } } }, - "sanitize-html": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.19.1.tgz", - "integrity": "sha512-zNYr6FvBn4bZukr9x2uny6od/9YdjCLwF+FqxivqI0YOt/m9GIxfX+tWhm52tBAPUXiTTb4bJTGVagRz5b06bw==", - "requires": { - "chalk": "^2.3.0", - "htmlparser2": "^3.9.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.0", - "postcss": "^6.0.14", - "srcset": "^1.0.0", - "xtend": "^4.0.0" - } - }, "sass-loader": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", @@ -13539,15 +13258,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } - }, "sshpk": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", @@ -13611,11 +13321,6 @@ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, - "store": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", - "integrity": "sha1-jFNOKguDH3K3X8XxEZhXxE711ZM=" - }, "stream-browserify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", @@ -14531,11 +14236,6 @@ "requires-port": "^1.0.0" } }, - "url-search-params-polyfill": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-search-params-polyfill/-/url-search-params-polyfill-5.0.0.tgz", - "integrity": "sha512-+SCD22QJp4UnqPOI5UTTR0Ljuh8cHbjEf1lIiZrZ8nHTlTixqwVsVQTSfk5vrmDz7N09/Y+ka5jQr0ff35FnQQ==" - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -14675,18 +14375,6 @@ "minimalistic-assert": "^1.0.0" } }, - "web-speech-cognitive-services": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/web-speech-cognitive-services/-/web-speech-cognitive-services-3.0.0.tgz", - "integrity": "sha512-mkAsnL1pzGTn4TXKxFNwcgYL5hhh/NyvZtUghsFk6cGuP2OHebG8K04uJlcwpZnzp18HSWyi3YcYXXGU1tAZJg==", - "requires": { - "@babel/runtime": "^7.1.2", - "event-as-promise": "^1.0.5", - "events": "^3.0.0", - "memoize-one": "^4.0.0", - "simple-update-in": "^1.2.0" - } - }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", diff --git a/packages/default.gbui/package.json b/packages/default.gbui/package.json index 1366ff36..80ae13d3 100644 --- a/packages/default.gbui/package.json +++ b/packages/default.gbui/package.json @@ -2,24 +2,27 @@ "name": "default.gbui", "version": "0.0.12", "private": false, + "repository": "https://github.com/pragmatismo-io/BotServer", + "description": "Default web interface for General Bots open-core", "license": "AGPL-3.0", "homepage": ".", "dependencies": { - "ajv": "^6.5.5", + "ajv": "^6.5.4", "botframework-directlinejs": "^0.10.0", - "botframework-webchat": "4.1.0", + "botframework-webchat": "0.14.3-master.72bfef9", "deep-extend": "^0.6.0", "fetch": "^1.1.0", "msal": "^0.2.3", "powerbi-client": "^2.6.5", - "react": "^16.6.3", - "react-dom": "^16.6.3", + "react": "^16.6.0", + "react-dom": "^16.6.0", "react-helmet": "^5.2.0", - "react-player": "^1.7.0", + "react-player": "^1.6.6", "react-powerbi": "^0.3.1", "react-scripts": "^2.1.1", "react-transition-group": "^2.5.0", - "rxjs": "^6.3.3", + "rxjs": "^5.5.12", + "rxjs-compat": "^6.3.3", "url-join": "^4.0.0" }, "scripts": { diff --git a/packages/default.gbui/src/GBUIApp.js b/packages/default.gbui/src/GBUIApp.js index 0a82ff67..7caf845a 100644 --- a/packages/default.gbui/src/GBUIApp.js +++ b/packages/default.gbui/src/GBUIApp.js @@ -314,6 +314,7 @@ class GBUIApp extends React.Component { // let speechOptions; // let token = this.state.instanceClient.speechToken; + // speechOptions = { // speechRecognizer: new SpeechRecognizer({ // locale: "pt-br", diff --git a/packages/kb.gbapp/dialogs/AskDialog.ts b/packages/kb.gbapp/dialogs/AskDialog.ts index 3b2f1774..e911b7f8 100644 --- a/packages/kb.gbapp/dialogs/AskDialog.ts +++ b/packages/kb.gbapp/dialogs/AskDialog.ts @@ -149,6 +149,7 @@ export class AskDialog extends IGBDialog { return await step.replaceDialog('/ask', { isReturning: true }); } else { + // Second time running Search, now with no filter. const resultsB = await service.ask( @@ -161,10 +162,10 @@ export class AskDialog extends IGBDialog { // If there is some result, answer immediately. if (resultsB && resultsB.answer) { + // Saves some context info. const user = await min.userProfile.get(step.context, {}); - user.isAsking = false; user.lastQuestionId = resultsB.questionId; await min.userProfile.set(step.context, user); @@ -212,9 +213,9 @@ export class AskDialog extends IGBDialog { // Three forms of asking. - if (step.options && step.options['firstTime'] ) { + if (step.options && step.options['firstTime']) { text = Messages[locale].ask_first_time; - } else if (step.options && step.options['isReturning'] ) { + } else if (step.options && step.options['isReturning']) { text = Messages[locale].anything_else; } else if (user.subjects.length > 0) { text = Messages[locale].which_question; diff --git a/packages/kb.gbapp/services/KBService.ts b/packages/kb.gbapp/services/KBService.ts index e94b9634..614d1876 100644 --- a/packages/kb.gbapp/services/KBService.ts +++ b/packages/kb.gbapp/services/KBService.ts @@ -30,11 +30,15 @@ | | \*****************************************************************************/ +/** + * @fileoverview Knowledge base services and logic. + */ + const logger = require('../../../src/logger'); const Path = require('path'); const Fs = require('fs'); -const promise = require('bluebird'); -const parse = promise.promisify(require('csv-parse')); + +const parse = require('bluebird').promisify(require('csv-parse')); const UrlJoin = require('url-join'); const marked = require('marked'); const path = require('path'); @@ -69,6 +73,7 @@ export class KBService { subjects.forEach(subject => { out.push(subject.title); }); + return out.join(', '); } @@ -77,6 +82,7 @@ export class KBService { subjects.forEach(subject => { out.push(subject.internalId); }); + return out.join(' '); } @@ -125,8 +131,10 @@ export class KBService { answerId: question.answerId } }); + return Promise.resolve({ question: question, answer: answer }); } + return Promise.resolve(null); } @@ -163,9 +171,9 @@ export class KBService { query = `${query} ${text}`; } } - // TODO: Filter by instance. what = `${what}&$filter=instanceId eq ${instanceId}` + query = `${query}&$filter=instanceId eq ${instance.instanceId}`; try { - if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') == 'mssql') { + if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') === 'mssql') { const service = new AzureSearch( instance.searchKey, instance.searchHost, @@ -203,6 +211,7 @@ export class KBService { parentId: number ): Promise { const where = { parentSubjectId: parentId, instanceId: instanceId }; + return GuaribasSubject.findAll({ where: where }); @@ -210,7 +219,7 @@ export class KBService { public async getFaqBySubjectArray(from: string, subjects: any): Promise { const where = { - from: from, subject1: null, subject2: null, subject3: null, subject4:null + from: from, subject1: null, subject2: null, subject3: null, subject4: null }; if (subjects) { @@ -230,6 +239,7 @@ export class KBService { where.subject4 = subjects[3].internalId; } } + return await GuaribasQuestion.findAll({ where: where }); @@ -250,6 +260,7 @@ export class KBService { let lastAnswer: GuaribasAnswer; const data = await parse(file, opts); + return asyncPromise.eachSeries(data, async line => { // Extracts values from columns in the current line. @@ -262,7 +273,7 @@ export class KBService { // Skips the first line. - if (!(subjectsText === 'subjects' && from == 'from')) { + if (!(subjectsText === 'subjects' && from === 'from')) { let format = '.txt'; // Extracts answer from external media if any. @@ -281,8 +292,10 @@ export class KBService { // Processes subjects hierarchy splitting by dots. const subjectArray = subjectsText.split('.'); - let subject1: string, subject2: string, subject3: string, - subject4: string; + let subject1: string; + let subject2: string; + let subject3: string; + let subject4: string; let indexer = 0; subjectArray.forEach(element => { diff --git a/packages/whatsapp.gblib/index.ts b/packages/whatsapp.gblib/index.ts index 41829f90..5bc37b89 100644 --- a/packages/whatsapp.gblib/index.ts +++ b/packages/whatsapp.gblib/index.ts @@ -59,7 +59,7 @@ export class GBWhatsappPackage implements IGBPackage { // Only loads engine if it is defined on services.json. - if (min.instance.whatsappBotKey != '') { + if (min.instance.whatsappBotKey) { this.channel = new WhatsappDirectLine(min.botId, min.instance.whatsappBotKey, min.instance.whatsappServiceKey, min.instance.whatsappServiceNumber, min.instance.whatsappServiceUrl, min.instance.whatsappServiceWebhookUrl); } diff --git a/src/app.ts b/src/app.ts index 1726d160..6a60a044 100644 --- a/src/app.ts +++ b/src/app.ts @@ -40,25 +40,17 @@ const logger = require('./logger'); const express = require('express'); const bodyParser = require('body-parser'); -const opn = require('opn'); - import { IGBInstance, IGBPackage } from 'botlib'; -import { GBAdminPackage } from '../packages/admin.gbapp/index'; import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService'; -import { GBAnalyticsPackage } from '../packages/analytics.gblib'; import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService'; -import { GBCorePackage } from '../packages/core.gbapp'; import { GuaribasInstance } from '../packages/core.gbapp/models/GBModel'; import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService'; import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService'; import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService'; import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer'; -import { GBImporter } from '../packages/core.gbapp/services/GBImporter'; +import { GBImporter } from '../packages/core.gbapp/services/GBImporterService'; import { GBMinService } from '../packages/core.gbapp/services/GBMinService'; -import { GBCustomerSatisfactionPackage } from '../packages/customer-satisfaction.gbapp'; -import { GBKBPackage } from '../packages/kb.gbapp'; -import { GBSecurityPackage } from '../packages/security.gblib'; -import { GBWhatsappPackage } from './../packages/whatsapp.gblib/index'; +import { GBVMService } from '../packages/core.gbapp/services/GBVMService'; const appPackages = new Array(); @@ -66,13 +58,11 @@ const appPackages = new Array(); * General Bots open-core entry point. */ export class GBServer { - /** * Program entry-point. */ public static run() { - logger.info(`The Bot Server is in STARTING mode...`); // Creates a basic HTTP server that will serve several URL, one for each @@ -104,136 +94,52 @@ export class GBServer { // Ensures cloud / on-premises infrastructure is setup. logger.info(`Establishing a development local proxy (ngrok)...`); - const proxyAddress = await core.ensureProxy(port); - - const deployer = new GBDeployer(core, new GBImporter(core)); - const azureDeployer = new AzureDeployerService(deployer); - - try { - await core.initDatabase(); - } catch (error) { - logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`); - try { - bootInstance = await azureDeployer.deployFarm(proxyAddress); - } catch (error) { - logger.warn( - 'In case of error, please cleanup any infrastructure objects created during this procedure and .env before running again.' - ); - throw error; - } - core.writeEnv(bootInstance); - logger.info(`File .env written, starting General Bots...`); - GBConfigService.init(); - - await core.initDatabase(); - } - - // TODO: Get .gb* templates from GitHub and download do additional deploy folder. - - // Check admin password. - - const conversationalService = new GBConversationalService(core); - const adminService = new GBAdminService(core); - const password = GBConfigService.get('ADMIN_PASS'); - - if (!GBAdminService.StrongRegex.test(password)) { - throw new Error( - 'Please, define a really strong password in ADMIN_PASS environment variable before running the server.' - ); - } - - // NOTE: the semicolon is necessary before this line. - // Loads all system packages. - - [ - GBAdminPackage, - GBAnalyticsPackage, - GBCorePackage, - GBSecurityPackage, - GBKBPackage, - GBCustomerSatisfactionPackage, - GBWhatsappPackage - ].forEach(e => { - logger.info(`Loading sys package: ${e.name}...`); - const p = Object.create(e.prototype) as IGBPackage; - p.loadPackage(core, core.sequelize); - }); - - // Loads all bot instances from object storage, if it's formatted. - - logger.info(`Loading instances from storage...`); - let instances: GuaribasInstance[]; - try { - instances = await core.loadInstances(); - const instance = instances[0]; - - if (process.env.NODE_ENV === 'development') { - logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`); - - await azureDeployer.updateBotProxy( - instance.botId, - instance.botId, - `${proxyAddress}/api/messages/${instance.botId}` - ); - } - } catch (error) { - if (error.parent.code === 'ELOGIN') { - const group = GBConfigService.get('CLOUD_GROUP'); - const serverName = GBConfigService.get('STORAGE_SERVER').split( - '.database.windows.net' - )[0]; - await azureDeployer.openStorageFirewall(group, serverName); - } else { - // Check if storage is empty and needs formatting. - - const isInvalidObject = - error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE. - - if (isInvalidObject) { - if (GBConfigService.get('STORAGE_SYNC') != 'true') { - throw new Error(`Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${ - error.message - }.`); - } else { - logger.info( - `Storage is empty. After collecting storage structure from all .gbapps it will get synced.` - ); - } - } else { - throw new Error(`Cannot connect to operating storage: ${error.message}.`); - } - } - } - - // Deploy packages and format object store according to .gbapp storage models. + + const proxyAddress: string = await core.ensureProxy(port); logger.info(`Deploying packages...`); + const importer: GBImporter = new GBImporter(core); + const deployer: GBDeployer = new GBDeployer(core, importer); + const azureDeployer: AzureDeployerService = new AzureDeployerService(deployer); + const adminService: GBAdminService = new GBAdminService(core); + const conversationalService: GBConversationalService = new GBConversationalService(core); + core.ensureAdminIsSecured(); + + let bootInstance: IGBInstance = null; + try { + await core.initStorage(); + } catch (error) { + bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress); + await core.initStorage(); + } + + await core.loadSysPackages(core); + await core.checkStorage(azureDeployer); await deployer.deployPackages(core, server, appPackages); - // If instances is undefined here it's because storage has been formatted. - // Load all instances from .gbot found on deploy package directory. - if (!instances) { - const saveInstance = new GuaribasInstance(bootInstance); - await saveInstance.save(); - instances = await core.loadInstances(); - } - - // Setup server dynamic (per bot instance) resources and listeners. - logger.info(`Publishing instances...`); - const minService = new GBMinService( - core, - conversationalService, - adminService, - deployer + const packageInstance = await importer.importIfNotExistsBotPackage( + GBConfigService.get('CLOUD_GROUP'), + 'boot.gbot', + 'packages/boot.gbot' ); - await minService.buildMin(server, appPackages, instances); - logger.info(`The Bot Server is in RUNNING mode...`); - - if (process.env.NODE_ENV === 'development') { - opn('http://localhost:4242'); + const fullInstance = Object.assign(packageInstance, bootInstance); + await core.saveInstance(fullInstance); + let instances: GuaribasInstance[] = await core.loadAllInstances(core, azureDeployer, proxyAddress); + instances = await core.ensureInstances(instances, bootInstance, core); + if(!bootInstance) { + bootInstance = instances[0]; } + const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer); + await minService.buildMin(bootInstance, server, appPackages, instances, deployer); + + logger.info(`Preparing default.gbui (it may take some additional time for the first time)...`); + deployer.installDefaultGBUI(); + + logger.info(`The Bot Server is in RUNNING mode...`); + core.openBrowserInDevelopment(); + return core; } catch (err) { logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`); @@ -246,4 +152,10 @@ export class GBServer { // First line to run. +// const path = 'packages/default.gbdialog'; +// const file = 'bot.vbs'; +// const source =(path + '/' + file); +// let s = new GBVMService(); +// s.run(source, path, null, null, null) + GBServer.run(); diff --git a/src/logger.ts b/src/logger.ts index 6a77c89a..ee201a78 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -30,6 +30,10 @@ | | \*****************************************************************************/ +/** + * @fileoverview Logging support. + */ + const { createLogger, format, transports } = require('winston'); const config = { diff --git a/tslint.json b/tslint.json index 99e6879a..1b26d002 100644 --- a/tslint.json +++ b/tslint.json @@ -15,6 +15,8 @@ ], "jsRules": {}, "rules": { + "no-var-requires":false, + "typedef":false, "variable-name": false, "no-parameter-properties": false, "no-reserved-keywords": false, @@ -34,6 +36,6 @@ "export-name":false, "no-relative-imports": false, "no-backbone-get-set-outside-model": false, - "max-line-length": [true,{"limit":80,"ignore-pattern":"^\\s+\\*"}] + "max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}] } }