diff --git a/package-lock.json b/package-lock.json index f425e813..ab8e08b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "botserver", - "version": "2.0.45", + "version": "2.0.79", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -87,15 +87,10 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -1325,28 +1320,6 @@ "dev": true, "optional": true }, - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - }, - "dependencies": { - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - } - } - }, "@derhuerst/http-basic": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.1.tgz", @@ -4569,9 +4542,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.46.tgz", - "integrity": "sha512-Tice8a+sJtlP9C1EUo0DYyjq52T37b3LexVu3p871+kfIBIN+OQ7PKPei1oF3MgF39olEpUfxaLtD+QFc1k69Q==" + "version": "10.17.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.50.tgz", + "integrity": "sha512-vwX+/ija9xKc/z9VqMCdbf4WYcMTGsI0I/L/6shIF3qXURxZOhPQlPRHtjTpiNhAwn0paMJzlOQqw6mAGEQnTA==" } } }, @@ -4671,9 +4644,9 @@ } }, "botlib": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/botlib/-/botlib-1.7.0.tgz", - "integrity": "sha512-U1kccXjruGNRWvAM/bvJ+r13hh/aWjrxk/Ws5jWZlOJeEmNMgO6yCNVjOp7ZaPYO7w4l/Tver2w8BGN9zZ4ibw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/botlib/-/botlib-1.7.1.tgz", + "integrity": "sha512-Nu518PUt9o8ezuhOMhKSnbS0qkHt6XAR2RVzxEbyDpxyVyD7zKcBsHvnhTx8/jdRLR9O5MuHa4dD4jSxWWuSww==", "requires": { "async": "3.2.0", "botbuilder": "4.11.0", @@ -4686,10 +4659,10 @@ "ms": "2.1.2", "pragmatismo-io-framework": "1.0.20", "reflect-metadata": "0.1.13", - "sequelize": "6.3.5", + "sequelize": "5.21.5", "sequelize-typescript": "1.1.0", "underscore": "1.11.0", - "winston": "3.3.3" + "winston": "3.2.1" }, "dependencies": { "async": { @@ -4697,14 +4670,6 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, "iconv-lite": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", @@ -4713,84 +4678,10 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "sequelize": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.3.5.tgz", - "integrity": "sha512-MiwiPkYSA8NWttRKAXdU9h0TxP6HAc1fl7qZmMO/VQqQOND83G4nZLXd0kWILtAoT9cxtZgFqeb/MPYgEeXwsw==", - "requires": { - "debug": "^4.1.1", - "dottie": "^2.0.0", - "inflection": "1.12.0", - "lodash": "^4.17.15", - "moment": "^2.26.0", - "moment-timezone": "^0.5.31", - "retry-as-promised": "^3.2.0", - "semver": "^7.3.2", - "sequelize-pool": "^6.0.0", - "toposort-class": "^1.0.1", - "uuid": "^8.1.0", - "validator": "^10.11.0", - "wkx": "^0.5.0" - } - }, "underscore": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==" - }, - "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" - }, - "validator": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", - "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } } } }, @@ -5725,8 +5616,7 @@ "colornames": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", - "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", - "dev": true + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" }, "colors": { "version": "1.4.0", @@ -7376,7 +7266,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", - "dev": true, "requires": { "colorspace": "1.1.x", "enabled": "1.0.x", @@ -7667,7 +7556,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", - "dev": true, "requires": { "env-variable": "0.0.x" } @@ -7792,8 +7680,7 @@ "env-variable": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", - "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==", - "dev": true + "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" }, "epub2": { "version": "1.3.4", @@ -8820,11 +8707,6 @@ "integrity": "sha512-iEjGZ94OBMcESxnLorXNjJmtd/JtQYXUVrQpfwvtAKkuyawRmv+2LM6nqyOsOJkISEYbyY6ziudRE0u4VyPSVA==", "dev": true }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -11111,7 +10993,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", - "dev": true, "requires": { "colornames": "^1.1.1" } @@ -16901,8 +16782,7 @@ "one-time": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", - "dev": true + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" }, "onetime": { "version": "5.1.0", @@ -19601,11 +19481,6 @@ } } }, - "sequelize-pool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz", - "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==" - }, "sequelize-typescript": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/sequelize-typescript/-/sequelize-typescript-1.1.0.tgz", @@ -21766,6 +21641,26 @@ } } }, + "tslint-microsoft-contrib": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.2.0.tgz", + "integrity": "sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==", + "dev": true, + "requires": { + "tsutils": "^2.27.2 <2.29.0" + }, + "dependencies": { + "tsutils": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", + "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "tsutils": { "version": "3.17.1", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", @@ -22087,8 +21982,7 @@ "universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" }, "universalify": { "version": "0.1.2", @@ -22559,7 +22453,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", - "dev": true, "requires": { "async": "^2.6.1", "diagnostics": "^1.1.1", @@ -22576,7 +22469,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -22594,14 +22486,6 @@ "triple-beam": "^1.2.0" } }, - "wkx": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", - "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", - "requires": { - "@types/node": "*" - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index 1b80a7a4..aaeebb27 100644 --- a/package.json +++ b/package.json @@ -112,9 +112,6 @@ "washyourmouthoutwithsoap": "1.0.2" }, "devDependencies": { - "ngrok": "3.3.0", - "typedoc": "0.19.2", - "simple-commit-message": "4.0.13", "@types/url-join": "4.0.0", "@types/winston": "2.4.4", "ban-sensitive-files": "1.9.14", @@ -123,12 +120,16 @@ "dependency-check": "4.1.0", "git-issues": "1.3.1", "license-checker": "25.0.1", + "ngrok": "3.3.0", "nsp": "3.2.1", "prettier-standard": "16.4.1", "semantic-release": "17.2.4", + "simple-commit-message": "4.0.13", "travis-deploy-once": "5.0.11", "ts-node": "9.0.0", - "tslint": "6.1.2" + "tslint": "6.1.2", + "tslint-microsoft-contrib": "^6.2.0", + "typedoc": "0.19.2" }, "eslintConfig": { "env": { diff --git a/packages/admin.gbapp/index.ts b/packages/admin.gbapp/index.ts index 1aebd619..55a1b077 100644 --- a/packages/admin.gbapp/index.ts +++ b/packages/admin.gbapp/index.ts @@ -63,7 +63,6 @@ export class GBAdminPackage implements IGBPackage { GBLog.verbose(`onExchangeData called.`); } - public async loadPackage(core: IGBCoreService, sequelize: Sequelize): Promise { core.sequelize.addModels([GuaribasAdmin]); } diff --git a/packages/admin.gbapp/services/GBAdminService.ts b/packages/admin.gbapp/services/GBAdminService.ts index 09d02878..bc9dc7f6 100644 --- a/packages/admin.gbapp/services/GBAdminService.ts +++ b/packages/admin.gbapp/services/GBAdminService.ts @@ -37,15 +37,15 @@ 'use strict'; import { AuthenticationContext, TokenResponse } from 'adal-node'; -import { IGBAdminService, IGBCoreService, IGBInstance, GBMinInstance, GBLog, IGBDeployer } from 'botlib'; +import { GBLog, GBMinInstance, IGBAdminService, IGBCoreService, IGBDeployer, IGBInstance } from 'botlib'; import urlJoin = require('url-join'); import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService'; import { GuaribasInstance } from '../../core.gbapp/models/GBModel'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; -import { GuaribasAdmin } from '../models/AdminModel'; -import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointService'; -import { GBImporter } from '../../core.gbapp/services/GBImporterService'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; +import { GBImporter } from '../../core.gbapp/services/GBImporterService'; +import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointService'; +import { GuaribasAdmin } from '../models/AdminModel'; const Path = require('path'); const msRestAzure = require('ms-rest-azure'); const PasswordGenerator = require('strict-password-generator').default; @@ -145,6 +145,57 @@ export class GBAdminService implements IGBAdminService { return passwordGenerator.generatePassword(options); } + public static async undeployPackageCommand(text: any, min: GBMinInstance) { + const packageName = text.split(' ')[1]; + const importer = new GBImporter(min.core); + const deployer = new GBDeployer(min.core, importer); + const localFolder = Path.join('work', `${min.instance.botId}.gbai`, Path.basename(packageName)); + await deployer.undeployPackageFromLocalPath(min.instance, localFolder); + } + + public static isSharePointPath(path: string) { + return path.indexOf('sharepoint.com') > 0; + } + public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: IGBDeployer) { + const packageName = text.split(' ')[1]; + + if (!this.isSharePointPath(packageName)) { + const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH'); + if (additionalPath === undefined) { + throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.'); + } + await deployer.deployPackage(min, urlJoin(additionalPath, packageName)); + } else { + const siteName = text.split(' ')[1]; + const folderName = text.split(' ')[2]; + + const s = new GBSharePointService(); + + const localFolder = Path.join('work', `${min.instance.botId}.gbai`, Path.basename(folderName)); + GBLog.warn(`${GBConfigService.get('CLOUD_USERNAME')} must be authorized on SharePoint related site`); + await s.downloadFolder( + localFolder, + siteName, + folderName, + GBConfigService.get('CLOUD_USERNAME'), + GBConfigService.get('CLOUD_PASSWORD') + ); + await deployer.deployPackage(min, localFolder); + } + } + public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: IGBDeployer) { + await deployer.rebuildIndex( + min.instance, + new AzureDeployerService(deployer).getKBSearchSchema(min.instance.searchIndex) + ); + } + + public static async syncBotServerCommand(min: GBMinInstance, deployer: GBDeployer) { + const serverName = `${min.instance.botId}-server`; + const service = await AzureDeployerService.createInstance(deployer); + service.syncBotServerRepository(min.instance.botId, serverName); + } + public async setValue(instanceId: number, key: string, value: string) { const options = { where: {} }; options.where = { key: key }; @@ -183,7 +234,7 @@ export class GBAdminService implements IGBAdminService { public async acquireElevatedToken(instanceId: number): Promise { // TODO: Use boot bot as base for authentication. - let botId = GBConfigService.get('BOT_ID'); + const botId = GBConfigService.get('BOT_ID'); instanceId = (await this.core.loadInstanceByBotId(botId)).instanceId; return new Promise(async (resolve, reject) => { @@ -228,56 +279,5 @@ export class GBAdminService implements IGBAdminService { }); } - public static async undeployPackageCommand(text: any, min: GBMinInstance) { - const packageName = text.split(' ')[1]; - const importer = new GBImporter(min.core); - const deployer = new GBDeployer(min.core, importer); - let localFolder = Path.join('work', `${min.instance.botId}.gbai`, Path.basename(packageName)); - await deployer.undeployPackageFromLocalPath(min.instance, localFolder); - } - - public static isSharePointPath(path: string) { - return path.indexOf('sharepoint.com') > 0; - } - public async publish(min: GBMinInstance, packageName: string, republish: boolean): Promise { } - public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: IGBDeployer) { - const packageName = text.split(' ')[1]; - - if (!this.isSharePointPath(packageName)) { - const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH'); - if (additionalPath === undefined) { - throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.'); - } - await deployer.deployPackage(min, urlJoin(additionalPath, packageName)); - } else { - let siteName = text.split(' ')[1]; - let folderName = text.split(' ')[2]; - - let s = new GBSharePointService(); - - let localFolder = Path.join('work', `${min.instance.botId}.gbai`, Path.basename(folderName)); - GBLog.warn(`${GBConfigService.get('CLOUD_USERNAME')} must be authorized on SharePoint related site`); - await s.downloadFolder( - localFolder, - siteName, - folderName, - GBConfigService.get('CLOUD_USERNAME'), - GBConfigService.get('CLOUD_PASSWORD') - ); - await deployer.deployPackage(min, localFolder); - } - } - public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: IGBDeployer) { - await deployer.rebuildIndex( - min.instance, - new AzureDeployerService(deployer).getKBSearchSchema(min.instance.searchIndex) - ); - } - - public static async syncBotServerCommand(min: GBMinInstance, deployer: GBDeployer) { - const serverName = `${min.instance.botId}-server`; - const service = await AzureDeployerService.createInstance(deployer); - service.syncBotServerRepository(min.instance.botId, serverName); - } } diff --git a/packages/admin.gbapp/strings.ts b/packages/admin.gbapp/strings.ts index 6236d642..752b52c6 100644 --- a/packages/admin.gbapp/strings.ts +++ b/packages/admin.gbapp/strings.ts @@ -22,7 +22,7 @@ export const Messages = { publish_must_be_admin: 'Seu telefone precisa estar com privilégios administrativos para realizar publicação.', publish_success: 'Publicação realizada.', publish_type_yes: 'Por favor, digite *Sim* para continuar com a publicação.', - publish_canceled: 'Publicação cancelada.', + publish_canceled: 'Publicação cancelada.' }, 'pt-BR': { authenticate: 'Please, authenticate:', @@ -47,6 +47,6 @@ export const Messages = { publish_must_be_admin: 'Seu telefone precisa estar com privilégios administrativos para realizar publicação.', publish_success: 'Publicação realizada.', publish_type_yes: 'Por favor, digite *Sim* para continuar com a publicação.', - publish_canceled: 'Publicação cancelada.', + publish_canceled: 'Publicação cancelada.' } }; diff --git a/packages/analytics.gblib/models/index.ts b/packages/analytics.gblib/models/index.ts index 275de45e..21a96a18 100644 --- a/packages/analytics.gblib/models/index.ts +++ b/packages/analytics.gblib/models/index.ts @@ -58,7 +58,6 @@ import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBMod import { GuaribasSubject } from '../../kb.gbapp/models'; import { GuaribasUser } from '../../security.gbapp/models'; - /** * A conversation that groups many messages. */ @@ -74,7 +73,6 @@ export class GuaribasConversation extends Model { @Column public instanceId: number; - @ForeignKey(() => GuaribasSubject) @Column public startSubjectId: number; @@ -110,7 +108,6 @@ export class GuaribasConversation extends Model { public startedBy: GuaribasUser; } - /** * A single message in a conversation. */ diff --git a/packages/analytics.gblib/services/AnalyticsService.ts b/packages/analytics.gblib/services/AnalyticsService.ts index 34ce1a82..99b84a9c 100644 --- a/packages/analytics.gblib/services/AnalyticsService.ts +++ b/packages/analytics.gblib/services/AnalyticsService.ts @@ -34,10 +34,10 @@ * @fileoverview General Bots server core. */ +import { AzureText } from 'pragmatismo-io-framework'; +import { GBServer } from '../../../src/app'; import { GuaribasUser } from '../../security.gbapp/models'; import { GuaribasConversation, GuaribasConversationMessage } from '../models'; -import { GBServer } from '../../../src/app'; -import { AzureText } from 'pragmatismo-io-framework'; /** * Base services for Bot Analytics. @@ -55,16 +55,19 @@ export class AnalyticsService { return await conversation.save(); } - public async updateConversationSuggestion(instanceId: number, conversationId: string, feedback: string, locale: string): Promise { - + public async updateConversationSuggestion(instanceId: number, + conversationId: string, feedback: string, locale: string): Promise { + const minBoot = GBServer.globals.minBoot as any; const rate = await AzureText.getSentiment( - minBoot.instance.textAnalyticsKey ? minBoot.instance.textAnalyticsKey : minBoot.instance.textAnalyticsKey, - minBoot.instance.textAnalyticsEndpoint ? minBoot.instance.textAnalyticsEndpoint : minBoot.instance.textAnalyticsEndpoint, + minBoot.instance.textAnalyticsKey ? minBoot.instance.textAnalyticsKey : + minBoot.instance.textAnalyticsKey, + minBoot.instance.textAnalyticsEndpoint ? minBoot.instance.textAnalyticsEndpoint : + minBoot.instance.textAnalyticsEndpoint, locale, feedback ); - + const options = { where: { } }; options.where = { conversationId: conversationId, instanceId: instanceId }; const item = await GuaribasConversation.findOne(options); @@ -78,7 +81,6 @@ export class AnalyticsService { } - public async createMessage( instanceId: number, conversation: GuaribasConversation, diff --git a/packages/azuredeployer.gbapp/dialogs/StartDialog.ts b/packages/azuredeployer.gbapp/dialogs/StartDialog.ts index 762665e8..f1d08398 100644 --- a/packages/azuredeployer.gbapp/dialogs/StartDialog.ts +++ b/packages/azuredeployer.gbapp/dialogs/StartDialog.ts @@ -104,7 +104,7 @@ export class StartDialog { const instance = {}; instance.botId = botId; - instance.state ='active'; + instance.state = 'active'; instance.cloudUsername = username; instance.cloudPassword = password; instance.cloudSubscriptionId = subscriptionId; diff --git a/packages/basic.gblib/index.ts b/packages/basic.gblib/index.ts index fcfb3c4e..b87e9f46 100644 --- a/packages/basic.gblib/index.ts +++ b/packages/basic.gblib/index.ts @@ -37,7 +37,7 @@ 'use strict'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; -import { GuaribasSchedule } from 'packages/core.gbapp/models/GBModel'; +import { GuaribasSchedule } from '../core.gbapp/models/GBModel'; import { Sequelize } from 'sequelize-typescript'; diff --git a/packages/basic.gblib/models/Model.ts b/packages/basic.gblib/models/Model.ts index d4944305..e90a081e 100644 --- a/packages/basic.gblib/models/Model.ts +++ b/packages/basic.gblib/models/Model.ts @@ -54,7 +54,7 @@ import { GuaribasInstance } from '../../core.gbapp/models/GBModel'; @Table //tslint:disable-next-line:max-classes-per-file export class GuaribasSchedule extends Model { - + @Column public name: string; diff --git a/packages/basic.gblib/services/DialogKeywords.ts b/packages/basic.gblib/services/DialogKeywords.ts index 438010d3..7b450ae0 100644 --- a/packages/basic.gblib/services/DialogKeywords.ts +++ b/packages/basic.gblib/services/DialogKeywords.ts @@ -32,14 +32,14 @@ 'use strict'; -import { TurnContext, BotAdapter } from 'botbuilder'; -import { WaterfallStepContext, WaterfallDialog } from 'botbuilder-dialogs'; +import { BotAdapter, TurnContext } from 'botbuilder'; +import { WaterfallDialog, WaterfallStepContext } from 'botbuilder-dialogs'; import { GBLog, GBMinInstance } from 'botlib'; import urlJoin = require('url-join'); -import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; -import { Messages } from '../strings'; import { GBServer } from '../../../src/app'; +import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { SecService } from '../../security.gbapp/services/SecService'; +import { Messages } from '../strings'; import { SystemKeywords } from './SystemKeywords'; /** @@ -48,8 +48,8 @@ import { SystemKeywords } from './SystemKeywords'; */ export class DialogKeywords { - /** - * Reference to minimal bot instance. + /** + * Reference to minimal bot instance. */ public min: GBMinInstance; @@ -77,17 +77,17 @@ export class DialogKeywords { /** * Returns the today data filled in dd/mm/yyyy or mm/dd/yyyy. - * + * * @example x = TODAY */ public async getToday(step) { - var d = new Date(), + let d = new Date(), month = '' + (d.getMonth() + 1), day = '' + d.getDate(), year = d.getFullYear(); - if (month.length < 2) month = '0' + month; - if (day.length < 2) day = '0' + day; + if (month.length < 2) { month = '0' + month; } + if (day.length < 2) { day = '0' + day; } const locale = step.context.activity.locale; switch (locale) { @@ -104,7 +104,7 @@ export class DialogKeywords { /** * Quits the dialog, currently required to get out of VM context. - * + * * @example EXIT */ public async exit(step) { @@ -113,24 +113,24 @@ export class DialogKeywords { /** * Returns current time in format hh:dd. - * + * * @example SAVE "file.xlsx", name, email, NOW - * + * */ public async getNow() { const nowUTC = new Date(); - const now = new Date((typeof nowUTC === "string" ? + const now = new Date((typeof nowUTC === 'string' ? new Date(nowUTC) : - nowUTC).toLocaleString("en-US", { timeZone: process.env.DEFAULT_TIMEZONE })); + nowUTC).toLocaleString('en-US', { timeZone: process.env.DEFAULT_TIMEZONE })); return now.getHours() + ':' + now.getMinutes(); } /** * Sends a file to a given mobile. - * + * * @example SEND FILE TO "+199988887777", "image.jpg" - * + * */ public async sendFileTo(mobile, filename, caption) { return await this.internalSendFile(null, mobile, filename, caption); @@ -138,47 +138,24 @@ export class DialogKeywords { /** * Sends a file to the current user. - * + * * @example SEND FILE "image.jpg" - * + * */ public async sendFile(step, filename, caption) { return await this.internalSendFile(step, null, filename, caption); } - /** - * Processes the sending of the file. - */ - private async internalSendFile(step, mobile, filename, caption) { - if (filename.indexOf('.md') > -1) { - GBLog.info(`BASIC: Sending the contents of ${filename} markdown to mobile.`); - let md = await this.min.kbService.getAnswerTextByMediaName(this.min.instance.instanceId, filename); - await this.min.conversationalService.sendMarkdownToMobile(this.min, step, mobile, md); - } else { - GBLog.info(`BASIC: Sending the file ${filename} to mobile.`); - let url = urlJoin( - GBServer.globals.publicAddress, - 'kb', - `${this.min.botId}.gbai`, - `${this.min.botId}.gbkb`, - 'assets', - filename - ); - - await this.min.conversationalService.sendFile(this.min, step, mobile, url, caption); - } - } - /** * Defines the current language of the bot conversation. - * + * * @example SET LANGUAGE "pt" - * + * */ public async setLanguage(step, language) { const user = await this.min.userProfile.get(step.context, {}); - let sec = new SecService(); + const sec = new SecService(); user.systemUser = await sec.updateUserLocale(user.systemUser.userId, language); await this.min.userProfile.set(step.context, user); @@ -190,12 +167,20 @@ export class DialogKeywords { public async userName(step) { return step.context.activity.from.name; } - + + /** + * OBSOLETE. + */ + public async getFrom(step) { + return await this.userMobile(step); + } + + /** * Returns current mobile number from user in conversation. - * + * * @example SAVE "file.xlsx", name, email, MOBILE - * + * */ public async userMobile(step) { if (isNaN(step.context.activity.from.id)) { @@ -207,9 +192,9 @@ export class DialogKeywords { /** * Shows the subject menu to the user - * + * * @example MENU - * + * */ public async showMenu(step) { return await step.beginDialog('/menu'); @@ -217,9 +202,9 @@ export class DialogKeywords { /** * Performs the transfer of the conversation to a human agent. - * + * * @example TRANSFER - * + * */ public async transfer(step) { return await step.beginDialog('/t'); @@ -227,9 +212,9 @@ export class DialogKeywords { /** * Hears something from user and put it in a variable - * + * * @example HEAR name - * + * */ public async hear(step, promise, previousResolve, kind, ...args) { function random(low, high) { @@ -253,4 +238,27 @@ export class DialogKeywords { public async talk(step, text: string) { return await this.min.conversationalService.sendText(this.min, step, text); } + + /** + * Processes the sending of the file. + */ + private async internalSendFile(step, mobile, filename, caption) { + if (filename.indexOf('.md') > -1) { + GBLog.info(`BASIC: Sending the contents of ${filename} markdown to mobile.`); + const md = await this.min.kbService.getAnswerTextByMediaName(this.min.instance.instanceId, filename); + await this.min.conversationalService.sendMarkdownToMobile(this.min, step, mobile, md); + } else { + GBLog.info(`BASIC: Sending the file ${filename} to mobile.`); + const url = urlJoin( + GBServer.globals.publicAddress, + 'kb', + `${this.min.botId}.gbai`, + `${this.min.botId}.gbkb`, + 'assets', + filename + ); + + await this.min.conversationalService.sendFile(this.min, step, mobile, url, caption); + } + } } diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index afe31b17..f3535f96 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -38,7 +38,6 @@ import * as fs from 'fs'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { TSCompiler } from './TSCompiler'; import { CollectionUtil } from 'pragmatismo-io-framework'; -const walkPromise = require('walk-promise'); import urlJoin = require('url-join'); import { DialogKeywords } from './DialogKeywords'; import { Messages } from '../strings'; @@ -47,7 +46,8 @@ import { GBConversationalService } from '../../core.gbapp/services/GBConversatio const vm = require('vm'); const vb2ts = require('./vbscript-to-typescript'); const beautify = require('js-beautify').js; -var textract = require('textract'); +const textract = require('textract'); +const walkPromise = require('walk-promise'); const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); const phone = require('phone'); @@ -678,7 +678,7 @@ export class GBVMService extends GBService { const promise = min.cbMap[id].promise; delete min.cbMap[id]; try { - + await promise(step, result); if (step.activeDialog.state.options.previousResolve != undefined) { step.activeDialog.state.options.previousResolve(); diff --git a/packages/basic.gblib/strings.ts b/packages/basic.gblib/strings.ts index 4bada273..379df8b8 100644 --- a/packages/basic.gblib/strings.ts +++ b/packages/basic.gblib/strings.ts @@ -9,12 +9,12 @@ export const Messages = { hi: (msg) => `Hello, ${msg}.`, very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`, canceled: 'Canceled. If I can be useful, let me know how', - whats_email: "What's your E-mail address?", - which_language: "Please, type the language name you would like to talk through.", - validation_enter_valid_email: "Please enter a valid e-mail." , - language_chosen: "Very good, so let's go..." , - affirmative_sentences: /^(sim|s|positivo|afirmativo|claro|evidente|sem dúvida|confirmo|confirmar|confirmado|uhum|si|y|yes|sure)/i, - + whats_email: 'What\'s your E-mail address?', + which_language: 'Please, type the language name you would like to talk through.', + validation_enter_valid_email: 'Please enter a valid e-mail.' , + language_chosen: 'Very good, so let\'s go...' , + affirmative_sentences: /^(sim|s|positivo|afirmativo|claro|evidente|sem dúvida|confirmo|confirmar|confirmado|uhum|si|y|yes|sure)/i + }, 'pt-BR': { show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...', @@ -24,11 +24,11 @@ export const Messages = { hi: (msg) => `Oi, ${msg}.`, very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`, canceled: 'Cancelado, avise como posso ser útil novamente.', - whats_email: "Qual seu e-mail?", - which_language: "Por favor, digite o idioma que você gostaria de usar para conversarmos.", - validation_enter_valid_email: "Por favor digite um email válido.", - language_chosen: "Muito bem, então vamos lá..." , - affirmative_sentences: /^(sim|s|positivo|afirmativo|claro|evidente|sem dúvida|confirmo|confirmar|confirmado|uhum|si|y|yes|sure)/i, + whats_email: 'Qual seu e-mail?', + which_language: 'Por favor, digite o idioma que você gostaria de usar para conversarmos.', + validation_enter_valid_email: 'Por favor digite um email válido.', + language_chosen: 'Muito bem, então vamos lá...' , + affirmative_sentences: /^(sim|s|positivo|afirmativo|claro|evidente|sem dúvida|confirmo|confirmar|confirmado|uhum|si|y|yes|sure)/i } }; diff --git a/packages/console.gblib/index.ts b/packages/console.gblib/index.ts index 7810d375..3521f7ce 100644 --- a/packages/console.gblib/index.ts +++ b/packages/console.gblib/index.ts @@ -64,8 +64,6 @@ export class GBConsolePackage implements IGBPackage { public async onExchangeData(min: GBMinInstance, kind: string, data: any) { GBLog.verbose(`onExchangeData called.`); } - - public async loadBot(min: GBMinInstance): Promise { this.channel = new ConsoleDirectLine(min.instance.webchatKey); } diff --git a/packages/core.gbapp/dialogs/SwitchBot.ts b/packages/core.gbapp/dialogs/SwitchBot.ts index f463e63d..c51b84ba 100644 --- a/packages/core.gbapp/dialogs/SwitchBot.ts +++ b/packages/core.gbapp/dialogs/SwitchBot.ts @@ -39,10 +39,10 @@ import { BotAdapter } from 'botbuilder'; import { WaterfallDialog } from 'botbuilder-dialogs'; import { GBMinInstance, IGBDialog } from 'botlib'; -import { Messages } from '../strings'; -import { SecService } from '../../security.gbapp/services/SecService'; import { GBServer } from '../../../src/app'; +import { SecService } from '../../security.gbapp/services/SecService'; import { GBConversationalService } from '../services/GBConversationalService'; +import { Messages } from '../strings'; /** * Dialog for the bot explains about itself. */ @@ -59,16 +59,16 @@ export class SwitchBotDialog extends IGBDialog { async step => { const locale = step.context.activity.locale; - return await min.conversationalService.prompt (min, step, "Qual seria o código de ativação?"); + return await min.conversationalService.prompt (min, step, 'Qual seria o código de ativação?'); }, async step => { - let sec = new SecService(); - let from = step.context.activity.from.id; + const sec = new SecService(); + const from = step.context.activity.from.id; const botId = step.result; const instance = await min.core.loadInstanceByBotId(botId); await sec.updateUserInstance(from, instance.instanceId); await min.conversationalService.sendText(min, step, `Opa, vamos lá!`); - + return await step.next(); } ])); diff --git a/packages/core.gbapp/dialogs/WelcomeDialog.ts b/packages/core.gbapp/dialogs/WelcomeDialog.ts index 8463a4b8..a323df5e 100644 --- a/packages/core.gbapp/dialogs/WelcomeDialog.ts +++ b/packages/core.gbapp/dialogs/WelcomeDialog.ts @@ -39,9 +39,9 @@ import { BotAdapter } from 'botbuilder'; import {WaterfallDialog } from 'botbuilder-dialogs'; import { GBMinInstance, IGBDialog } from 'botlib'; -import { Messages } from '../strings'; import { GBServer } from '../../../src/app'; import { GBConversationalService } from '../services/GBConversationalService'; +import { Messages } from '../strings'; /** * Dialog for Welcoming people. @@ -59,16 +59,15 @@ export class WelcomeDialog extends IGBDialog { async step => { if (GBServer.globals.entryPointDialog !== null && - min.instance.botId === process.env.BOT_ID && - step.context.activity.channelId === "webchat") - { + min.instance.botId === process.env.BOT_ID && + step.context.activity.channelId === 'webchat') { return step.replaceDialog(GBServer.globals.entryPointDialog); } const user = await min.userProfile.get(step.context, {}); const locale = step.context.activity.locale; - if (!user.once && step.context.activity.channelId === "webchat") { + if (!user.once && step.context.activity.channelId === 'webchat') { user.once = true; await min.userProfile.set(step.context, user); const a = new Date(); @@ -81,7 +80,7 @@ export class WelcomeDialog extends IGBDialog { : Messages[locale].good_night; await min.conversationalService.sendText(min, step, Messages[locale].hi(msg)); - + await step.replaceDialog('/ask', { firstTime: true }); if ( diff --git a/packages/core.gbapp/dialogs/WhoAmIDialog.ts b/packages/core.gbapp/dialogs/WhoAmIDialog.ts index 1736742e..76c232ce 100644 --- a/packages/core.gbapp/dialogs/WhoAmIDialog.ts +++ b/packages/core.gbapp/dialogs/WhoAmIDialog.ts @@ -39,8 +39,8 @@ import { BotAdapter } from 'botbuilder'; import { WaterfallDialog } from 'botbuilder-dialogs'; import { GBMinInstance, IGBDialog } from 'botlib'; -import { Messages } from '../strings'; import { GBConversationalService } from '../services/GBConversationalService'; +import { Messages } from '../strings'; /** * Dialog for the bot explains about itself. */ diff --git a/packages/core.gbapp/index.ts b/packages/core.gbapp/index.ts index d66df032..21181c10 100644 --- a/packages/core.gbapp/index.ts +++ b/packages/core.gbapp/index.ts @@ -50,7 +50,8 @@ import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } */ export class GBCorePackage implements IGBPackage { public sysPackages: IGBPackage[]; - public CurrentEngineName = "guaribas-1.0.0"; + public CurrentEngineName = 'guaribas-1.0.0'; + public async loadPackage(core: IGBCoreService, sequelize: Sequelize): Promise { core.sequelize.addModels([GuaribasInstance, GuaribasPackage, GuaribasChannel, GuaribasException]); } @@ -71,7 +72,6 @@ export class GBCorePackage implements IGBPackage { GBLog.verbose(`onExchangeData called.`); } - public async loadBot(min: GBMinInstance): Promise { WelcomeDialog.setup(min.bot, min); WhoAmIDialog.setup(min.bot, min); diff --git a/packages/core.gbapp/models/GBModel.ts b/packages/core.gbapp/models/GBModel.ts index 56703367..04257b73 100644 --- a/packages/core.gbapp/models/GBModel.ts +++ b/packages/core.gbapp/models/GBModel.ts @@ -102,7 +102,6 @@ export class GuaribasInstance extends Model @Column public textAnalyticsEndpoint: string; - @Column({ type: DataType.STRING(64) }) public translatorKey: string; @@ -333,7 +332,7 @@ export class GuaribasException extends Model { @Table //tslint:disable-next-line:max-classes-per-file export class GuaribasApplications extends Model { - + @Column public name: string; @@ -353,11 +352,10 @@ export class GuaribasApplications extends Model { public updatedAt: Date; } - @Table //tslint:disable-next-line:max-classes-per-file export class GuaribasSchedule extends Model { - + @Column public name: string; diff --git a/packages/core.gbapp/services/GBConfigService.ts b/packages/core.gbapp/services/GBConfigService.ts index e26a6b58..b6e372dd 100644 --- a/packages/core.gbapp/services/GBConfigService.ts +++ b/packages/core.gbapp/services/GBConfigService.ts @@ -42,7 +42,7 @@ import { GBLog } from 'botlib'; * Base configuration for the server like storage. */ export class GBConfigService { - static getBoolean(value: string): boolean { + public static getBoolean(value: string): boolean { return this.get(value) as unknown as boolean; } public static getServerPort(): string { diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts index 22177b37..102b245b 100644 --- a/packages/core.gbapp/services/GBDeployer.ts +++ b/packages/core.gbapp/services/GBDeployer.ts @@ -44,21 +44,21 @@ const child_process = require('child_process'); const graph = require('@microsoft/microsoft-graph-client'); const rimraf = require('rimraf'); -import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage, IGBDeployer } from 'botlib'; +import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBDeployer, IGBInstance, IGBPackage } from 'botlib'; import { AzureSearch } from 'pragmatismo-io-framework'; +import { CollectionUtil } from 'pragmatismo-io-framework'; import { GBServer } from '../../../src/app'; +import { GBVMService } from '../../basic.gblib/services/GBVMService'; import { GuaribasPackage } from '../models/GBModel'; import { GBAdminService } from './../../admin.gbapp/services/GBAdminService'; import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService'; import { KBService } from './../../kb.gbapp/services/KBService'; import { GBConfigService } from './GBConfigService'; import { GBImporter } from './GBImporterService'; -import { GBVMService } from '../../basic.gblib/services/GBVMService'; -import { CollectionUtil } from 'pragmatismo-io-framework'; const MicrosoftGraph = require('@microsoft/microsoft-graph-client'); /** - * Deployer service for bots, themes, ai and more. + * Deployer service for bots, themes, ai and more. */ export class GBDeployer implements IGBDeployer { @@ -77,7 +77,7 @@ export class GBDeployer implements IGBDeployer { */ public core: IGBCoreService; - /** + /** * Reference to the importer service. */ public importer: GBImporter; @@ -112,7 +112,7 @@ export class GBDeployer implements IGBDeployer { } const botPackages: string[] = []; const gbappPackages: string[] = []; - let generalPackages: string[] = []; + const generalPackages: string[] = []; async function scanPackageDirectory(path) { @@ -126,7 +126,6 @@ export class GBDeployer implements IGBDeployer { const dirs = getDirectories(path); await CollectionUtil.asyncForEach(dirs, async element => { - // For each folder, checks its extensions looking for valid packages. element = element.toLowerCase(); @@ -166,7 +165,7 @@ export class GBDeployer implements IGBDeployer { // Deploys all .gblib files first. - let list = []; + const list = []; for (let index = 0; index < gbappPackages.length; index++) { const element = gbappPackages[index]; if (element.endsWith('.gblib')) { @@ -187,7 +186,7 @@ export class GBDeployer implements IGBDeployer { const instances = await core.loadInstances(); await CollectionUtil.asyncForEach(instances, async instance => { this.mountGBKBAssets(`${instance.botId}.gbkb`, - instance.botId, `${instance.botId}.gbkb`); + instance.botId, `${instance.botId}.gbkb`); }); GBLog.info(`Package deployment done.`); @@ -200,7 +199,7 @@ export class GBDeployer implements IGBDeployer { // Creates a new row on the GuaribasInstance table. - let instance = await this.importer.createBotInstance(botId); + const instance = await this.importer.createBotInstance(botId); const bootInstance = GBServer.globals.bootInstance; // Gets the access token to perform service operations. @@ -210,7 +209,7 @@ export class GBDeployer implements IGBDeployer { // Creates the MSFT application that will be associated to the bot. const service = new AzureDeployerService(this); - let application = await service.createApplication(accessToken, botId); + const application = await service.createApplication(accessToken, botId); // Fills new instance base information and get App secret. @@ -240,6 +239,7 @@ export class GBDeployer implements IGBDeployer { */ public async botExists(botId: string): Promise { const service = new AzureDeployerService(this); + return await service.botExists(botId); } @@ -267,13 +267,9 @@ export class GBDeployer implements IGBDeployer { instance.description, `${publicAddress}/api/messages/${instance.botId}` ); - } - - // Otherwise, perform the bot creation. - - else { - let botId = GBConfigService.get('BOT_ID'); - let bootInstance = await this.core.loadInstanceByBotId(botId); + } else { + const botId = GBConfigService.get('BOT_ID'); + const bootInstance = await this.core.loadInstanceByBotId(botId); instance.searchHost = bootInstance.searchHost; instance.searchIndex = bootInstance.searchIndex; @@ -324,8 +320,8 @@ export class GBDeployer implements IGBDeployer { public async publishNLP(instance: IGBInstance): Promise { const service = new AzureDeployerService(this); const res = await service.publishNLP(instance.cloudLocation, instance.nlpAppId, - instance.nlpAuthoringKey); - if (res.status !== 200 && res.status !== 201) throw res.bodyAsText; + instance.nlpAuthoringKey); + if (res.status !== 200 && res.status !== 201) { throw res.bodyAsText; } } /** @@ -334,8 +330,8 @@ export class GBDeployer implements IGBDeployer { public async trainNLP(instance: IGBInstance): Promise { const service = new AzureDeployerService(this); const res = await service.trainNLP(instance.cloudLocation, instance.nlpAppId, instance.nlpAuthoringKey); - if (res.status !== 200 && res.status !== 202) throw res.bodyAsText; - let sleep = ms => { + if (res.status !== 200 && res.status !== 202) { throw res.bodyAsText; } + const sleep = ms => { return new Promise(resolve => { setTimeout(resolve, ms); }); @@ -355,7 +351,7 @@ export class GBDeployer implements IGBDeployer { instance.nlpAuthoringKey, listData ); - if (res.status !== 200) throw res.bodyAsText; + if (res.status !== 200) { throw res.bodyAsText; } } /** @@ -363,7 +359,7 @@ export class GBDeployer implements IGBDeployer { */ public async deployBotFromLocalPath(localPath: string, publicAddress: string): Promise { const packageName = Path.basename(localPath); - let instance = await this.importer.importIfNotExistsBotPackage(undefined, packageName, localPath); + const instance = await this.importer.importIfNotExistsBotPackage(undefined, packageName, localPath); await this.deployBotFull(instance, publicAddress); } @@ -374,10 +370,10 @@ export class GBDeployer implements IGBDeployer { // Connects to MSFT storage. - let token = await min.adminService.acquireElevatedToken(min.instance.instanceId); - let siteId = process.env.STORAGE_SITE_ID; - let libraryId = process.env.STORAGE_LIBRARY; - let client = MicrosoftGraph.Client.init({ + const token = await min.adminService.acquireElevatedToken(min.instance.instanceId); + const siteId = process.env.STORAGE_SITE_ID; + const libraryId = process.env.STORAGE_LIBRARY; + const client = MicrosoftGraph.Client.init({ authProvider: done => { done(null, token); } @@ -387,24 +383,25 @@ export class GBDeployer implements IGBDeployer { const botId = min.instance.botId; const path = `/${botId}.gbai/${botId}.gbot`; - let res = await client + const res = await client .api(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:${path}:/children`) .get(); // Finds Config.xlsx. - let document = res.value.filter(m => { + const document = res.value.filter(m => { return m.name === 'Config.xlsx'; }); if (document === undefined || document.length === 0) { GBLog.info(`Config.xlsx not found on .bot folder, check the package.`); + return null; } // Reads all rows in Config.xlsx that contains a pair of name/value // and fills an object that is returned to be saved in params instance field. - let results = await client + const results = await client .api( `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/items/${document[0].id}/workbook/worksheets('General')/range(address='A7:B100')` ) @@ -416,6 +413,7 @@ export class GBDeployer implements IGBDeployer { } obj[results.text[index][0]] = results.text[index][1]; } + return obj; } @@ -494,7 +492,7 @@ export class GBDeployer implements IGBDeployer { return pck; } - // Deploy platform packages here accordingly to their extension. + // Deploy platform packages here accordingly to their extension. switch (packageType) { case '.gbot': @@ -585,6 +583,7 @@ export class GBDeployer implements IGBDeployer { case '.gbkb': const service = new KBService(this.core.sequelize); rimraf.sync(localPath); + return await service.undeployKbFromStorage(instance, this, p.packageId); case '.gbui': @@ -712,7 +711,7 @@ export class GBDeployer implements IGBDeployer { * Servers bot storage assets to be used by web, WhatsApp and other channels. */ public mountGBKBAssets(packageName: any, botId: string, filename: string) { - + // Servers menu assets. GBServer.globals.server.use( @@ -723,55 +722,18 @@ export class GBDeployer implements IGBDeployer { // Servers all other assets in .gbkb folders. const gbaiName = `${botId}.gbai`; - GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/assets`, express.static(urlJoin('work', gbaiName, filename, 'assets'))); - GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/images`, express.static(urlJoin('work', gbaiName, filename, 'images'))); - GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/audios`, express.static(urlJoin('work', gbaiName, filename, 'audios'))); - GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/videos`, express.static(urlJoin('work', gbaiName, filename, 'videos'))); + GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/assets`, + express.static(urlJoin('work', gbaiName, filename, 'assets'))); + GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/images`, + express.static(urlJoin('work', gbaiName, filename, 'images'))); + GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/audios`, + express.static(urlJoin('work', gbaiName, filename, 'audios'))); + GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/videos`, + express.static(urlJoin('work', gbaiName, filename, 'videos'))); GBLog.info(`KB (.gbkb) assets accessible at: /kb/${botId}.gbai/${packageName}.`); } - /** - * Determines if a given package is of system kind. - */ - private isSystemPackage(name: string): Boolean { - const names = [ - 'analytics.gblib', - 'console.gblib', - 'security.gbapp', - 'whatsapp.gblib', - 'sharepoint.gblib', - 'core.gbapp', - 'admin.gbapp', - 'azuredeployer.gbapp', - 'customer-satisfaction.gbapp', - 'kb.gbapp' - ]; - - return names.indexOf(name) > -1; - } - - /** - * Performs the process of compiling all .gbapp folders. - */ - private async deployAppPackages(gbappPackages: string[], core: any, appPackages: any[]) { - - // Loops through all ready to load .gbapp packages. - - let appPackagesProcessed = 0; - await CollectionUtil.asyncForEach(gbappPackages, async e => { - const filenameOnly = Path.basename(e); - - // Skips .gbapp inside deploy folder. - - if (this.isSystemPackage(filenameOnly) === false) { - appPackagesProcessed = await this.callGBAppCompiler(e, core, appPackages, appPackagesProcessed); - } - }); - - return appPackagesProcessed; - } - /** * Invokes Type Script compiler for a given .gbapp package (Node.js based). */ @@ -792,7 +754,7 @@ export class GBDeployer implements IGBDeployer { child_process.execSync('npm install', { cwd: gbappPath }); } } - + folder = Path.join(gbappPath, 'dist'); try { @@ -823,6 +785,48 @@ export class GBDeployer implements IGBDeployer { } appPackagesProcessed++; } + + return appPackagesProcessed; + } + + /** + * Determines if a given package is of system kind. + */ + private isSystemPackage(name: string): Boolean { + const names = [ + 'analytics.gblib', + 'console.gblib', + 'security.gbapp', + 'whatsapp.gblib', + 'sharepoint.gblib', + 'core.gbapp', + 'admin.gbapp', + 'azuredeployer.gbapp', + 'customer-satisfaction.gbapp', + 'kb.gbapp' + ]; + + return names.indexOf(name) > -1; + } + + /** + * Performs the process of compiling all .gbapp folders. + */ + private async deployAppPackages(gbappPackages: string[], core: any, appPackages: any[]) { + + // Loops through all ready to load .gbapp packages. + + let appPackagesProcessed = 0; + await CollectionUtil.asyncForEach(gbappPackages, async e => { + const filenameOnly = Path.basename(e); + + // Skips .gbapp inside deploy folder. + + if (this.isSystemPackage(filenameOnly) === false) { + appPackagesProcessed = await this.callGBAppCompiler(e, core, appPackages, appPackagesProcessed); + } + }); + return appPackagesProcessed; } } diff --git a/packages/core.gbapp/services/GBImporterService.ts b/packages/core.gbapp/services/GBImporterService.ts index 0dfba07d..1537e643 100644 --- a/packages/core.gbapp/services/GBImporterService.ts +++ b/packages/core.gbapp/services/GBImporterService.ts @@ -36,12 +36,12 @@ 'use strict'; -import { IGBCoreService, IGBInstance, GBMinInstance } from 'botlib'; +import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib'; import fs = require('fs'); import urlJoin = require('url-join'); +import { GBServer } from '../../../src/app'; import { GuaribasInstance } from '../models/GBModel'; import { GBConfigService } from './GBConfigService'; -import { GBServer } from '../../../src/app'; /** * Handles the importing of packages. @@ -53,8 +53,8 @@ export class GBImporter { this.core = core; } - public async importIfNotExistsBotPackage(botId: string, - packageName: string, localPath: string, additionalInstance: IGBInstance = null) { + public async importIfNotExistsBotPackage(botId: string, + packageName: string, localPath: string, additionalInstance: IGBInstance = null) { const settingsJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8')); if (botId === undefined) { botId = settingsJson.botId; @@ -84,10 +84,9 @@ export class GBImporter { instance = await this.core.loadInstanceByBotId(botId); } - if (instance != null && instance.botId === null) { + if (instance != undefined && instance.botId === null) { console.log(`Null BotId after load instance with botId: ${botId}.`); - } - else{ + } else { instance = additionalInstance; } @@ -95,8 +94,9 @@ export class GBImporter { } public async createBotInstance(botId: string) { - let fullSettingsJson = { ...GBServer.globals.bootInstance }; + const fullSettingsJson = { ...GBServer.globals.bootInstance }; fullSettingsJson.botId = botId; + return await GuaribasInstance.create(fullSettingsJson); } @@ -106,10 +106,10 @@ export class GBImporter { localPath: string, settingsJson: any ) { - let packageJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8')); + const packageJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8')); const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8')); - let fullSettingsJson = { ...GBServer.globals.bootInstance, ...packageJson, ...settingsJson, ...servicesJson }; + const fullSettingsJson = { ...GBServer.globals.bootInstance, ...packageJson, ...settingsJson, ...servicesJson }; if (botId !== undefined) { fullSettingsJson.botId = botId; diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 3efa3656..71c9bdb9 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -51,7 +51,6 @@ import { TurnContext, UserState } from 'botbuilder'; -import { CollectionUtil, AzureText } from 'pragmatismo-io-framework'; import { ConfirmPrompt, OAuthPrompt, WaterfallDialog } from 'botbuilder-dialogs'; import { GBDialogStep, @@ -63,28 +62,34 @@ import { IGBInstance, IGBPackage } from 'botlib'; +import { AzureText, CollectionUtil } from 'pragmatismo-io-framework'; import { MicrosoftAppCredentials } from 'botframework-connector'; +import fs = require('fs'); import { GBServer } from '../../../src/app'; +import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; +import { GuaribasConversationMessage } from '../../analytics.gblib/models'; +import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; +import { GBVMService } from '../../basic.gblib/services/GBVMService'; import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog'; import { KBService } from '../../kb.gbapp/services/KBService'; +import { SecService } from '../../security.gbapp/services/SecService'; +import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirectLine'; import { Messages } from '../strings'; import { GBConfigService } from './GBConfigService'; -import { GBDeployer } from './GBDeployer'; -import { SecService } from '../../security.gbapp/services/SecService'; -import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; -import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirectLine'; -import fs = require('fs'); -import { GuaribasConversationMessage } from '../../analytics.gblib/models'; -import { GBVMService } from '../../basic.gblib/services/GBVMService'; -import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBConversationalService } from './GBConversationalService'; +import { GBDeployer } from './GBDeployer'; /** * Minimal service layer for a bot and encapsulation of BOT Framework calls. */ export class GBMinService { + /** + * Default General Bots User Interface package. + */ + private static uiPackage = 'default.gbui'; + /** * Main core service attached to this bot service. */ @@ -105,11 +110,6 @@ export class GBMinService { */ public deployer: GBDeployer; - /** - * Default General Bots User Interface package. - */ - private static uiPackage = 'default.gbui'; - /** * Static initialization of minimal instance. * @@ -127,118 +127,6 @@ export class GBMinService { this.deployer = deployer; } - - private async WhatsAppCallback(req, res) { - try { - - // Detects if the message is echo fro itself. - - const id = req.body.messages[0].chatId.split('@')[0]; - const senderName = req.body.messages[0].senderName; - const text = req.body.messages[0].body; - if (req.body.messages[0].fromMe) { - res.end(); - return; // Exit here. - } - - // Detects if the welcome message is enabled. - - let activeMin; - if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') { - - // Tries to find if user wants to switch bots. - - let toSwitchMin = GBServer.globals.minInstances.filter( - p => p.instance.botId.toLowerCase() === text.toLowerCase() - )[0]; - if (!toSwitchMin) { - toSwitchMin = GBServer.globals.minInstances.filter(p => - p.instance.activationCode ? p.instance.activationCode.toLowerCase() === text.toLowerCase() : false - )[0]; - } - - // Find active bot instance. - - activeMin = toSwitchMin ? toSwitchMin : GBServer.globals.minBoot; - - // If it is the first time for the user, tries to auto-execute - // start dialog if any is specified in Config.xlsx. - - let sec = new SecService(); - let user = await sec.getUserFromSystemId(id); - if (user === null || user.hearOnDialog) { - user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName); - - let startDialog = user.hearOnDialog ? - user.hearOnDialog : - activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); - - GBLog.info(`Auto start dialog is now being called: ${startDialog}...`); - if (startDialog) { - req.body.messages[0].body = `${startDialog}`; - - // Resets HEAR ON DIALOG value to none and passes - // current dialog to the direct line. - - await sec.updateUserHearOnDialog(user.userId, null); - await (activeMin as any).whatsAppDirectLine.received(req, res); - } - else { - await (activeMin as any).whatsAppDirectLine.sendToDevice( - id, - `Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.` - ); - res.end(); - } - } else { - - // User wants to switch bots. - - if (toSwitchMin !== undefined) { - - // So gets the new bot instance information and prepares to - // auto start dialog if any is specified. - - const instance = await this.core.loadInstanceByBotId(activeMin.botId); - await sec.updateUserInstance(id, instance.instanceId); - await (activeMin as any).whatsAppDirectLine.resetConversationId(id); - let startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); - GBLog.info(`Auto start dialog is now being called: ${startDialog}...`); - - if (startDialog) { - req.body.messages[0].body = `${startDialog}`; - await (activeMin as any).whatsAppDirectLine.received(req, res); - } - else { - await (activeMin as any).whatsAppDirectLine.sendToDevice( - id, - `Agora falando com ${activeMin.instance.title}...` - ); - res.end(); - } - } else { - activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0]; - if (activeMin === undefined) { - activeMin = GBServer.globals.minBoot; - await (activeMin as any).whatsAppDirectLine.sendToDevice( - id, - `O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...` - ); - } - await (activeMin as any).whatsAppDirectLine.received(req, res); - } - } - } else { - - // Just pass the message to the receiver. - - await (GBServer.globals.minBoot as any).whatsAppDirectLine.received(req, res); - } - } catch (error) { - GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error}`); - } - } - /** * Constructs a new minimal instance for each bot. */ @@ -246,7 +134,7 @@ export class GBMinService { // Servers default UI on root address '/' if web enabled. if (process.env.DISABLE_WEB !== 'true') { - let url = GBServer.globals.wwwroot + const url = GBServer.globals.wwwroot ? GBServer.globals.wwwroot : urlJoin(GBDeployer.deployFolder, GBMinService.uiPackage, 'build'); @@ -257,12 +145,12 @@ export class GBMinService { // instance information stored on server. if (process.env.DISABLE_WEB !== 'true') { - GBServer.globals.server.get('/instances/:botId', this.handleGetInstanceForClient); + GBServer.globals.server.get('/instances/:botId', this.handleGetInstanceForClient.bind(this)); } // Servers the WhatsApp callback. - GBServer.globals.server.post('/webhooks/whatsapp', this.WhatsAppCallback); + GBServer.globals.server.post('/webhooks/whatsapp', this.WhatsAppCallback.bind(this)); // Call mountBot event to all bots. @@ -372,6 +260,116 @@ export class GBMinService { this.createCheckHealthAddress(GBServer.globals.server, min, min.instance); } + private async WhatsAppCallback(req, res) { + try { + + // Detects if the message is echo fro itself. + + const id = req.body.messages[0].chatId.split('@')[0]; + const senderName = req.body.messages[0].senderName; + const text = req.body.messages[0].body; + if (req.body.messages[0].fromMe) { + res.end(); + + return; // Exit here. + } + + // Detects if the welcome message is enabled. + + let activeMin; + if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') { + + // Tries to find if user wants to switch bots. + + let toSwitchMin = GBServer.globals.minInstances.filter( + p => p.instance.botId.toLowerCase() === text.toLowerCase() + )[0]; + if (!toSwitchMin) { + toSwitchMin = GBServer.globals.minInstances.filter(p => + p.instance.activationCode ? p.instance.activationCode.toLowerCase() === text.toLowerCase() : false + )[0]; + } + + // Find active bot instance. + + activeMin = toSwitchMin ? toSwitchMin : GBServer.globals.minBoot; + + // If it is the first time for the user, tries to auto-execute + // start dialog if any is specified in Config.xlsx. + + const sec = new SecService(); + let user = await sec.getUserFromSystemId(id); + if (user === null || user.hearOnDialog) { + user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName); + + const startDialog = user.hearOnDialog ? + user.hearOnDialog : + activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); + + GBLog.info(`Auto start dialog is now being called: ${startDialog}...`); + if (startDialog) { + req.body.messages[0].body = `${startDialog}`; + + // Resets HEAR ON DIALOG value to none and passes + // current dialog to the direct line. + + await sec.updateUserHearOnDialog(user.userId, null); + await (activeMin as any).whatsAppDirectLine.received(req, res); + } else { + await (activeMin as any).whatsAppDirectLine.sendToDevice( + id, + `Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.` + ); + res.end(); + } + } else { + + // User wants to switch bots. + + if (toSwitchMin !== undefined) { + + // So gets the new bot instance information and prepares to + // auto start dialog if any is specified. + + const instance = await this.core.loadInstanceByBotId(activeMin.botId); + await sec.updateUserInstance(id, instance.instanceId); + await (activeMin as any).whatsAppDirectLine.resetConversationId(id); + const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); + GBLog.info(`Auto start dialog is now being called: ${startDialog}...`); + + if (startDialog) { + req.body.messages[0].body = `${startDialog}`; + await (activeMin as any).whatsAppDirectLine.received(req, res); + } else { + await (activeMin as any).whatsAppDirectLine.sendToDevice( + id, + `Agora falando com ${activeMin.instance.title}...` + ); + res.end(); + } + } else { + activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0]; + if (activeMin === undefined) { + activeMin = GBServer.globals.minBoot; + await (activeMin as any).whatsAppDirectLine.sendToDevice( + id, + `O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...` + ); + } + await (activeMin as any).whatsAppDirectLine.received(req, res); + } + } + } else { + + // Just pass the message to the receiver. + + await (GBServer.globals.minBoot as any).whatsAppDirectLine.received(req, res); + } + } catch (error) { + GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error}`); + } + } + /** * Creates a listener that can be used by external monitors to check * bot instance health. @@ -499,7 +497,7 @@ export class GBMinService { // Gets the webchat token, speech token and theme. const webchatTokenContainer = await this.getWebchatToken(instance); - const speechToken = instance.speechKey != null ? await this.getSTSToken(instance) : null; + const speechToken = instance.speechKey != undefined ? await this.getSTSToken(instance) : null; let theme = instance.theme; // Sends all information to the .gbui web client. @@ -540,9 +538,11 @@ export class GBMinService { try { const json = await request(options); + return JSON.parse(json); } catch (error) { const msg = `[botId:${instance.botId}] Error calling Direct Line to generate a token for Web control: ${error}.`; + return Promise.reject(new Error(msg)); } } @@ -575,13 +575,14 @@ export class GBMinService { // MSFT stuff. - const adapter = new BotFrameworkAdapter({ appId: instance.marketplaceId, appPassword: instance.marketplacePassword }); + const adapter = new BotFrameworkAdapter( + { appId: instance.marketplaceId, appPassword: instance.marketplacePassword }); const storage = new MemoryStorage(); const conversationState = new ConversationState(storage); const userState = new UserState(storage); adapter.use(new AutoSaveStateMiddleware(conversationState, userState)); MicrosoftAppCredentials.trustServiceUrl('https://directline.botframework.com', - new Date(new Date().setFullYear(new Date().getFullYear() + 10)) + new Date(new Date().setFullYear(new Date().getFullYear() + 10)) ); // The minimal bot is built here. @@ -611,7 +612,7 @@ export class GBMinService { await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => { let services: ConcatArray; if ((services = await e.onExchangeData(min, 'getServices', null))) { - min.gbappServices = Object.assign(min.gbappServices, services); + min.gbappServices = {...min.gbappServices, ...services}; } }); @@ -736,7 +737,7 @@ export class GBMinService { user.welcomed = false; firstTime = true; - // Sends loadInstance event to .gbui clients. + // Sends loadInstance event to .gbui clients. await min.conversationalService.sendEvent(min, step, 'loadInstance', { instanceId: instance.instanceId, @@ -746,7 +747,7 @@ export class GBMinService { }); // This same event is dispatched either to all participants - // including the bot, that is filtered bellow. + // including the bot, that is filtered bellow. if (context.activity.from.id !== min.botId) { @@ -762,7 +763,7 @@ export class GBMinService { member.name ); - // Required for MSTEAMS handling of persisted conversations. + // Required for MSTEAMS handling of persisted conversations. if (step.context.activity.channelId === 'msteams') { persistedUser.conversationReference = JSON.stringify( @@ -809,8 +810,7 @@ export class GBMinService { user.welcomed = true; GBLog.info(`Auto start dialog is now being called: ${startDialog}...`); await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer); - } - else { + } else { // Otherwise, calls / (root) to default welcome users. @@ -822,7 +822,6 @@ export class GBMinService { GBLog.info(`Member added to conversation: ${member.name}`); } - } else if (context.activity.type === 'message') { // Process messages activities. @@ -896,7 +895,7 @@ export class GBMinService { // Removes Bot Id from MS Teams. context.activity.text = context.activity.text.trim(); - context.activity.text = context.activity.text.replace(/\.*\<\/at\>\s/gi, '') + context.activity.text = context.activity.text.replace(/\.*\<\/at\>\s/gi, ''); const user = await min.userProfile.get(context, {}); let message: GuaribasConversationMessage; @@ -931,64 +930,38 @@ export class GBMinService { const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined; if (isVMCall) { await GBVMService.callVM(context.activity.text, min, step, this.deployer); - } + } else if (context.activity.text.charAt(0) === '/') { - // When user starts text by typing a slash, it can call either a dialog directly - // or /call