fix(general): tslint being applied in all sources. #90
70 changed files with 2046 additions and 2227 deletions
|
@ -5,4 +5,5 @@
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid",
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true
|
"singleQuote": true
|
||||||
|
|
||||||
}
|
}
|
8
FEATURES.md
Normal file
8
FEATURES.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# General Bots Features
|
||||||
|
|
||||||
|
| Feature | BF | GB |
|
||||||
|
|----------------------------------------------------------------------------|----|----|
|
||||||
|
| Use of conversational administration to manage bot packages (Talk to admin | - | X |
|
||||||
|
| F5 to run on VSCode | - | X |
|
||||||
|
| Isolated code on packages | - | X |
|
||||||
|
| Breaking changes protected | - | X |
|
6
ROADMAP.md
Normal file
6
ROADMAP.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
| Title | Priority | Release | Status |
|
||||||
|
|-------------------------------|------------------------------------------------------------------------------------------------------------|---------|--------|
|
||||||
|
| Isolation of .gbapp per .gbot | Today .gbapp loaded is shared across all bot instances and must be associated to one or more individually. | Medium | 2019Q4 |
|
||||||
|
| Python based .gbapps | Write conversational login in Python | Low | - |
|
800
package-lock.json
generated
800
package-lock.json
generated
File diff suppressed because it is too large
Load diff
50
package.json
50
package.json
|
@ -23,7 +23,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "shx rm -rf node_modules/ dist/ docs/reference",
|
"clean": "shx rm -rf node_modules/ dist/ docs/reference",
|
||||||
"tslint": "tslint --fix ./src/*.ts ./packages/**/*.ts -t verbose",
|
"tslint": "tslint --fix ./src/*.ts ./packages/**/*.ts -t verbose -e ./packages/default.gbui/**/* -e ./packages/**/*.gbdialog/**/*",
|
||||||
"build": "npm install && npm run build-server && npm run build-gbui && npm run build-docs",
|
"build": "npm install && npm run build-server && npm run build-gbui && npm run build-docs",
|
||||||
"build-server": "tsc",
|
"build-server": "tsc",
|
||||||
"build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build",
|
"build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build",
|
||||||
|
@ -38,7 +38,6 @@
|
||||||
"ban": "ban",
|
"ban": "ban",
|
||||||
"issues": "git-issues",
|
"issues": "git-issues",
|
||||||
"license": "license-checker --production --onlyunknown --csv",
|
"license": "license-checker --production --onlyunknown --csv",
|
||||||
"pretslint": "npm run pretty",
|
|
||||||
"pretty": "prettier-standard 'src/*.ts' 'packages/**/*.ts'",
|
"pretty": "prettier-standard 'src/*.ts' 'packages/**/*.ts'",
|
||||||
"secure": "nsp check",
|
"secure": "nsp check",
|
||||||
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
|
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
|
||||||
|
@ -48,7 +47,7 @@
|
||||||
"commit": "git-cz"
|
"commit": "git-cz"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/microsoft-graph-client": "1.4.0",
|
"@microsoft/microsoft-graph-client": "1.5.2",
|
||||||
"@semantic-release/exec": "^3.3.2",
|
"@semantic-release/exec": "^3.3.2",
|
||||||
"adal-node": "0.1.28",
|
"adal-node": "0.1.28",
|
||||||
"async": "2.6.2",
|
"async": "2.6.2",
|
||||||
|
@ -58,36 +57,37 @@
|
||||||
"azure-arm-search": "^1.3.0-preview",
|
"azure-arm-search": "^1.3.0-preview",
|
||||||
"azure-arm-sql": "5.6.0",
|
"azure-arm-sql": "5.6.0",
|
||||||
"azure-arm-website": "5.7.0",
|
"azure-arm-website": "5.7.0",
|
||||||
"bluebird": "^3.5.3",
|
"bluebird": "^3.5.4",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"botbuilder": "4.1.7",
|
"botbuilder": "4.3.4",
|
||||||
"botbuilder-ai": "4.2.0",
|
"botbuilder-ai": "4.3.4",
|
||||||
"botbuilder-azure": "4.2.0",
|
"botbuilder-azure": "4.3.4",
|
||||||
"botbuilder-choices": "4.0.0-preview1.2",
|
"botbuilder-choices": "4.0.0-preview1.2",
|
||||||
"botbuilder-dialogs": "4.2.0",
|
"botbuilder-dialogs": "4.3.4",
|
||||||
"botbuilder-prompts": "4.0.0-preview1.2",
|
"botbuilder-prompts": "4.0.0-preview1.2",
|
||||||
"botlib": "0.1.19",
|
"botlib": "0.1.24",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
"chokidar": "2.1.2",
|
"chokidar": "2.1.5",
|
||||||
"cli-spinner": "^0.2.8",
|
"cli-spinner": "^0.2.10",
|
||||||
"csv-parse": "4.3.3",
|
"csv-parse": "4.3.4",
|
||||||
"dotenv-extended": "2.3.0",
|
"dotenv-extended": "2.4.0",
|
||||||
"express": "4.16.4",
|
"express": "4.16.4",
|
||||||
"express-promise-router": "3.0.3",
|
"express-promise-router": "3.0.3",
|
||||||
"fs-extra": "7.0.1",
|
"fs-extra": "7.0.1",
|
||||||
"ip": "^1.1.5",
|
"ip": "^1.1.5",
|
||||||
"js-beautify": "^1.8.9",
|
"js-beautify": "^1.9.1",
|
||||||
"localize": "0.4.7",
|
"localize": "0.4.7",
|
||||||
"marked": "0.6.1",
|
"marked": "0.6.2",
|
||||||
"mocha": "6.0.2",
|
"mocha": "6.0.2",
|
||||||
"mocha-typescript": "1.1.17",
|
"mocha-typescript": "1.1.17",
|
||||||
"ms": "2.1.1",
|
"ms": "2.1.1",
|
||||||
"ms-rest-azure": "2.6.0",
|
"ms-rest-azure": "2.6.0",
|
||||||
|
"ms-rest-js": "^1.0.1",
|
||||||
"nexmo": "2.4.1",
|
"nexmo": "2.4.1",
|
||||||
"ngrok": "3.1.1",
|
"ngrok": "3.1.1",
|
||||||
"nyc": "13.3.0",
|
"nyc": "13.3.0",
|
||||||
"opn": "5.4.0",
|
"opn": "6.0.0",
|
||||||
"pragmatismo-io-framework": "1.0.19",
|
"pragmatismo-io-framework": "1.0.19",
|
||||||
"process-exists": "3.1.0",
|
"process-exists": "3.1.0",
|
||||||
"public-ip": "^3.0.0",
|
"public-ip": "^3.0.0",
|
||||||
|
@ -95,20 +95,20 @@
|
||||||
"request-promise": "4.2.4",
|
"request-promise": "4.2.4",
|
||||||
"request-promise-native": "1.0.7",
|
"request-promise-native": "1.0.7",
|
||||||
"scanf": "^1.0.2",
|
"scanf": "^1.0.2",
|
||||||
"sequelize": "4.42.1",
|
"sequelize": "^5.2.12",
|
||||||
"sequelize-typescript": "0.6.7",
|
"sequelize-typescript": "0.6.9",
|
||||||
"shx": "0.3.2",
|
"shx": "0.3.2",
|
||||||
"simple-git": "1.107.0",
|
"simple-git": "1.110.0",
|
||||||
"sqlite3": "4.0.6",
|
"sqlite3": "4.0.6",
|
||||||
"strict-password-generator": "^1.1.2",
|
"strict-password-generator": "^1.1.2",
|
||||||
"swagger-client": "3.8.25",
|
"swagger-client": "3.8.25",
|
||||||
"tedious": "5.0.3",
|
"tedious": "6.1.0",
|
||||||
"temperature-js": "^0.1.0",
|
"temperature-js": "^0.1.0",
|
||||||
"ts-node": "8.0.2",
|
"ts-node": "8.0.3",
|
||||||
"typedoc": "0.14.2",
|
"typedoc": "0.14.2",
|
||||||
"typedoc-plugin-external-module-name": "^2.0.0",
|
"typedoc-plugin-external-module-name": "^2.0.0",
|
||||||
"typedoc-plugin-markdown": "^1.1.27",
|
"typedoc-plugin-markdown": "^1.1.27",
|
||||||
"typescript": "3.3.3333",
|
"typescript": "3.4.1",
|
||||||
"url-join": "4.0.0",
|
"url-join": "4.0.0",
|
||||||
"vbscript-to-typescript": "^1.0.8",
|
"vbscript-to-typescript": "^1.0.8",
|
||||||
"wait-until": "0.0.2",
|
"wait-until": "0.0.2",
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
"@semantic-release/release-notes-generator": "^7.1.4",
|
"@semantic-release/release-notes-generator": "^7.1.4",
|
||||||
"@types/chai": "4.1.7",
|
"@types/chai": "4.1.7",
|
||||||
"@types/mocha": "5.2.6",
|
"@types/mocha": "5.2.6",
|
||||||
"@types/sequelize": "4.27.38",
|
"@types/sequelize": "4.27.46",
|
||||||
"@types/url-join": "4.0.0",
|
"@types/url-join": "4.0.0",
|
||||||
"@types/winston": "2.4.4",
|
"@types/winston": "2.4.4",
|
||||||
"ban-sensitive-files": "1.9.2",
|
"ban-sensitive-files": "1.9.2",
|
||||||
|
@ -142,8 +142,8 @@
|
||||||
"standard": "12.0.1",
|
"standard": "12.0.1",
|
||||||
"travis-deploy-once": "5.0.11",
|
"travis-deploy-once": "5.0.11",
|
||||||
"ts-loader": "^5.3.3",
|
"ts-loader": "^5.3.3",
|
||||||
"tslint": "^5.13.0",
|
"tslint": "^5.15.0",
|
||||||
"tslint-microsoft-contrib": "^6.0.0"
|
"tslint-microsoft-contrib": "^6.1.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"env": {
|
"env": {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,28 +36,24 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { WaterfallDialog, WaterfallStep, WaterfallStepContext } from 'botbuilder-dialogs';
|
import urlJoin = require('url-join');
|
||||||
import { GBMinInstance } from 'botlib';
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
import { IGBDialog } from 'botlib';
|
|
||||||
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
||||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||||
import { GBImporter } from '../../core.gbapp/services/GBImporterService';
|
import { GBImporter } from '../../core.gbapp/services/GBImporterService';
|
||||||
import { GBAdminService } from '../services/GBAdminService';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialogs for administration tasks.
|
* Dialogs for administration tasks.
|
||||||
*/
|
*/
|
||||||
export class AdminDialog extends IGBDialog {
|
export class AdminDialog extends IGBDialog {
|
||||||
public static async createFarmCommand(text: any, min: GBMinInstance) {}
|
public static async undeployPamand(text: any, min: GBMinInstance) {
|
||||||
|
|
||||||
public static async undeployPackageCommand(text: any, min: GBMinInstance) {
|
|
||||||
const packageName = text.split(' ')[1];
|
const packageName = text.split(' ')[1];
|
||||||
const importer = new GBImporter(min.core);
|
const importer = new GBImporter(min.core);
|
||||||
const deployer = new GBDeployer(min.core, importer);
|
const deployer = new GBDeployer(min.core, importer);
|
||||||
await deployer.undeployPackageFromLocalPath(min.instance, UrlJoin('packages', packageName));
|
await deployer.undeployPackageFromLocalPath(min.instance, urlJoin('packages', packageName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isSharePointPath(path: string) {
|
public static isSharePointPath(path: string) {
|
||||||
|
@ -67,26 +63,20 @@ export class AdminDialog extends IGBDialog {
|
||||||
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
||||||
const packageName = text.split(' ')[1];
|
const packageName = text.split(' ')[1];
|
||||||
|
|
||||||
if (AdminDialog.isSharePointPath(packageName)) {
|
if (!AdminDialog.isSharePointPath(packageName)) {
|
||||||
await deployer.deployFromSharePoint(min.instance.instanceId, packageName);
|
|
||||||
} else {
|
|
||||||
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
||||||
if (!additionalPath) {
|
if (additionalPath === undefined) {
|
||||||
throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
|
throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
|
||||||
}
|
}
|
||||||
await deployer.deployPackageFromLocalPath(min, UrlJoin(additionalPath, packageName));
|
await deployer.deployPackage(min, urlJoin(additionalPath, packageName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async rebuildIndexPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
await deployer.rebuildIndex(min.instance);
|
await deployer.rebuildIndex(
|
||||||
}
|
min.instance,
|
||||||
|
new AzureDeployerService(deployer).getKBSearchSchema(min.instance.searchIndex)
|
||||||
public static async addConnectionCommand(min: GBMinInstance, text: any) {
|
);
|
||||||
const packageName = text.split(' ')[1];
|
|
||||||
const importer = new GBImporter(min.core);
|
|
||||||
const admin = new GBAdminService(min.core);
|
|
||||||
// TODO: await admin.addConnection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,7 +85,7 @@ export class AdminDialog extends IGBDialog {
|
||||||
* @param bot The bot adapter.
|
* @param bot The bot adapter.
|
||||||
* @param min The minimal bot instance data.
|
* @param min The minimal bot instance data.
|
||||||
*/
|
*/
|
||||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
public static setup(min: GBMinInstance) {
|
||||||
// Setup services.
|
// Setup services.
|
||||||
|
|
||||||
const importer = new GBImporter(min.core);
|
const importer = new GBImporter(min.core);
|
||||||
|
@ -113,9 +103,9 @@ export class AdminDialog extends IGBDialog {
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const password = step.result;
|
const sensitive = step.result;
|
||||||
|
|
||||||
if (password === GBConfigService.get('ADMIN_PASS')) {
|
if (sensitive === GBConfigService.get('ADMIN_PASS')) {
|
||||||
await step.context.sendActivity(Messages[locale].welcome);
|
await step.context.sendActivity(Messages[locale].welcome);
|
||||||
|
|
||||||
return await step.prompt('textPrompt', Messages[locale].which_task);
|
return await step.prompt('textPrompt', Messages[locale].which_task);
|
||||||
|
@ -126,8 +116,9 @@ export class AdminDialog extends IGBDialog {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale: string = step.context.activity.locale;
|
||||||
const text = step.result;
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
const text: string = step.result;
|
||||||
const cmdName = text.split(' ')[0];
|
const cmdName = text.split(' ')[0];
|
||||||
|
|
||||||
step.context.sendActivity(Messages[locale].working(cmdName));
|
step.context.sendActivity(Messages[locale].working(cmdName));
|
||||||
|
@ -135,28 +126,16 @@ export class AdminDialog extends IGBDialog {
|
||||||
|
|
||||||
if (text === 'quit') {
|
if (text === 'quit') {
|
||||||
return await step.replaceDialog('/');
|
return await step.replaceDialog('/');
|
||||||
} else if (cmdName === 'createFarm') {
|
|
||||||
await AdminDialog.createFarmCommand(text, deployer);
|
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
|
||||||
} else if (cmdName === 'deployPackage') {
|
} else if (cmdName === 'deployPackage') {
|
||||||
await AdminDialog.deployPackageCommand(min, text, deployer);
|
await AdminDialog.deployPackageCommand(min, text, deployer);
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
return await step.replaceDialog('/admin', { firstRun: false });
|
||||||
} else if (cmdName === 'redeployPackage') {
|
} else if (cmdName === 'redeployPackage') {
|
||||||
await AdminDialog.undeployPackageCommand(text, min);
|
|
||||||
await AdminDialog.deployPackageCommand(min, text, deployer);
|
await AdminDialog.deployPackageCommand(min, text, deployer);
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
return await step.replaceDialog('/admin', { firstRun: false });
|
||||||
} else if (cmdName === 'rebuildIndex') {
|
} else if (cmdName === 'rebuildIndex') {
|
||||||
await AdminDialog.rebuildIndexPackageCommand(min, text, deployer);
|
await AdminDialog.rebuildIndexPackageCommand(min, deployer);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
return await step.replaceDialog('/admin', { firstRun: false });
|
||||||
} else if (cmdName === 'setupSecurity') {
|
} else if (cmdName === 'setupSecurity') {
|
||||||
|
@ -220,13 +199,13 @@ export class AdminDialog extends IGBDialog {
|
||||||
);
|
);
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const state = `${min.instance.instanceId}${Math.floor(Math.random() * 1000000000)}`;
|
const state = `${min.instance.instanceId}${crypto.getRandomValues(new Uint32Array(16))[0]}`;
|
||||||
|
|
||||||
await min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
|
min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
|
||||||
|
|
||||||
const url = `https://login.microsoftonline.com/${min.instance.authenticatorTenant}/oauth2/authorize?client_id=${
|
const url = `https://login.microsoftonline.com/${
|
||||||
min.instance.authenticatorClientId
|
min.instance.authenticatorTenant
|
||||||
}&response_type=code&redirect_uri=${UrlJoin(
|
}/oauth2/authorize?client_id=${min.instance.authenticatorClientId}&response_type=code&redirect_uri=${urlJoin(
|
||||||
min.instance.botEndpoint,
|
min.instance.botEndpoint,
|
||||||
min.instance.botId,
|
min.instance.botId,
|
||||||
'/token'
|
'/token'
|
||||||
|
@ -234,10 +213,9 @@ export class AdminDialog extends IGBDialog {
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].consent(url));
|
await step.context.sendActivity(Messages[locale].consent(url));
|
||||||
|
|
||||||
return await step.replaceDialog('/ask', {isReturning: true});
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,21 +36,35 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import urlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { AdminDialog } from './dialogs/AdminDialog';
|
import { AdminDialog } from './dialogs/AdminDialog';
|
||||||
import { GuaribasAdmin } from './models/AdminModel';
|
import { GuaribasAdmin } from './models/AdminModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The package for admin.gbapp.
|
||||||
|
*/
|
||||||
export class GBAdminPackage implements IGBPackage {
|
export class GBAdminPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
|
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
|
}
|
||||||
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
|
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
core.sequelize.addModels([GuaribasAdmin]);
|
core.sequelize.addModels([GuaribasAdmin]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
AdminDialog.setup(min.bot, min);
|
AdminDialog.setup(min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -39,12 +39,15 @@
|
||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
CreatedAt,
|
CreatedAt,
|
||||||
|
DataType,
|
||||||
Model,
|
Model,
|
||||||
Table,
|
Table,
|
||||||
UpdatedAt,
|
UpdatedAt
|
||||||
DataType
|
|
||||||
} from 'sequelize-typescript';
|
} from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General settings store.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasAdmin extends Model<GuaribasAdmin> {
|
export class GuaribasAdmin extends Model<GuaribasAdmin> {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,17 +37,17 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { AuthenticationContext, TokenResponse } from 'adal-node';
|
import { AuthenticationContext, TokenResponse } from 'adal-node';
|
||||||
import { IGBCoreService } from 'botlib';
|
import { IGBAdminService, IGBCoreService, IGBInstance } from 'botlib';
|
||||||
|
import urlJoin = require('url-join');
|
||||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||||
import { GuaribasAdmin } from '../models/AdminModel';
|
import { GuaribasAdmin } from '../models/AdminModel';
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const msRestAzure = require('ms-rest-azure');
|
const msRestAzure = require('ms-rest-azure');
|
||||||
const PasswordGenerator = require('strict-password-generator').default;
|
const PasswordGenerator = require('strict-password-generator').default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services for server administration.
|
* Services for server administration.
|
||||||
*/
|
*/
|
||||||
export class GBAdminService {
|
export class GBAdminService implements IGBAdminService {
|
||||||
public static GB_PROMPT: string = 'GeneralBots: ';
|
public static GB_PROMPT: string = 'GeneralBots: ';
|
||||||
public static masterBotInstanceId = 0;
|
public static masterBotInstanceId = 0;
|
||||||
|
|
||||||
|
@ -65,18 +65,15 @@ export class GBAdminService {
|
||||||
|
|
||||||
public static async getADALTokenFromUsername(username: string, password: string) {
|
public static async getADALTokenFromUsername(username: string, password: string) {
|
||||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
const accessToken = credentials.tokenCache._entries[0].accessToken;
|
|
||||||
|
|
||||||
return accessToken;
|
return credentials.tokenCache._entries[0].accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getADALCredentialsFromUsername(username: string, password: string) {
|
public static async getADALCredentialsFromUsername(username: string, password: string) {
|
||||||
const credentials = await msRestAzure.loginWithUsernamePassword(username, password);
|
return await msRestAzure.loginWithUsernamePassword(username, password);
|
||||||
|
|
||||||
return credentials;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getRndPassword() {
|
public static getRndPassword(): string {
|
||||||
const passwordGenerator = new PasswordGenerator();
|
const passwordGenerator = new PasswordGenerator();
|
||||||
const options = {
|
const options = {
|
||||||
upperCaseAlpha: true,
|
upperCaseAlpha: true,
|
||||||
|
@ -88,6 +85,7 @@ export class GBAdminService {
|
||||||
};
|
};
|
||||||
let password = passwordGenerator.generatePassword(options);
|
let password = passwordGenerator.generatePassword(options);
|
||||||
password = password.replace(/[\@\[\=\:\;\?]/g, '#');
|
password = password.replace(/[\@\[\=\:\;\?]/g, '#');
|
||||||
|
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,11 +99,11 @@ export class GBAdminService {
|
||||||
minimumLength: 12,
|
minimumLength: 12,
|
||||||
maximumLength: 14
|
maximumLength: 14
|
||||||
};
|
};
|
||||||
const name = passwordGenerator.generatePassword(options);
|
|
||||||
return name;
|
return passwordGenerator.generatePassword(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setValue(instanceId: number, key: string, value: string): Promise<GuaribasAdmin> {
|
public async setValue(instanceId: number, key: string, value: string) {
|
||||||
const options = { where: {} };
|
const options = { where: {} };
|
||||||
options.where = { key: key };
|
options.where = { key: key };
|
||||||
let admin = await GuaribasAdmin.findOne(options);
|
let admin = await GuaribasAdmin.findOne(options);
|
||||||
|
@ -115,8 +113,7 @@ export class GBAdminService {
|
||||||
}
|
}
|
||||||
admin.value = value;
|
admin.value = value;
|
||||||
admin.instanceId = instanceId;
|
admin.instanceId = instanceId;
|
||||||
|
await admin.save();
|
||||||
return admin.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateSecurityInfo(
|
public async updateSecurityInfo(
|
||||||
|
@ -125,7 +122,7 @@ export class GBAdminService {
|
||||||
authenticatorAuthorityHostUrl: string,
|
authenticatorAuthorityHostUrl: string,
|
||||||
authenticatorClientId: string,
|
authenticatorClientId: string,
|
||||||
authenticatorClientSecret: string
|
authenticatorClientSecret: string
|
||||||
): Promise<GuaribasInstance> {
|
): Promise<IGBInstance> {
|
||||||
const options = { where: {} };
|
const options = { where: {} };
|
||||||
options.where = { instanceId: instanceId };
|
options.where = { instanceId: instanceId };
|
||||||
const item = await GuaribasInstance.findOne(options);
|
const item = await GuaribasInstance.findOne(options);
|
||||||
|
@ -137,7 +134,7 @@ export class GBAdminService {
|
||||||
return item.save();
|
return item.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getValue(instanceId: number, key: string) {
|
public async getValue(instanceId: number, key: string): Promise<string> {
|
||||||
const options = { where: {} };
|
const options = { where: {} };
|
||||||
options.where = { key: key, instanceId: instanceId };
|
options.where = { key: key, instanceId: instanceId };
|
||||||
const obj = await GuaribasAdmin.findOne(options);
|
const obj = await GuaribasAdmin.findOne(options);
|
||||||
|
@ -154,7 +151,7 @@ export class GBAdminService {
|
||||||
const accessToken = await this.getValue(instanceId, 'accessToken');
|
const accessToken = await this.getValue(instanceId, 'accessToken');
|
||||||
resolve(accessToken);
|
resolve(accessToken);
|
||||||
} else {
|
} else {
|
||||||
const authorizationUrl = UrlJoin(
|
const authorizationUrl = urlJoin(
|
||||||
instance.authenticatorAuthorityHostUrl,
|
instance.authenticatorAuthorityHostUrl,
|
||||||
instance.authenticatorTenant,
|
instance.authenticatorTenant,
|
||||||
'/oauth2/authorize'
|
'/oauth2/authorize'
|
||||||
|
@ -169,7 +166,7 @@ export class GBAdminService {
|
||||||
instance.authenticatorClientSecret,
|
instance.authenticatorClientSecret,
|
||||||
resource,
|
resource,
|
||||||
async (err, res) => {
|
async (err, res) => {
|
||||||
if (err) {
|
if (err !== undefined) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
const token = res as TokenResponse;
|
const token = res as TokenResponse;
|
||||||
|
|
|
@ -16,7 +16,8 @@ export const Messages = {
|
||||||
wrong_password: 'Sorry, wrong password. Please, try again.',
|
wrong_password: 'Sorry, wrong password. Please, try again.',
|
||||||
enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
|
enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
|
||||||
enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',
|
enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',
|
||||||
enter_authenticator_client_id: 'Enter the Client Id [Application Id](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview) GUID:',
|
enter_authenticator_client_id: `Enter the Client Id GUID: Get from
|
||||||
|
[this url](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview)`,
|
||||||
enter_authenticator_client_secret: 'Enter the Client Secret:'
|
enter_authenticator_client_secret: 'Enter the Client Secret:'
|
||||||
},
|
},
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,18 +36,30 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .gblib Package handler.
|
||||||
|
*/
|
||||||
export class GBAnalyticsPackage implements IGBPackage {
|
export class GBAnalyticsPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
|
GBLog.verbose(`getDialogs called.`);
|
||||||
public unloadPackage(core: IGBCoreService): void {}
|
}
|
||||||
public loadBot(min: GBMinInstance): void {}
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
public unloadBot(min: GBMinInstance): void {}
|
GBLog.verbose(`loadPackage called.`);
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {}
|
}
|
||||||
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`loadBot called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,12 +36,6 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {
|
|
||||||
DataTypeDate,
|
|
||||||
DataTypeDecimal,
|
|
||||||
DataTypes,
|
|
||||||
DataTypeUUIDv4
|
|
||||||
} from 'sequelize';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
|
@ -65,6 +59,56 @@ import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBMod
|
||||||
import { GuaribasSubject } from '../../kb.gbapp/models';
|
import { GuaribasSubject } from '../../kb.gbapp/models';
|
||||||
import { GuaribasUser } from '../../security.gblib/models';
|
import { GuaribasUser } from '../../security.gblib/models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single message in a conversation.
|
||||||
|
*/
|
||||||
|
@Table
|
||||||
|
export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> {
|
||||||
|
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
public conversationMessageId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => GuaribasSubject)
|
||||||
|
@Column
|
||||||
|
public subjectId: number;
|
||||||
|
|
||||||
|
@Column(DataType.TEXT)
|
||||||
|
public content: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@CreatedAt
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@UpdatedAt
|
||||||
|
public updatedAt: Date;
|
||||||
|
|
||||||
|
//tslint:disable-next-line:no-use-before-declare
|
||||||
|
@ForeignKey(() => GuaribasConversation)
|
||||||
|
@Column
|
||||||
|
public conversationId: number;
|
||||||
|
|
||||||
|
//tslint:disable-next-line:no-use-before-declare
|
||||||
|
@BelongsTo(() => GuaribasConversation)
|
||||||
|
public conversation: GuaribasConversation;
|
||||||
|
|
||||||
|
@ForeignKey(() => GuaribasInstance)
|
||||||
|
@Column
|
||||||
|
public instanceId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => GuaribasUser)
|
||||||
|
@Column
|
||||||
|
public userId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => GuaribasUser)
|
||||||
|
public user: GuaribasUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A conversation that groups many messages.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasConversation extends Model<GuaribasConversation> {
|
export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||||
|
|
||||||
|
@ -106,45 +150,3 @@ export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||||
@BelongsTo(() => GuaribasUser)
|
@BelongsTo(() => GuaribasUser)
|
||||||
public startedBy: GuaribasUser;
|
public startedBy: GuaribasUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Table
|
|
||||||
export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> {
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
@AutoIncrement
|
|
||||||
@Column
|
|
||||||
public conversationMessageId: number;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasSubject)
|
|
||||||
@Column
|
|
||||||
public subjectId: number;
|
|
||||||
|
|
||||||
@Column(DataType.TEXT)
|
|
||||||
public content: string;
|
|
||||||
|
|
||||||
@Column
|
|
||||||
@CreatedAt
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Column
|
|
||||||
@UpdatedAt
|
|
||||||
public updatedAt: Date;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasConversation)
|
|
||||||
@Column
|
|
||||||
public conversationId: number;
|
|
||||||
|
|
||||||
@BelongsTo(() => GuaribasConversation)
|
|
||||||
public conversation: GuaribasConversation;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasInstance)
|
|
||||||
@Column
|
|
||||||
public instanceId: number;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasUser)
|
|
||||||
@Column
|
|
||||||
public userId: number;
|
|
||||||
|
|
||||||
@BelongsTo(() => GuaribasUser)
|
|
||||||
public user: GuaribasUser;
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,6 +37,9 @@
|
||||||
import { GuaribasUser } from '../../security.gblib/models';
|
import { GuaribasUser } from '../../security.gblib/models';
|
||||||
import { GuaribasConversation, GuaribasConversationMessage } from '../models';
|
import { GuaribasConversation, GuaribasConversationMessage } from '../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base services for Bot Analytics.
|
||||||
|
*/
|
||||||
export class AnalyticsService {
|
export class AnalyticsService {
|
||||||
public async createConversation(
|
public async createConversation(
|
||||||
user: GuaribasUser
|
user: GuaribasUser
|
||||||
|
@ -52,7 +55,7 @@ export class AnalyticsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public createMessage(
|
public async createMessage(
|
||||||
conversation: GuaribasConversation,
|
conversation: GuaribasConversation,
|
||||||
user: GuaribasUser,
|
user: GuaribasUser,
|
||||||
content: string
|
content: string
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*****************************************************************************\
|
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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 { BotAdapter } from 'botbuilder';
|
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { IGBDialog } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
|
||||||
|
|
||||||
export class BotFarmDialog extends IGBDialog {
|
|
||||||
/**
|
|
||||||
* Setup dialogs flows and define services call.
|
|
||||||
*
|
|
||||||
* @param bot The bot adapter.
|
|
||||||
* @param min The minimal bot instance data.
|
|
||||||
*/
|
|
||||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
|
||||||
min.dialogs.add('/createBotFarm', [
|
|
||||||
async step => {
|
|
||||||
const locale = step.context.activity.locale;
|
|
||||||
await step.prompt('choicePrompt', Messages[locale].what_about_me, [
|
|
||||||
'1',
|
|
||||||
'2',
|
|
||||||
'3',
|
|
||||||
'4',
|
|
||||||
'5'
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async step => {
|
|
||||||
const locale = step.context.activity.locale;
|
|
||||||
await step.context.sendActivity(Messages[locale].thanks);
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,19 +36,17 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { IGBInstance } from 'botlib';
|
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||||
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
||||||
import { AzureDeployerService } from '../services/AzureDeployerService';
|
|
||||||
import { GuaribasInstance } from '../../../packages/core.gbapp/models/GBModel';
|
|
||||||
const scanf = require('scanf');
|
const scanf = require('scanf');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles command-line dialog for getting info for Boot Bot.
|
* Handles command-line dialog for getting info for Boot Bot.
|
||||||
*/
|
*/
|
||||||
export class StartDialog {
|
export class StartDialog {
|
||||||
public static async createBaseInstance() {
|
public static async createBaseInstance(installationDeployer: IGBInstallationDeployer) {
|
||||||
// No .env so asks for cloud credentials to start a new farm.
|
// No .env so asks for cloud credentials to start a new farm.
|
||||||
|
|
||||||
if (!fs.existsSync(`.env`)) {
|
if (!fs.existsSync(`.env`)) {
|
||||||
|
@ -75,7 +73,7 @@ export class StartDialog {
|
||||||
// Connects to the cloud and retrieves subscriptions.
|
// Connects to the cloud and retrieves subscriptions.
|
||||||
|
|
||||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
const list = await AzureDeployerService.getSubscriptions(credentials);
|
const list = await installationDeployer.getSubscriptions(credentials);
|
||||||
|
|
||||||
let subscriptionId: string;
|
let subscriptionId: string;
|
||||||
while (subscriptionId === undefined) {
|
while (subscriptionId === undefined) {
|
||||||
|
@ -104,9 +102,8 @@ export class StartDialog {
|
||||||
|
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}Thank you. That is enough information.\nNow building farm...`);
|
process.stdout.write(`${GBAdminService.GB_PROMPT}Thank you. That is enough information.\nNow building farm...`);
|
||||||
|
|
||||||
|
|
||||||
// Prepares the first instance on bot farm.
|
// Prepares the first instance on bot farm.
|
||||||
const instance: IGBInstance = {};
|
const instance = <IGBInstance>{};
|
||||||
|
|
||||||
instance.botId = botId;
|
instance.botId = botId;
|
||||||
instance.cloudUsername = username;
|
instance.cloudUsername = username;
|
||||||
|
@ -123,7 +120,7 @@ export class StartDialog {
|
||||||
|
|
||||||
private static retrieveUsername() {
|
private static retrieveUsername() {
|
||||||
let value = GBConfigService.get('CLOUD_USERNAME');
|
let value = GBConfigService.get('CLOUD_USERNAME');
|
||||||
if (!value) {
|
if (value === undefined) {
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_USERNAME:`);
|
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_USERNAME:`);
|
||||||
value = scanf('%s').replace(/(\n|\r)+$/, '');
|
value = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
}
|
}
|
||||||
|
@ -133,25 +130,23 @@ export class StartDialog {
|
||||||
|
|
||||||
private static retrievePassword() {
|
private static retrievePassword() {
|
||||||
let password = GBConfigService.get('CLOUD_PASSWORD');
|
let password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
if (!password) {
|
if (password === undefined) {
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_PASSWORD:`);
|
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_PASSWORD:`);
|
||||||
password = scanf('%s').replace(/(\n|\r)+$/, '');
|
password = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static retrieveBotId() {
|
private static retrieveBotId() {
|
||||||
let botId = GBConfigService.get('BOT_ID');
|
let botId = GBConfigService.get('BOT_ID');
|
||||||
if (!botId) {
|
if (botId === undefined) {
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
`${GBAdminService.GB_PROMPT}Choose a unique bot Id containing lowercase letters, digits or
|
`${GBAdminService.GB_PROMPT}Choose a unique bot Id containing lowercase letters, digits or
|
||||||
dashes (cannot use dash as the first two or last one characters),
|
dashes (cannot use dash as the first two or last one characters),
|
||||||
cannot start or end with or contain consecutive dashes and having 4 to 42 characters long.\n`
|
cannot start or end with or contain consecutive dashes and having 4 to 42 characters long.\n`
|
||||||
);
|
);
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}BOT_ID:`);
|
process.stdout.write(`${GBAdminService.GB_PROMPT}BOT_ID:`);
|
||||||
|
|
||||||
// TODO: Update this regexp to match description of it.
|
|
||||||
|
|
||||||
botId = scanf('%s').replace(/(\n|\r)+$/, '');
|
botId = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +155,7 @@ cannot start or end with or contain consecutive dashes and having 4 to 42 charac
|
||||||
|
|
||||||
private static retrieveAuthoringKey() {
|
private static retrieveAuthoringKey() {
|
||||||
let authoringKey = GBConfigService.get('NLP_AUTHORING_KEY');
|
let authoringKey = GBConfigService.get('NLP_AUTHORING_KEY');
|
||||||
if (!authoringKey) {
|
if (authoringKey === undefined) {
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
`${
|
`${
|
||||||
GBAdminService.GB_PROMPT
|
GBAdminService.GB_PROMPT
|
||||||
|
@ -179,12 +174,12 @@ cannot start or end with or contain consecutive dashes and having 4 to 42 charac
|
||||||
|
|
||||||
private static retrieveAppId() {
|
private static retrieveAppId() {
|
||||||
let appId = GBConfigService.get('MARKETPLACE_ID');
|
let appId = GBConfigService.get('MARKETPLACE_ID');
|
||||||
process.stdout.write(
|
if (appId === undefined) {
|
||||||
`Sorry, this part cannot be automated yet due to Microsoft schedule,
|
process.stdout.write(
|
||||||
|
`Sorry, this part cannot be automated yet due to Microsoft schedule,
|
||||||
please go to https://apps.dev.microsoft.com/portal/register-app to
|
please go to https://apps.dev.microsoft.com/portal/register-app to
|
||||||
generate manually an App ID and App Secret.\n`
|
generate manually an App ID and App Secret.\n`
|
||||||
);
|
);
|
||||||
if (!appId) {
|
|
||||||
process.stdout.write('Generated Application Id (MARKETPLACE_ID):');
|
process.stdout.write('Generated Application Id (MARKETPLACE_ID):');
|
||||||
appId = scanf('%s').replace(/(\n|\r)+$/, '');
|
appId = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
}
|
}
|
||||||
|
@ -194,7 +189,7 @@ generate manually an App ID and App Secret.\n`
|
||||||
|
|
||||||
private static retrieveAppPassword() {
|
private static retrieveAppPassword() {
|
||||||
let appPassword = GBConfigService.get('MARKETPLACE_SECRET');
|
let appPassword = GBConfigService.get('MARKETPLACE_SECRET');
|
||||||
if (!appPassword) {
|
if (appPassword === undefined) {
|
||||||
process.stdout.write('Generated Password (MARKETPLACE_SECRET):');
|
process.stdout.write('Generated Password (MARKETPLACE_SECRET):');
|
||||||
appPassword = scanf('%s').replace(/(\n|\r)+$/, '');
|
appPassword = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
}
|
}
|
||||||
|
@ -207,7 +202,7 @@ generate manually an App ID and App Secret.\n`
|
||||||
const map = {};
|
const map = {};
|
||||||
let index = 1;
|
let index = 1;
|
||||||
list.forEach(element => {
|
list.forEach(element => {
|
||||||
console.log(`${index}: ${element.displayName} (${element.subscriptionId})`);
|
GBLog.info(`${index}: ${element.displayName} (${element.subscriptionId})`);
|
||||||
map[index++] = element;
|
map[index++] = element;
|
||||||
});
|
});
|
||||||
let subscriptionIndex;
|
let subscriptionIndex;
|
||||||
|
@ -222,8 +217,8 @@ generate manually an App ID and App Secret.\n`
|
||||||
|
|
||||||
private static retrieveLocation() {
|
private static retrieveLocation() {
|
||||||
let location = GBConfigService.get('CLOUD_LOCATION');
|
let location = GBConfigService.get('CLOUD_LOCATION');
|
||||||
if (!location) {
|
if (location === undefined) {
|
||||||
process.stdout.write("CLOUD_LOCATION (eg. 'westus'):");
|
process.stdout.write('CLOUD_LOCATION (eg. westus):');
|
||||||
location = scanf('%s');
|
location = scanf('%s');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,19 +36,30 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for Azure Deployer.
|
||||||
|
*/
|
||||||
export class GBAzureDeployerPackage implements IGBPackage {
|
export class GBAzureDeployerPackage implements IGBPackage {
|
||||||
|
public sysPackages: IGBPackage[];
|
||||||
public sysPackages: IGBPackage[] = null;
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
|
}
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
public unloadPackage(core: IGBCoreService): void {}
|
GBLog.verbose(`loadPackage called.`);
|
||||||
|
}
|
||||||
public loadBot(min: GBMinInstance): void {}
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
public unloadBot(min: GBMinInstance): void {}
|
}
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {}
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`loadBot called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,39 +37,40 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
||||||
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
|
||||||
import { ResourceManagementClient, SubscriptionClient } from 'azure-arm-resource';
|
import { ResourceManagementClient, SubscriptionClient } from 'azure-arm-resource';
|
||||||
import { SearchManagementClient } from 'azure-arm-search';
|
import { SearchManagementClient } from 'azure-arm-search';
|
||||||
import { SqlManagementClient } from 'azure-arm-sql';
|
import { SqlManagementClient } from 'azure-arm-sql';
|
||||||
import { WebSiteManagementClient } from 'azure-arm-website';
|
import { WebSiteManagementClient } from 'azure-arm-website';
|
||||||
|
//tslint:disable-next-line:no-submodule-imports
|
||||||
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
||||||
import { GBService, IGBInstance } from 'botlib';
|
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
|
||||||
import { HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
import { HttpHeaders, HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
||||||
import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer';
|
|
||||||
import * as simplegit from 'simple-git/promise';
|
|
||||||
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||||
import { GBCorePackage } from '../../../packages/core.gbapp';
|
import { GBCorePackage } from '../../../packages/core.gbapp';
|
||||||
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
||||||
import { GuaribasInstance } from '../../../packages/core.gbapp/models/GBModel';
|
import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer';
|
||||||
|
|
||||||
const Spinner = require('cli-spinner').Spinner;
|
const Spinner = require('cli-spinner').Spinner;
|
||||||
const scanf = require('scanf');
|
|
||||||
const git = simplegit();
|
// tslint:disable-next-line:no-submodule-imports
|
||||||
const logger = require('../../../src/logger');
|
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
|
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
|
||||||
const publicIp = require('public-ip');
|
const publicIp = require('public-ip');
|
||||||
|
|
||||||
export class AzureDeployerService extends GBService {
|
/**
|
||||||
public static apiVersion = '2017-12-01';
|
* Deployer for Microsoft cloud.
|
||||||
public static defaultEndPoint = 'http://localhost:4242';
|
*/
|
||||||
|
export class AzureDeployerService implements IGBInstallationDeployer {
|
||||||
|
public apiVersion = '2017-12-01';
|
||||||
|
public defaultEndPoint = 'http://localhost:4242';
|
||||||
public instance: IGBInstance;
|
public instance: IGBInstance;
|
||||||
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
||||||
public webSiteClient: WebSiteManagementClient;
|
public webSiteClient: WebSiteManagementClient;
|
||||||
public storageClient: SqlManagementClient;
|
public storageClient: SqlManagementClient;
|
||||||
public cognitiveClient: CognitiveServicesManagementClient;
|
public cognitiveClient: CognitiveServicesManagementClient;
|
||||||
public searchClient: SearchManagementClient;
|
public searchClient: SearchManagementClient;
|
||||||
public static provider = 'Microsoft.BotService';
|
public provider = 'Microsoft.BotService';
|
||||||
public subscriptionClient: SubscriptionClient.SubscriptionClient;
|
public subscriptionClient: SubscriptionClient.SubscriptionClient;
|
||||||
public accessToken: string;
|
public accessToken: string;
|
||||||
public location: string;
|
public location: string;
|
||||||
|
@ -78,15 +79,28 @@ export class AzureDeployerService extends GBService {
|
||||||
public deployer: GBDeployer;
|
public deployer: GBDeployer;
|
||||||
|
|
||||||
constructor(deployer: GBDeployer) {
|
constructor(deployer: GBDeployer) {
|
||||||
super();
|
|
||||||
this.deployer = deployer;
|
this.deployer = deployer;
|
||||||
}
|
}
|
||||||
public static async getSubscriptions(credentials) {
|
|
||||||
|
private static createRequestObject(url: string, accessToken: string, verb: HttpMethods, body: string) {
|
||||||
|
const req = new WebResource();
|
||||||
|
req.method = verb;
|
||||||
|
req.url = url;
|
||||||
|
req.headers.set('Content-Type', 'application/json');
|
||||||
|
req.headers.set('accept-language', '*');
|
||||||
|
req.headers.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
req.body = body;
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSubscriptions(credentials) {
|
||||||
const subscriptionClient = new SubscriptionClient.default(credentials);
|
const subscriptionClient = new SubscriptionClient.default(credentials);
|
||||||
|
|
||||||
return subscriptionClient.subscriptions.list();
|
return subscriptionClient.subscriptions.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getKBSearchSchema(indexName) {
|
public getKBSearchSchema(indexName) {
|
||||||
return {
|
return {
|
||||||
name: indexName,
|
name: indexName,
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -182,21 +196,18 @@ export class AzureDeployerService extends GBService {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
scoringProfiles: [],
|
scoringProfiles: [],
|
||||||
defaultScoringProfile: null,
|
defaultScoringProfile: undefined,
|
||||||
corsOptions: null
|
corsOptions: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async updateBotProxy(botId, group, endpoint) {
|
public async updateBotProxy(botId, group, endpoint) {
|
||||||
const baseUrl = `https://management.azure.com/`;
|
const baseUrl = `https://management.azure.com/`;
|
||||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||||
|
|
||||||
const accessToken = await GBAdminService.getADALTokenFromUsername(
|
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
|
||||||
username,
|
|
||||||
password
|
|
||||||
);
|
|
||||||
const httpClient = new ServiceClient();
|
const httpClient = new ServiceClient();
|
||||||
|
|
||||||
const parameters = {
|
const parameters = {
|
||||||
|
@ -207,22 +218,18 @@ export class AzureDeployerService extends GBService {
|
||||||
|
|
||||||
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
||||||
this.provider
|
this.provider
|
||||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
}/botServices/${botId}?api-version=${this.apiVersion}`;
|
||||||
const url = UrlJoin(baseUrl, query);
|
const url = urlJoin(baseUrl, query);
|
||||||
const req = this.createRequestObject(
|
const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
|
||||||
url,
|
|
||||||
accessToken,
|
|
||||||
'PATCH',
|
|
||||||
JSON.stringify(parameters)
|
|
||||||
);
|
|
||||||
const res = await httpClient.sendRequest(req);
|
const res = await httpClient.sendRequest(req);
|
||||||
if (!(res.bodyAsJson as any).id) {
|
// CHECK
|
||||||
|
if (!JSON.parse(res.bodyAsText).id) {
|
||||||
throw res.bodyAsText;
|
throw res.bodyAsText;
|
||||||
}
|
}
|
||||||
logger.info(`Bot proxy updated at: ${endpoint}.`);
|
GBLog.info(`Bot proxy updated at: ${endpoint}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async openStorageFirewall(groupName, serverName) {
|
public async openStorageFirewall(groupName, serverName) {
|
||||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||||
|
@ -239,19 +246,6 @@ export class AzureDeployerService extends GBService {
|
||||||
await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params);
|
await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createRequestObject(url: string, accessToken: string, verb: HttpMethods, body: string) {
|
|
||||||
const req = new WebResource();
|
|
||||||
req.method = verb;
|
|
||||||
req.url = url;
|
|
||||||
req.headers = {};
|
|
||||||
req.headers['Content-Type'] = 'application/json';
|
|
||||||
req.headers['accept-language'] = '*';
|
|
||||||
req.headers.Authorization = 'Bearer ' + accessToken;
|
|
||||||
req.body = body;
|
|
||||||
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deployFarm(
|
public async deployFarm(
|
||||||
proxyAddress: string,
|
proxyAddress: string,
|
||||||
instance: IGBInstance,
|
instance: IGBInstance,
|
||||||
|
@ -261,22 +255,20 @@ export class AzureDeployerService extends GBService {
|
||||||
const culture = 'en-us';
|
const culture = 'en-us';
|
||||||
|
|
||||||
this.initServices(credentials, subscriptionId);
|
this.initServices(credentials, subscriptionId);
|
||||||
|
|
||||||
const spinner = new Spinner('%s');
|
const spinner = new Spinner('%s');
|
||||||
spinner.start();
|
spinner.start();
|
||||||
spinner.setSpinnerString('|/-\\');
|
spinner.setSpinnerString('|/-\\');
|
||||||
|
|
||||||
let keys: any;
|
let keys: any;
|
||||||
const name = instance.botId;
|
const name = instance.botId;
|
||||||
|
|
||||||
logger.info(`Deploying Deploy Group (It may take a few minutes)...`);
|
GBLog.info(`Deploying Deploy Group (It may take a few minutes)...`);
|
||||||
await this.createDeployGroup(name, instance.cloudLocation);
|
await this.createDeployGroup(name, instance.cloudLocation);
|
||||||
|
|
||||||
logger.info(`Deploying Bot Server...`);
|
GBLog.info(`Deploying Bot Server...`);
|
||||||
const serverFarm = await this.createHostingPlan(name, `${name}-server-plan`, instance.cloudLocation);
|
const serverFarm = await this.createHostingPlan(name, `${name}-server-plan`, instance.cloudLocation);
|
||||||
await this.createServer(serverFarm.id, name, `${name}-server`, instance.cloudLocation);
|
await this.createServer(serverFarm.id, name, `${name}-server`, instance.cloudLocation);
|
||||||
|
|
||||||
logger.info(`Deploying Bot Storage...`);
|
GBLog.info(`Deploying Bot Storage...`);
|
||||||
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
|
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
|
||||||
const administratorPassword = GBAdminService.getRndPassword();
|
const administratorPassword = GBAdminService.getRndPassword();
|
||||||
const storageServer = `${name.toLowerCase()}-storage-server`;
|
const storageServer = `${name.toLowerCase()}-storage-server`;
|
||||||
|
@ -296,7 +288,7 @@ export class AzureDeployerService extends GBService {
|
||||||
instance.storageDialect = 'mssql';
|
instance.storageDialect = 'mssql';
|
||||||
instance.storageServer = storageServer;
|
instance.storageServer = storageServer;
|
||||||
|
|
||||||
logger.info(`Deploying Search...`);
|
GBLog.info(`Deploying Search...`);
|
||||||
const searchName = `${name}-search`.toLowerCase();
|
const searchName = `${name}-search`.toLowerCase();
|
||||||
await this.createSearch(name, searchName, instance.cloudLocation);
|
await this.createSearch(name, searchName, instance.cloudLocation);
|
||||||
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
|
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
|
||||||
|
@ -304,27 +296,28 @@ export class AzureDeployerService extends GBService {
|
||||||
instance.searchIndex = 'azuresql-index';
|
instance.searchIndex = 'azuresql-index';
|
||||||
instance.searchIndexer = 'azuresql-indexer';
|
instance.searchIndexer = 'azuresql-indexer';
|
||||||
instance.searchKey = searchKeys.primaryKey;
|
instance.searchKey = searchKeys.primaryKey;
|
||||||
this.deployer.rebuildIndex(instance);
|
this.deployer.rebuildIndex(instance, this.deployer);
|
||||||
|
|
||||||
logger.info(`Deploying Speech...`);
|
GBLog.info(`Deploying Speech...`);
|
||||||
const speech = await this.createSpeech(name, `${name}-speech`, instance.cloudLocation);
|
const speech = await this.createSpeech(name, `${name}-speech`, instance.cloudLocation);
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
||||||
instance.speechKeyEndpoint = speech.endpoint;
|
instance.speechEndpoint = speech.endpoint;
|
||||||
instance.speechKey = keys.key1;
|
instance.speechKey = keys.key1;
|
||||||
|
|
||||||
logger.info(`Deploying SpellChecker...`);
|
GBLog.info(`Deploying SpellChecker...`);
|
||||||
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`, instance.cloudLocation);
|
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`);
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, spellChecker.name);
|
keys = await this.cognitiveClient.accounts.listKeys(name, spellChecker.name);
|
||||||
instance.spellCheckerKey = keys.key1;
|
instance.spellcheckerKey = keys.key1;
|
||||||
instance.spellCheckerEndpoint = spellChecker.endpoint;
|
instance.spellcheckerEndpoint = spellChecker.endpoint;
|
||||||
|
|
||||||
logger.info(`Deploying Text Analytics...`);
|
GBLog.info(`Deploying Text Analytics...`);
|
||||||
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
|
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
|
keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
|
||||||
instance.textAnalyticsEndpoint = textAnalytics.endpoint;
|
|
||||||
|
instance.textAnalyticsEndpoint = textAnalytics.endpoint.replace(`/text/analytics/v2.0`, '');
|
||||||
instance.textAnalyticsKey = keys.key1;
|
instance.textAnalyticsKey = keys.key1;
|
||||||
|
|
||||||
logger.info(`Deploying NLP...`);
|
GBLog.info(`Deploying NLP...`);
|
||||||
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
|
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
||||||
const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey);
|
const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey);
|
||||||
|
@ -333,8 +326,8 @@ export class AzureDeployerService extends GBService {
|
||||||
instance.nlpKey = keys.key1;
|
instance.nlpKey = keys.key1;
|
||||||
instance.nlpAppId = nlpAppId;
|
instance.nlpAppId = nlpAppId;
|
||||||
|
|
||||||
logger.info(`Deploying Bot...`);
|
GBLog.info(`Deploying Bot...`);
|
||||||
instance.botEndpoint = AzureDeployerService.defaultEndPoint;
|
instance.botEndpoint = this.defaultEndPoint;
|
||||||
|
|
||||||
instance = await this.internalDeployBot(
|
instance = await this.internalDeployBot(
|
||||||
instance,
|
instance,
|
||||||
|
@ -347,27 +340,27 @@ export class AzureDeployerService extends GBService {
|
||||||
'global',
|
'global',
|
||||||
instance.nlpAppId,
|
instance.nlpAppId,
|
||||||
instance.nlpKey,
|
instance.nlpKey,
|
||||||
instance.appId,
|
instance.marketplaceId,
|
||||||
instance.appPassword,
|
instance.marketplacePassword,
|
||||||
instance.cloudSubscriptionId
|
instance.cloudSubscriptionId
|
||||||
);
|
);
|
||||||
|
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployToCloud(
|
public async deployToCloud(
|
||||||
title,
|
title: string,
|
||||||
username,
|
username: string,
|
||||||
password,
|
password: string,
|
||||||
cloudLocation,
|
cloudLocation: string,
|
||||||
authoringKey,
|
authoringKey: string,
|
||||||
appId,
|
appId: string,
|
||||||
appPassword,
|
appPassword: string,
|
||||||
subscriptionId
|
subscriptionId: string
|
||||||
) {
|
) {
|
||||||
|
const instance = <IGBInstance>{};
|
||||||
const instance: IGBInstance = {};
|
|
||||||
|
|
||||||
instance.botId = title;
|
instance.botId = title;
|
||||||
instance.cloudUsername = username;
|
instance.cloudUsername = username;
|
||||||
|
@ -380,10 +373,9 @@ export class AzureDeployerService extends GBService {
|
||||||
instance.adminPass = GBAdminService.getRndPassword();
|
instance.adminPass = GBAdminService.getRndPassword();
|
||||||
|
|
||||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
this.deployFarm(`http://${instance.botId}.azurewebsites.net`, instance, credentials, subscriptionId);
|
// tslint:disable-next-line:no-http-string
|
||||||
|
const url = `http://${instance.botId}.azurewebsites.net`;
|
||||||
// TODO: Copy github to webapp.
|
this.deployFarm(url, instance, credentials, subscriptionId);
|
||||||
//const status = await git.status();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initServices(credentials: any, subscriptionId: string) {
|
private initServices(credentials: any, subscriptionId: string) {
|
||||||
|
@ -395,19 +387,6 @@ export class AzureDeployerService extends GBService {
|
||||||
this.accessToken = credentials.tokenCache._entries[0].accessToken;
|
this.accessToken = credentials.tokenCache._entries[0].accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateWebisteConfig(group, serverFarmId, name, location) {
|
|
||||||
const siteConfig = {
|
|
||||||
location: location,
|
|
||||||
serverFarmId: serverFarmId,
|
|
||||||
numberOfWorkers: 1,
|
|
||||||
phpVersion: '5.5'
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Copy .env to app settings.
|
|
||||||
|
|
||||||
return this.webSiteClient.webApps.createOrUpdateConfiguration(group, name, siteConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createStorageServer(group, name, administratorLogin, administratorPassword, serverName, location) {
|
private async createStorageServer(group, name, administratorLogin, administratorPassword, serverName, location) {
|
||||||
const params = {
|
const params = {
|
||||||
location: location,
|
location: location,
|
||||||
|
@ -420,22 +399,16 @@ export class AzureDeployerService extends GBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async registerProviders(subscriptionId, baseUrl, accessToken) {
|
private async registerProviders(subscriptionId, baseUrl, accessToken) {
|
||||||
const query = `subscriptions/${subscriptionId}/providers/${
|
const query = `subscriptions/${subscriptionId}/providers/${this.provider}/register?api-version=2018-02-01`;
|
||||||
AzureDeployerService.provider
|
const requestUrl = urlJoin(baseUrl, query);
|
||||||
}/register?api-version=2018-02-01`;
|
|
||||||
const requestUrl = UrlJoin(baseUrl, query);
|
|
||||||
|
|
||||||
const req = new WebResource();
|
const req = new WebResource();
|
||||||
req.method = 'POST';
|
req.method = 'POST';
|
||||||
req.url = requestUrl;
|
req.url = requestUrl;
|
||||||
req.headers = {};
|
req.headers = <any>{};
|
||||||
req.headers['Content-Type'] = 'application/json; charset=utf-8';
|
req.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||||
req.headers['accept-language'] = '*';
|
req.headers['accept-language'] = '*';
|
||||||
req.headers.Authorization = 'Bearer ' + accessToken;
|
(req.headers as any).Authorization = `Bearer ${accessToken}`;
|
||||||
|
|
||||||
const httpClient = new ServiceClient();
|
|
||||||
const res = await httpClient.sendRequest(req);
|
|
||||||
// TODO: Check res for error.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -455,7 +428,7 @@ export class AzureDeployerService extends GBService {
|
||||||
appId,
|
appId,
|
||||||
appPassword,
|
appPassword,
|
||||||
subscriptionId
|
subscriptionId
|
||||||
) {
|
): Promise<IGBInstance> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const baseUrl = `https://management.azure.com/`;
|
const baseUrl = `https://management.azure.com/`;
|
||||||
await this.registerProviders(subscriptionId, baseUrl, accessToken);
|
await this.registerProviders(subscriptionId, baseUrl, accessToken);
|
||||||
|
@ -487,25 +460,27 @@ export class AzureDeployerService extends GBService {
|
||||||
|
|
||||||
const httpClient = new ServiceClient();
|
const httpClient = new ServiceClient();
|
||||||
let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
||||||
AzureDeployerService.provider
|
this.provider
|
||||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
}/botServices/${botId}?api-version=${this.apiVersion}`;
|
||||||
let url = UrlJoin(baseUrl, query);
|
let url = urlJoin(baseUrl, query);
|
||||||
let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters));
|
let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters));
|
||||||
const res = await httpClient.sendRequest(req);
|
const res = await httpClient.sendRequest(req);
|
||||||
if (!(res.bodyAsJson as any).id) {
|
if (!JSON.parse(res.bodyAsText).id) {
|
||||||
reject(res.bodyAsText);
|
reject(res.bodyAsText);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
|
//tslint:disable-next-line:max-line-length
|
||||||
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
|
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
|
||||||
AzureDeployerService.apiVersion
|
this.apiVersion
|
||||||
}`;
|
}`;
|
||||||
url = UrlJoin(baseUrl, query);
|
url = urlJoin(baseUrl, query);
|
||||||
req = AzureDeployerService.createRequestObject(url, accessToken, 'GET', JSON.stringify(parameters));
|
req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(parameters));
|
||||||
const resChannel = await httpClient.sendRequest(req);
|
const resChannel = await httpClient.sendRequest(req);
|
||||||
const key = (resChannel.bodyAsJson as any).properties.properties.sites[0].key;
|
const key = JSON.parse(resChannel.bodyAsText).properties.properties.sites[0].key;
|
||||||
instance.webchatKey = key;
|
instance.webchatKey = key;
|
||||||
resolve(instance);
|
resolve(instance);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -529,8 +504,8 @@ export class AzureDeployerService extends GBService {
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = JSON.stringify(parameters);
|
const body = JSON.stringify(parameters);
|
||||||
const apps = await this.makeNlpRequest(location, authoringKey, null, 'GET', 'apps');
|
const apps = await this.makeNlpRequest(location, authoringKey, undefined, 'GET', 'apps');
|
||||||
const app = (apps.bodyAsJson as any).filter(x => x.name == name)[0];
|
const app = JSON.parse(apps.bodyAsText).filter(x => x.name === name)[0];
|
||||||
let id: string;
|
let id: string;
|
||||||
if (!app) {
|
if (!app) {
|
||||||
const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps');
|
const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps');
|
||||||
|
@ -552,10 +527,9 @@ export class AzureDeployerService extends GBService {
|
||||||
const req = new WebResource();
|
const req = new WebResource();
|
||||||
req.method = method;
|
req.method = method;
|
||||||
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
|
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
|
||||||
req.headers = {};
|
req.headers.set('Content-Type', 'application/json');
|
||||||
req.headers['Content-Type'] = 'application/json';
|
req.headers.set('accept-language', '*');
|
||||||
req.headers['accept-language'] = '*';
|
req.headers.set('Ocp-Apim-Subscription-Key', authoringKey);
|
||||||
req.headers['Ocp-Apim-Subscription-Key'] = authoringKey;
|
|
||||||
req.body = body;
|
req.body = body;
|
||||||
const httpClient = new ServiceClient();
|
const httpClient = new ServiceClient();
|
||||||
|
|
||||||
|
@ -601,7 +575,7 @@ export class AzureDeployerService extends GBService {
|
||||||
return await this.createCognitiveServices(group, name, location, 'LUIS');
|
return await this.createCognitiveServices(group, name, location, 'LUIS');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSpellChecker(group, name, location): Promise<CognitiveServicesAccount> {
|
private async createSpellChecker(group, name): Promise<CognitiveServicesAccount> {
|
||||||
return await this.createCognitiveServices(group, name, 'global', 'Bing.SpellCheck.v7');
|
return await this.createCognitiveServices(group, name, 'global', 'Bing.SpellCheck.v7');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,6 +585,7 @@ export class AzureDeployerService extends GBService {
|
||||||
|
|
||||||
private async createDeployGroup(name, location) {
|
private async createDeployGroup(name, location) {
|
||||||
const params = { location: location };
|
const params = { location: location };
|
||||||
|
|
||||||
return this.resourceClient.resourceGroups.createOrUpdate(name, params);
|
return this.resourceClient.resourceGroups.createOrUpdate(name, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +608,7 @@ export class AzureDeployerService extends GBService {
|
||||||
location: location,
|
location: location,
|
||||||
serverFarmId: farmId
|
serverFarmId: farmId
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.webSiteClient.webApps.createOrUpdate(group, name, parameters);
|
return this.webSiteClient.webApps.createOrUpdate(group, name, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,30 +36,33 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { ConsoleDirectLine } from './services/ConsoleDirectLine';
|
import { ConsoleDirectLine } from './services/ConsoleDirectLine';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for console.glib.
|
||||||
|
*/
|
||||||
export class GBConsolePackage implements IGBPackage {
|
export class GBConsolePackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
public channel: ConsoleDirectLine;
|
public channel: ConsoleDirectLine;
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
GBLog.verbose(`getDialogs called.`);
|
||||||
|
}
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
GBLog.verbose(`loadPackage called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
this.channel = new ConsoleDirectLine(min.instance.webchatKey);
|
this.channel = new ConsoleDirectLine(min.instance.webchatKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,192 +1,176 @@
|
||||||
/*****************************************************************************\
|
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
const Path = require('path');
|
|
||||||
const Fs = require('fs');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const Parse = require('csv-parse');
|
|
||||||
const Async = require('async');
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Swagger = require('swagger-client');
|
const Swagger = require('swagger-client');
|
||||||
const rp = require('request-promise');
|
const rp = require('request-promise');
|
||||||
import { GBService } from 'botlib';
|
import { GBLog, GBService } from 'botlib';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bot simulator in terminal window.
|
||||||
|
*/
|
||||||
export class ConsoleDirectLine extends GBService {
|
export class ConsoleDirectLine extends GBService {
|
||||||
|
public pollInterval: number = 1000;
|
||||||
|
public directLineSecret: string = '';
|
||||||
|
public directLineClientName: string = 'DirectLineClient';
|
||||||
|
public directLineSpecUrl: string = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||||
|
|
||||||
public pollInterval = 1000;
|
constructor(directLineSecret: string) {
|
||||||
public directLineSecret = '';
|
super();
|
||||||
public directLineClientName = 'DirectLineClient';
|
|
||||||
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
|
||||||
|
|
||||||
constructor(directLineSecret) {
|
this.directLineSecret = directLineSecret;
|
||||||
super();
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
const directLineClient = rp(this.directLineSpecUrl)
|
||||||
this.directLineSecret = directLineSecret;
|
.then((spec: string) => {
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
// TODO: Migrate to Swagger 3.
|
return new Swagger({
|
||||||
const directLineClient = rp(this.directLineSpecUrl)
|
spec: JSON.parse(spec.trim()),
|
||||||
.then(function (spec) {
|
usePromise: true
|
||||||
return new Swagger({
|
});
|
||||||
spec: JSON.parse(spec.trim()),
|
})
|
||||||
usePromise: true
|
.then(client => {
|
||||||
});
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
})
|
client.clientAuthorizations.add(
|
||||||
.then(function (client) {
|
'AuthorizationBotConnector',
|
||||||
client.clientAuthorizations.add('AuthorizationBotConnector',
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'));
|
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${directLineSecret}`, 'header')
|
||||||
return client;
|
);
|
||||||
})
|
|
||||||
.catch(function (err) {
|
return client;
|
||||||
console.error('Error initializing DirectLine client', err);
|
})
|
||||||
});
|
.catch(err => {
|
||||||
|
GBLog.error(`Error initializing DirectLine client ${err}`);
|
||||||
// TODO: Remove *this* issue.
|
});
|
||||||
const _this_ = this;
|
|
||||||
directLineClient.then((client) => {
|
const _this_ = this;
|
||||||
client.Conversations.Conversations_StartConversation()
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
.then(function (response) {
|
directLineClient.then(client => {
|
||||||
return response.obj.conversationId;
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
})
|
client.Conversations.Conversations_StartConversation()
|
||||||
.then(function (conversationId) {
|
.then(response => {
|
||||||
_this_.sendMessagesFromConsole(client, conversationId);
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
_this_.pollMessages(client, conversationId);
|
return response.obj.conversationId;
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.then(conversationId => {
|
||||||
console.error('Error starting conversation', err);
|
_this_.sendMessagesFromConsole(client, conversationId);
|
||||||
});
|
_this_.pollMessages(client, conversationId);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
GBLog.error(`Error starting conversation ${err}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMessagesFromConsole(client, conversationId) {
|
||||||
|
const _this_ = this;
|
||||||
|
process.stdin.resume();
|
||||||
|
const stdin = process.stdin;
|
||||||
|
process.stdout.write('Command> ');
|
||||||
|
stdin.addListener('data', e => {
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
const input: string = e.toString().trim();
|
||||||
|
if (input !== undefined) {
|
||||||
|
// exit
|
||||||
|
if (input.toLowerCase() === 'exit') {
|
||||||
|
return process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
client.Conversations.Conversations_PostActivity({
|
||||||
|
conversationId: conversationId,
|
||||||
|
activity: {
|
||||||
|
textFormat: 'plain',
|
||||||
|
text: input,
|
||||||
|
type: 'message',
|
||||||
|
from: {
|
||||||
|
id: _this_.directLineClientName,
|
||||||
|
name: _this_.directLineClientName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
GBLog.error(`Error sending message: ${err}`);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
public sendMessagesFromConsole(client, conversationId) {
|
|
||||||
const _this_ = this;
|
|
||||||
process.stdin.resume();
|
|
||||||
const stdin = process.stdin;
|
|
||||||
process.stdout.write('Command> ');
|
process.stdout.write('Command> ');
|
||||||
stdin.addListener('data', function (e) {
|
}
|
||||||
const input = e.toString().trim();
|
});
|
||||||
if (input) {
|
}
|
||||||
// exit
|
|
||||||
if (input.toLowerCase() === 'exit') {
|
|
||||||
return process.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Conversations.Conversations_PostActivity(
|
public pollMessages(client, conversationId) {
|
||||||
{
|
const _this_ = this;
|
||||||
conversationId: conversationId,
|
GBLog.info(`Starting polling message for conversationId: ${conversationId}`);
|
||||||
activity: {
|
let watermark;
|
||||||
textFormat: 'plain',
|
setInterval(() => {
|
||||||
text: input,
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
type: 'message',
|
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
|
||||||
from: {
|
.then(response => {
|
||||||
id: _this_.directLineClientName,
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
name: _this_.directLineClientName
|
watermark = response.obj.watermark;
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(function (err) {
|
|
||||||
console.error('Error sending message:', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.stdout.write('Command> ');
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
}
|
return response.obj.activities;
|
||||||
});
|
})
|
||||||
|
.then(_this_.printMessages, _this_.directLineClientName);
|
||||||
|
// tslint:disable-next-line:align
|
||||||
|
}, this.pollInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
|
public printMessages(activities, directLineClientName) {
|
||||||
|
if (activities && activities.length) {
|
||||||
|
// ignore own messages
|
||||||
|
activities = activities.filter(m => {
|
||||||
|
return m.from.id !== directLineClientName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activities.length) {
|
||||||
|
// print other messages
|
||||||
|
activities.forEach(activity => {
|
||||||
|
GBLog.info(activity.text);
|
||||||
|
// tslint:disable-next-line:align
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
process.stdout.write('Command> ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
|
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
|
public printMessage(activity) {
|
||||||
|
if (activity.text) {
|
||||||
|
GBLog.info(activity.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TBD: Poll Messages from conversation using DirectLine client */
|
if (activity.attachments) {
|
||||||
public pollMessages(client, conversationId) {
|
activity.attachments.forEach(attachment => {
|
||||||
const _this_ = this;
|
switch (attachment.contentType) {
|
||||||
console.log('Starting polling message for conversationId: ' + conversationId);
|
case 'application/vnd.microsoft.card.hero':
|
||||||
let watermark = null;
|
this.renderHeroCard(attachment);
|
||||||
setInterval(function () {
|
break;
|
||||||
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
|
|
||||||
.then(function (response) {
|
|
||||||
watermark = response.obj.watermark; // use watermark so subsequent requests skip old messages
|
|
||||||
return response.obj.activities;
|
|
||||||
})
|
|
||||||
.then(_this_.printMessages, _this_.directLineClientName);
|
|
||||||
}, this.pollInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public printMessages(activities, directLineClientName) {
|
case 'image/png':
|
||||||
|
GBLog.info(`Opening the requested image ${attachment.contentUrl}`);
|
||||||
|
open(attachment.contentUrl);
|
||||||
|
break;
|
||||||
|
|
||||||
if (activities && activities.length) {
|
default:
|
||||||
// ignore own messages
|
GBLog.info(`Unknown contentType: ${attachment.contentType}`);
|
||||||
activities = activities.filter(function (m) { return m.from.id !== directLineClientName; });
|
break;
|
||||||
|
|
||||||
if (activities.length) {
|
|
||||||
|
|
||||||
// print other messages
|
|
||||||
activities.forEach(activity => {
|
|
||||||
console.log(activity.text);
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
process.stdout.write('Command> ');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
|
|
||||||
public printMessage(activity) {
|
// tslint:disable:no-unsafe-any
|
||||||
if (activity.text) {
|
public renderHeroCard(attachment) {
|
||||||
console.log(activity.text);
|
const width = 70;
|
||||||
}
|
const contentLine = content => {
|
||||||
|
return `${' '.repeat((width - content.length) / 2)}content${' '.repeat((width - content.length) / 2)}`;
|
||||||
|
};
|
||||||
|
|
||||||
if (activity.attachments) {
|
GBLog.info(`/${'*'.repeat(width + 1)}`);
|
||||||
activity.attachments.forEach(function (attachment) {
|
GBLog.info(`*${contentLine(attachment.content.title)}*`);
|
||||||
switch (attachment.contentType) {
|
GBLog.info(`*${' '.repeat(width)}*`);
|
||||||
case 'application/vnd.microsoft.card.hero':
|
GBLog.info(`*${contentLine(attachment.content.text)}*`);
|
||||||
this.renderHeroCard(attachment);
|
GBLog.info(`${'*'.repeat(width + 1)}/`);
|
||||||
break;
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
case 'image/png':
|
|
||||||
console.log('Opening the requested image ' + attachment.contentUrl);
|
|
||||||
open(attachment.contentUrl);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderHeroCard(attachment) {
|
|
||||||
const width = 70;
|
|
||||||
const contentLine = function (content) {
|
|
||||||
return ' '.repeat((width - content.length) / 2) +
|
|
||||||
content +
|
|
||||||
' '.repeat((width - content.length) / 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('/' + '*'.repeat(width + 1));
|
|
||||||
console.log('*' + contentLine(attachment.content.title) + '*');
|
|
||||||
console.log('*' + ' '.repeat(width) + '*');
|
|
||||||
console.log('*' + contentLine(attachment.content.text) + '*');
|
|
||||||
console.log('*'.repeat(width + 1) + '/');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,10 +38,12 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import {WaterfallDialog } from 'botbuilder-dialogs';
|
import {WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for Welcoming people.
|
||||||
|
*/
|
||||||
export class WelcomeDialog extends IGBDialog {
|
export class WelcomeDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -73,9 +75,9 @@ export class WelcomeDialog extends IGBDialog {
|
||||||
await step.replaceDialog('/ask', { firstTime: true });
|
await step.replaceDialog('/ask', { firstTime: true });
|
||||||
|
|
||||||
if (
|
if (
|
||||||
step.context.activity &&
|
step.context.activity !== undefined &&
|
||||||
step.context.activity.type == 'message' &&
|
step.context.activity.type === 'message' &&
|
||||||
step.context.activity.text != ''
|
step.context.activity.text !== ''
|
||||||
) {
|
) {
|
||||||
await step.replaceDialog('/answer', { query: step.context.activity.text });
|
await step.replaceDialog('/answer', { query: step.context.activity.text });
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,10 +38,11 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
/**
|
||||||
|
* Dialog for the bot explains about itself.
|
||||||
|
*/
|
||||||
export class WhoAmIDialog extends IGBDialog {
|
export class WhoAmIDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -55,7 +56,7 @@ export class WhoAmIDialog extends IGBDialog {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
await step.context.sendActivity(`${min.instance.description}`);
|
await step.context.sendActivity(`${min.instance.description}`);
|
||||||
|
|
||||||
if (min.instance.whoAmIVideo) {
|
if (min.instance.whoAmIVideo !== undefined) {
|
||||||
await step.context.sendActivity(Messages[locale].show_video);
|
await step.context.sendActivity(Messages[locale].show_video);
|
||||||
await min.conversationalService.sendEvent(step, 'play', {
|
await min.conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'video',
|
playerType: 'video',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,42 +36,37 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBPackage } from 'botlib';
|
|
||||||
|
|
||||||
import { IGBCoreService} from 'botlib';
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { WelcomeDialog } from './dialogs/WelcomeDialog';
|
import { WelcomeDialog } from './dialogs/WelcomeDialog';
|
||||||
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
|
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
|
||||||
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel';
|
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for core.gbapp.
|
||||||
|
*/
|
||||||
export class GBCorePackage implements IGBPackage {
|
export class GBCorePackage implements IGBPackage {
|
||||||
public static CurrentEngineName = 'guaribas-1.0.0';
|
public static CurrentEngineName = 'guaribas-1.0.0';
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
|
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
core.sequelize.addModels([
|
core.sequelize.addModels([GuaribasInstance, GuaribasPackage, GuaribasChannel, GuaribasException]);
|
||||||
GuaribasInstance,
|
|
||||||
GuaribasPackage,
|
|
||||||
GuaribasChannel,
|
|
||||||
GuaribasException
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
|
}
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
WelcomeDialog.setup(min.bot, min);
|
WelcomeDialog.setup(min.bot, min);
|
||||||
WhoAmIDialog.setup(min.bot, min);
|
WhoAmIDialog.setup(min.bot, min);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -31,46 +31,20 @@
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Logging support.
|
* @fileoverview General Bots server core.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { createLogger, format, transports } = require('winston');
|
'use strict';
|
||||||
|
|
||||||
const config = {
|
import {
|
||||||
levels: {
|
AutoIncrement,
|
||||||
error: 0,
|
BelongsTo,
|
||||||
debug: 1,
|
Column,
|
||||||
warn: 2,
|
CreatedAt,
|
||||||
data: 3,
|
ForeignKey,
|
||||||
info: 4,
|
Model,
|
||||||
verbose: 5,
|
PrimaryKey,
|
||||||
silly: 6,
|
Table,
|
||||||
custom: 7
|
UpdatedAt
|
||||||
},
|
} from 'sequelize-typescript';
|
||||||
colors: {
|
import { GuaribasInstance } from './GBModel';
|
||||||
error: 'red',
|
|
||||||
debug: 'blue',
|
|
||||||
warn: 'yellow',
|
|
||||||
data: 'grey',
|
|
||||||
info: 'green',
|
|
||||||
verbose: 'cyan',
|
|
||||||
silly: 'magenta',
|
|
||||||
custom: 'yellow'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const logger = createLogger({
|
|
||||||
format: format.combine(
|
|
||||||
format.colorize(),
|
|
||||||
format.simple(),
|
|
||||||
format.label({ label: 'GeneralBots' }),
|
|
||||||
format.timestamp(),
|
|
||||||
format.printf(nfo => {
|
|
||||||
return `${nfo.timestamp} [${nfo.label}] ${nfo.level}: ${nfo.message}`;
|
|
||||||
})
|
|
||||||
),
|
|
||||||
levels: config.levels,
|
|
||||||
transports: [new transports.Console()]
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = logger;
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -51,9 +51,13 @@ import {
|
||||||
|
|
||||||
import { IGBInstance } from 'botlib';
|
import { IGBInstance } from 'botlib';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base instance data for a bot.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasInstance extends Model<GuaribasInstance>
|
export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
implements IGBInstance {
|
implements IGBInstance {
|
||||||
|
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@AutoIncrement
|
@AutoIncrement
|
||||||
@Column
|
@Column
|
||||||
|
@ -80,8 +84,6 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
@Column
|
@Column
|
||||||
public enabledAdmin: boolean;
|
public enabledAdmin: boolean;
|
||||||
|
|
||||||
/* Services section on bot.json */
|
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
public engineName: string;
|
public engineName: string;
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
public speechKey: string;
|
public speechKey: string;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
public speechKeyEndpoint: string;
|
public speechEndpoint: string;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
public spellcheckerKey: string;
|
public spellcheckerKey: string;
|
||||||
|
@ -218,8 +220,6 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
@Column
|
@Column
|
||||||
public adminPass: string;
|
public adminPass: string;
|
||||||
|
|
||||||
/* Settings section of bot.json */
|
|
||||||
|
|
||||||
@Column(DataType.FLOAT)
|
@Column(DataType.FLOAT)
|
||||||
public nlpVsSearch: number;
|
public nlpVsSearch: number;
|
||||||
|
|
||||||
|
@ -238,6 +238,9 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each packaged listed for use in a bot instance.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasPackage extends Model<GuaribasPackage> {
|
export class GuaribasPackage extends Model<GuaribasPackage> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -264,6 +267,9 @@ export class GuaribasPackage extends Model<GuaribasPackage> {
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bot channel.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasChannel extends Model<GuaribasChannel> {
|
export class GuaribasChannel extends Model<GuaribasChannel> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -283,7 +289,11 @@ export class GuaribasChannel extends Model<GuaribasChannel> {
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception that has been thrown.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
|
//tslint:disable-next-line:max-classes-per-file
|
||||||
export class GuaribasException extends Model<GuaribasException> {
|
export class GuaribasException extends Model<GuaribasException> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@AutoIncrement
|
@AutoIncrement
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -34,23 +34,32 @@
|
||||||
|
|
||||||
import { TurnContext } from 'botbuilder';
|
import { TurnContext } from 'botbuilder';
|
||||||
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
import { GBLog, GBMinInstance } from 'botlib';
|
||||||
|
import * as request from 'request-promise-native';
|
||||||
|
import urlJoin = require('url-join');
|
||||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
|
import { GBDeployer } from './GBDeployer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview General Bots server core.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BASIC system class for extra manipulation of bot behaviour.
|
* BASIC system class for extra manipulation of bot behaviour.
|
||||||
*/
|
*/
|
||||||
class SysClass {
|
class SysClass {
|
||||||
public min: GBMinInstance;
|
public min: GBMinInstance;
|
||||||
|
private readonly deployer: GBDeployer;
|
||||||
|
|
||||||
constructor(min: GBMinInstance) {
|
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
|
this.deployer = deployer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async wait(seconds: number) {
|
public async wait(seconds: number) {
|
||||||
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
|
// tslint:disable-next-line no-string-based-set-timeout
|
||||||
|
const timeout = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
await timeout(seconds * 1000);
|
await timeout(seconds * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +68,16 @@ class SysClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createABotFarmUsing(
|
public async createABotFarmUsing(
|
||||||
botId,
|
botId: string,
|
||||||
username,
|
username: string,
|
||||||
password,
|
password: string,
|
||||||
location,
|
location: string,
|
||||||
nlpAuthoringKey,
|
nlpAuthoringKey: string,
|
||||||
appId,
|
appId: string,
|
||||||
appPassword,
|
appPassword: string,
|
||||||
subscriptionId
|
subscriptionId: string
|
||||||
) {
|
) {
|
||||||
const service = new AzureDeployerService(this.min.deployer);
|
const service = new AzureDeployerService(this.deployer);
|
||||||
await service.deployToCloud(
|
await service.deployToCloud(
|
||||||
botId,
|
botId,
|
||||||
username,
|
username,
|
||||||
|
@ -80,21 +89,42 @@ class SysClass {
|
||||||
subscriptionId
|
subscriptionId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @fileoverview General Bots server core.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class DialogClass {
|
/**
|
||||||
|
* Generic function to call any REST API.
|
||||||
|
*/
|
||||||
|
public async sendEmail(to, subject, body) {
|
||||||
|
// tslint:disable-next-line:no-console
|
||||||
|
GBLog.info(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to call any REST API.
|
||||||
|
*/
|
||||||
|
public async httpGet(url: string, qs) {
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
uri: urlJoin(url , qs)
|
||||||
|
};
|
||||||
|
|
||||||
|
return request.get(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base services of conversation to be called by BASIC.
|
||||||
|
*/
|
||||||
|
export class DialogClass {
|
||||||
|
|
||||||
public min: GBMinInstance;
|
public min: GBMinInstance;
|
||||||
public context: TurnContext;
|
public context: TurnContext;
|
||||||
public step: WaterfallStepContext;
|
public step: WaterfallStepContext;
|
||||||
public internalSys: SysClass;
|
public internalSys: SysClass;
|
||||||
|
|
||||||
constructor(min: GBMinInstance) {
|
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
this.internalSys = new SysClass(min);
|
this.internalSys = new SysClass(min, deployer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sys(): SysClass {
|
public sys(): SysClass {
|
||||||
|
@ -102,7 +132,7 @@ export default class DialogClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async hear(cb) {
|
public async hear(cb) {
|
||||||
const idCallback = Math.floor(Math.random() * 1000000000000);
|
const idCallback = crypto.getRandomValues(new Uint32Array(16))[0];
|
||||||
this.min.cbMap[idCallback] = cb;
|
this.min.cbMap[idCallback] = cb;
|
||||||
await this.step.beginDialog('/hear', { id: idCallback });
|
await this.step.beginDialog('/hear', { id: idCallback });
|
||||||
}
|
}
|
||||||
|
@ -110,17 +140,4 @@ export default class DialogClass {
|
||||||
public async talk(text: string) {
|
public async talk(text: string) {
|
||||||
return await this.context.sendActivity(text);
|
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) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -30,16 +30,29 @@
|
||||||
| |
|
| |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
'use strict';
|
||||||
import * as fs from 'fs';
|
|
||||||
|
import { GBLog } from 'botlib';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview General Bots server core.
|
* @fileoverview General Bots server core.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
/**
|
||||||
|
* Base configuration for the server like storage.
|
||||||
|
*/
|
||||||
export class GBConfigService {
|
export class GBConfigService {
|
||||||
|
public static getServerPort(): number {
|
||||||
|
if (process.env.port !== undefined) {
|
||||||
|
return Number(process.env.port);
|
||||||
|
}
|
||||||
|
if (process.env.PORT !== undefined) {
|
||||||
|
return Number(process.env.PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4242;
|
||||||
|
}
|
||||||
|
|
||||||
public static init(): any {
|
public static init(): any {
|
||||||
try {
|
try {
|
||||||
require('dotenv-extended').load({
|
require('dotenv-extended').load({
|
||||||
|
@ -49,7 +62,7 @@ export class GBConfigService {
|
||||||
overrideProcessEnv: true
|
overrideProcessEnv: true
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message);
|
GBLog.error(e.message);
|
||||||
process.exit(3);
|
process.exit(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +70,7 @@ export class GBConfigService {
|
||||||
public static get(key: string): string | undefined {
|
public static get(key: string): string | undefined {
|
||||||
let value = GBConfigService.tryGet(key);
|
let value = GBConfigService.tryGet(key);
|
||||||
|
|
||||||
if (!value) {
|
if (value === undefined) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'CLOUD_USERNAME':
|
case 'CLOUD_USERNAME':
|
||||||
value = undefined;
|
value = undefined;
|
||||||
|
@ -108,18 +121,20 @@ export class GBConfigService {
|
||||||
value = 'true';
|
value = 'true';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.warn(`Invalid key on .env file: '${key}'`);
|
GBLog.warn(`Invalid key on .env file: '${key}'`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static tryGet(key: string) {
|
public static tryGet(key: string): any {
|
||||||
let value = process.env['container:' + key];
|
let value = process.env[`container:${key}`];
|
||||||
if (!value) {
|
if (value === undefined) {
|
||||||
value = process.env[key];
|
value = process.env[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,13 +36,11 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
import { MessageFactory, RecognizerResult } from 'botbuilder';
|
||||||
import { MessageFactory } from 'botbuilder';
|
|
||||||
import { LuisRecognizer } from 'botbuilder-ai';
|
import { LuisRecognizer } from 'botbuilder-ai';
|
||||||
import { GBMinInstance, IGBConversationalService } from 'botlib';
|
import { GBDialogStep, GBLog, GBMinInstance, IGBConversationalService, IGBCoreService } from 'botlib';
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
import { AzureText } from 'pragmatismo-io-framework';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { GBCoreService } from './GBCoreService';
|
|
||||||
const Nexmo = require('nexmo');
|
const Nexmo = require('nexmo');
|
||||||
|
|
||||||
export interface LanguagePickerSettings {
|
export interface LanguagePickerSettings {
|
||||||
|
@ -50,18 +48,22 @@ export interface LanguagePickerSettings {
|
||||||
supportedLocales?: string[];
|
supportedLocales?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides basic services for handling messages and dispatching to back-end
|
||||||
|
* services like NLP or Search.
|
||||||
|
*/
|
||||||
export class GBConversationalService implements IGBConversationalService {
|
export class GBConversationalService implements IGBConversationalService {
|
||||||
public coreService: GBCoreService;
|
public coreService: IGBCoreService;
|
||||||
|
|
||||||
constructor(coreService: GBCoreService) {
|
constructor(coreService: IGBCoreService) {
|
||||||
this.coreService = coreService;
|
this.coreService = coreService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrentLanguage(step: any) {
|
public getCurrentLanguage(step: GBDialogStep) {
|
||||||
return step.context.activity.locale;
|
return step.context.activity.locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendEvent(step: any, name: string, value: any): Promise<any> {
|
public async sendEvent(step: GBDialogStep, name: string, value: Object): Promise<any> {
|
||||||
if (step.context.activity.channelId === 'webchat') {
|
if (step.context.activity.channelId === 'webchat') {
|
||||||
const msg = MessageFactory.text('');
|
const msg = MessageFactory.text('');
|
||||||
msg.value = value;
|
msg.value = value;
|
||||||
|
@ -72,6 +74,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tslint:disable:no-unsafe-any due to Nexmo.
|
||||||
public async sendSms(min: GBMinInstance, mobile: string, text: string): Promise<any> {
|
public async sendSms(min: GBMinInstance, mobile: string, text: string): Promise<any> {
|
||||||
return new Promise(
|
return new Promise(
|
||||||
(resolve: any, reject: any): any => {
|
(resolve: any, reject: any): any => {
|
||||||
|
@ -79,6 +82,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
apiKey: min.instance.smsKey,
|
apiKey: min.instance.smsKey,
|
||||||
apiSecret: min.instance.smsSecret
|
apiSecret: min.instance.smsSecret
|
||||||
});
|
});
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, (err, data) => {
|
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
@ -89,11 +93,12 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
|
|
||||||
public async routeNLP(step: any, min: GBMinInstance, text: string): Promise<boolean> {
|
public async routeNLP(step: GBDialogStep, min: GBMinInstance, text: string): Promise<boolean> {
|
||||||
// Invokes LUIS.
|
// Invokes LUIS.
|
||||||
|
|
||||||
let endpoint = min.instance.nlpEndpoint.replace('/luis/v2.0', '');
|
const endpoint = min.instance.nlpEndpoint.replace('/luis/v2.0', '');
|
||||||
|
|
||||||
const model = new LuisRecognizer({
|
const model = new LuisRecognizer({
|
||||||
applicationId: min.instance.nlpAppId,
|
applicationId: min.instance.nlpAppId,
|
||||||
|
@ -101,38 +106,42 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
endpoint: endpoint
|
endpoint: endpoint
|
||||||
});
|
});
|
||||||
|
|
||||||
let nlp: any;
|
let nlp: RecognizerResult;
|
||||||
try {
|
try {
|
||||||
nlp = await model.recognize(step.context);
|
nlp = await model.recognize(step.context);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
if (error.statusCode === 404) {
|
if (error.statusCode === 404) {
|
||||||
logger.warn('NLP application still not publish and there are no other options for answering.');
|
GBLog.warn('NLP application still not publish and there are no other options for answering.');
|
||||||
|
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
} else {
|
} else {
|
||||||
const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${
|
const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${
|
||||||
error.statusCode ? error.statusCode : ''
|
error.statusCode ? error.statusCode : ''
|
||||||
} ${error.message}`;
|
} {error.message; }`;
|
||||||
|
|
||||||
return Promise.reject(new Error(msg));
|
return Promise.reject(new Error(msg));
|
||||||
}
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves intents returned from LUIS.
|
// Resolves intents returned from LUIS.
|
||||||
|
|
||||||
const topIntent = LuisRecognizer.topIntent(nlp);
|
const topIntent = LuisRecognizer.topIntent(nlp);
|
||||||
if (topIntent) {
|
if (topIntent !== undefined) {
|
||||||
const intent = topIntent;
|
const intent = topIntent;
|
||||||
const entity = nlp.entities && nlp.entities.length > 0 ? nlp.entities[0].entity.toUpperCase() : null;
|
// tslint:disable:no-unsafe-any
|
||||||
|
const firstEntity = nlp.entities && nlp.entities.length > 0 ? nlp.entities[0].entity.toUpperCase() : undefined;
|
||||||
|
// tslint:ensable:no-unsafe-any
|
||||||
|
|
||||||
if (intent === 'None') {
|
if (intent === 'None') {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`NLP called: ${intent}, ${entity}`);
|
GBLog.info(`NLP called: ${intent} ${firstEntity}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await step.replaceDialog(`/${intent}`, nlp.entities);
|
await step.replaceDialog(` /${intent}`, nlp.entities);
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -145,7 +154,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkLanguage(step, min, text) {
|
public async checkLanguage(step: GBDialogStep, min, text) {
|
||||||
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, text);
|
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, text);
|
||||||
if (locale !== step.context.activity.locale.split('-')[0]) {
|
if (locale !== step.context.activity.locale.split('-')[0]) {
|
||||||
switch (locale) {
|
switch (locale) {
|
||||||
|
@ -158,7 +167,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
await step.context.sendActivity(Messages[locale].changing_language);
|
await step.context.sendActivity(Messages[locale].changing_language);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
await step.context.sendActivity(`Unknown language: ${locale}`);
|
await step.context.sendActivity(`; Unknown; language: $;{locale;}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,13 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
import { GBLog, IGBCoreService, IGBInstallationDeployer, IGBInstance, IGBPackage } from 'botlib';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { GBAdminPackage } from '../../admin.gbapp/index';
|
import { GBAdminPackage } from '../../admin.gbapp/index';
|
||||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog';
|
||||||
import { GBCorePackage } from '../../core.gbapp';
|
import { GBCorePackage } from '../../core.gbapp';
|
||||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||||
import { GBKBPackage } from '../../kb.gbapp';
|
import { GBKBPackage } from '../../kb.gbapp';
|
||||||
|
@ -50,10 +50,7 @@ import { GBSecurityPackage } from '../../security.gblib';
|
||||||
import { GBWhatsappPackage } from '../../whatsapp.gblib/index';
|
import { GBWhatsappPackage } from '../../whatsapp.gblib/index';
|
||||||
import { GuaribasInstance } from '../models/GBModel';
|
import { GuaribasInstance } from '../models/GBModel';
|
||||||
import { GBConfigService } from './GBConfigService';
|
import { GBConfigService } from './GBConfigService';
|
||||||
import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog';
|
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const opn = require('opn');
|
const opn = require('opn');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,11 +118,11 @@ export class GBCoreService implements IGBCoreService {
|
||||||
throw new Error(`Unknown dialect: ${this.dialect}.`);
|
throw new Error(`Unknown dialect: ${this.dialect}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const logging: any =
|
const logging: boolean | Function =
|
||||||
GBConfigService.get('STORAGE_LOGGING') === 'true'
|
GBConfigService.get('STORAGE_LOGGING') === 'true'
|
||||||
? (str: string): void => {
|
? (str: string): void => {
|
||||||
logger.info(str);
|
GBLog.info(str);
|
||||||
}
|
}
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
|
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
|
||||||
|
@ -140,9 +137,10 @@ export class GBCoreService implements IGBCoreService {
|
||||||
dialect: this.dialect,
|
dialect: this.dialect,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
dialectOptions: {
|
dialectOptions: {
|
||||||
encrypt: encrypt
|
options: {
|
||||||
},
|
encrypt: encrypt
|
||||||
pool: {
|
}
|
||||||
|
}, pool: {
|
||||||
max: 32,
|
max: 32,
|
||||||
min: 8,
|
min: 8,
|
||||||
idle: 40000,
|
idle: 40000,
|
||||||
|
@ -153,32 +151,36 @@ export class GBCoreService implements IGBCoreService {
|
||||||
|
|
||||||
if (this.dialect === 'mssql') {
|
if (this.dialect === 'mssql') {
|
||||||
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
|
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
this.createTableQuery = this.queryGenerator.createTableQuery;
|
this.createTableQuery = this.queryGenerator.createTableQuery;
|
||||||
this.queryGenerator.createTableQuery = (tableName, attributes, options) =>
|
this.queryGenerator.createTableQuery = (tableName, attributes, options) =>
|
||||||
this.createTableQueryOverride(tableName, attributes, options);
|
this.createTableQueryOverride(tableName, attributes, options);
|
||||||
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
|
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
|
||||||
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
|
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
|
||||||
this.changeColumnQueryOverride(tableName, attributes);
|
this.changeColumnQueryOverride(tableName, attributes);
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkStorage(azureDeployer: AzureDeployerService) {
|
public async checkStorage(installationDeployer: IGBInstallationDeployer) {
|
||||||
try {
|
try {
|
||||||
await this.sequelize.authenticate();
|
await this.sequelize.authenticate();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.info('Opening storage firewall on infrastructure...');
|
GBLog.info('Opening storage firewall on infrastructure...');
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
if (error.parent.code === 'ELOGIN') {
|
if (error.parent.code === 'ELOGIN') {
|
||||||
await this.openStorageFrontier(azureDeployer);
|
await this.openStorageFrontier(installationDeployer);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
// tslint:ensable:no-unsafe-any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async syncDatabaseStructure() {
|
public async syncDatabaseStructure() {
|
||||||
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
|
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
|
||||||
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
|
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
|
||||||
logger.info('Syncing database...');
|
GBLog.info('Syncing database...');
|
||||||
|
|
||||||
return this.sequelize.sync({
|
return this.sequelize.sync({
|
||||||
alter: alter,
|
alter: alter,
|
||||||
|
@ -186,21 +188,21 @@ export class GBCoreService implements IGBCoreService {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const msg = `Database synchronization is disabled.`;
|
const msg = `Database synchronization is disabled.`;
|
||||||
logger.info(msg);
|
GBLog.info(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all items to start several listeners.
|
* Loads all items to start several listeners.
|
||||||
*/
|
*/
|
||||||
public async loadInstances(): Promise<IGBInstance> {
|
public async loadInstances(): Promise<IGBInstance[]> {
|
||||||
return GuaribasInstance.findAll({});
|
return GuaribasInstance.findAll({});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads just one Bot instance by its internal Id.
|
* Loads just one Bot instance by its internal Id.
|
||||||
*/
|
*/
|
||||||
public async loadInstanceById(instanceId: string): Promise<IGBInstance> {
|
public async loadInstanceById(instanceId: number): Promise<IGBInstance> {
|
||||||
const options = { where: { instanceId: instanceId } };
|
const options = { where: { instanceId: instanceId } };
|
||||||
|
|
||||||
return GuaribasInstance.findOne(options);
|
return GuaribasInstance.findOne(options);
|
||||||
|
@ -240,12 +242,19 @@ STORAGE_SYNC=true
|
||||||
|
|
||||||
public async ensureProxy(port): Promise<string> {
|
public async ensureProxy(port): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const ngrok = require('ngrok');
|
if (fs.existsSync('node_modules/ngrok/bin/ngrok.exe')) {
|
||||||
return await ngrok.connect({ port: port });
|
const ngrok = require('ngrok');
|
||||||
|
|
||||||
|
return await ngrok.connect({ port: port });
|
||||||
|
} else {
|
||||||
|
GBLog.warn('ngrok executable not found (only tested on Windows). Check installation or node_modules folder.');
|
||||||
|
|
||||||
|
return 'localhost';
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// There are false positive from ngrok regarding to no memory, but it's just
|
// There are false positive from ngrok regarding to no memory, but it's just
|
||||||
// lack of connection.
|
// lack of connection.
|
||||||
logger.verbose(error);
|
GBLog.verbose(error);
|
||||||
throw new Error('Error connecting to remote ngrok server, please check network connection.');
|
throw new Error('Error connecting to remote ngrok server, please check network connection.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,34 +276,42 @@ STORAGE_SYNC=true
|
||||||
* @param azureDeployer
|
* @param azureDeployer
|
||||||
* @param proxyAddress
|
* @param proxyAddress
|
||||||
*/
|
*/
|
||||||
public async loadAllInstances(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
public async loadAllInstances(
|
||||||
logger.info(`Loading instances from storage...`);
|
core: IGBCoreService,
|
||||||
let instances: GuaribasInstance[];
|
installationDeployer: IGBInstallationDeployer,
|
||||||
|
proxyAddress: string
|
||||||
|
) {
|
||||||
|
GBLog.info(`Loading instances from storage...`);
|
||||||
|
let instances: IGBInstance[];
|
||||||
try {
|
try {
|
||||||
instances = await core.loadInstances();
|
instances = await core.loadInstances();
|
||||||
const instance = instances[0];
|
const instance = instances[0];
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
GBLog.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
||||||
await AzureDeployerService.updateBotProxy(
|
await installationDeployer.updateBotProxy(
|
||||||
instance.botId,
|
instance.botId,
|
||||||
instance.botId,
|
instance.botId,
|
||||||
`${proxyAddress}/api/messages/${instance.botId}`
|
`${proxyAddress}/api/messages/${instance.botId}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Check if storage is empty and needs formatting.
|
if (error.parent === undefined) {
|
||||||
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}.`);
|
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
|
||||||
|
} 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 {
|
||||||
|
GBLog.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}.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,9 +325,9 @@ STORAGE_SYNC=true
|
||||||
* @param bootInstance
|
* @param bootInstance
|
||||||
* @param core
|
* @param core
|
||||||
*/
|
*/
|
||||||
public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) {
|
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {
|
||||||
if (!instances) {
|
if (instances === undefined) {
|
||||||
const instance: IGBInstance = {};
|
const instance = new GuaribasInstance();
|
||||||
await instance.save();
|
await instance.save();
|
||||||
instances = await core.loadInstances();
|
instances = await core.loadInstances();
|
||||||
}
|
}
|
||||||
|
@ -329,10 +346,10 @@ STORAGE_SYNC=true
|
||||||
GBCorePackage,
|
GBCorePackage,
|
||||||
GBSecurityPackage,
|
GBSecurityPackage,
|
||||||
GBKBPackage,
|
GBKBPackage,
|
||||||
GBCustomerSatisfactionPackage,
|
GBCustomerSatisfactionPackage
|
||||||
GBWhatsappPackage
|
// GBWhatsappPackage
|
||||||
].forEach(e => {
|
].forEach(e => {
|
||||||
logger.info(`Loading sys package: ${e.name}...`);
|
GBLog.info(`Loading sys package: ${e.name}...`);
|
||||||
const p = Object.create(e.prototype) as IGBPackage;
|
const p = Object.create(e.prototype) as IGBPackage;
|
||||||
p.loadPackage(core, core.sequelize);
|
p.loadPackage(core, core.sequelize);
|
||||||
});
|
});
|
||||||
|
@ -347,18 +364,27 @@ STORAGE_SYNC=true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
public async createBootInstance(
|
||||||
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
core: GBCoreService,
|
||||||
|
installationDeployer: IGBInstallationDeployer,
|
||||||
|
proxyAddress: string
|
||||||
|
) {
|
||||||
|
GBLog.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
||||||
try {
|
try {
|
||||||
let { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance();
|
const { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance(installationDeployer);
|
||||||
instance = await azureDeployer.deployFarm(proxyAddress, instance, credentials, subscriptionId);
|
const changedInstance = await installationDeployer.deployFarm(
|
||||||
core.writeEnv(instance);
|
proxyAddress,
|
||||||
logger.info(`File .env written, starting General Bots...`);
|
instance,
|
||||||
|
credentials,
|
||||||
|
subscriptionId
|
||||||
|
);
|
||||||
|
core.writeEnv(changedInstance);
|
||||||
|
GBLog.info(`File .env written, starting General Bots...`);
|
||||||
GBConfigService.init();
|
GBConfigService.init();
|
||||||
|
|
||||||
return instance;
|
return changedInstance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(
|
GBLog.warn(
|
||||||
`In case of error, please cleanup any infrastructure objects
|
`In case of error, please cleanup any infrastructure objects
|
||||||
created during this procedure and .env before running again.`
|
created during this procedure and .env before running again.`
|
||||||
);
|
);
|
||||||
|
@ -391,13 +417,13 @@ STORAGE_SYNC=true
|
||||||
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 re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
|
||||||
const matches = re1.exec(sql);
|
const matches = re1.exec(sql);
|
||||||
if (matches) {
|
if (matches !== null) {
|
||||||
const table = matches[1];
|
const table = matches[1];
|
||||||
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
|
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
|
||||||
sql = sql.replace(
|
sql = sql.replace(
|
||||||
re2,
|
re2,
|
||||||
(match: string, ...args: any[]): string => {
|
(match: string, ...args: any[]): string => {
|
||||||
return 'CONSTRAINT [' + table + '_pk] ' + match;
|
return `CONSTRAINT [${table}_pk] ${match}`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
|
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
|
||||||
|
@ -407,15 +433,17 @@ STORAGE_SYNC=true
|
||||||
(match: string, ...args: any[]): string => {
|
(match: string, ...args: any[]): string => {
|
||||||
const fkcols = args[0];
|
const fkcols = args[0];
|
||||||
let fkname = table;
|
let fkname = table;
|
||||||
let matches = re4.exec(fkcols);
|
let matches2 = re4.exec(fkcols);
|
||||||
while (matches != undefined) {
|
while (matches2 !== null) {
|
||||||
fkname += '_' + matches[1];
|
fkname += `_${matches2[1]}`;
|
||||||
matches = re4.exec(fkcols);
|
matches2 = re4.exec(fkcols);
|
||||||
}
|
}
|
||||||
return 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')';
|
|
||||||
|
return `CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,7 +459,7 @@ STORAGE_SYNC=true
|
||||||
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 re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
|
||||||
const matches = re1.exec(sql);
|
const matches = re1.exec(sql);
|
||||||
if (matches) {
|
if (matches !== null) {
|
||||||
const table = matches[1];
|
const table = matches[1];
|
||||||
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
|
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
|
||||||
const re3 = /\[([^\]]*)\]/g;
|
const re3 = /\[([^\]]*)\]/g;
|
||||||
|
@ -440,15 +468,17 @@ STORAGE_SYNC=true
|
||||||
(match: string, ...args: any[]): string => {
|
(match: string, ...args: any[]): string => {
|
||||||
const fkcols = args[2];
|
const fkcols = args[2];
|
||||||
let fkname = table;
|
let fkname = table;
|
||||||
let matches = re3.exec(fkcols);
|
let matches2 = re3.exec(fkcols);
|
||||||
while (matches != undefined) {
|
while (matches2 !== null) {
|
||||||
fkname += '_' + matches[1];
|
fkname += `_${matches2[1]}`;
|
||||||
matches = re3.exec(fkcols);
|
matches2 = 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;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,9 +487,9 @@ STORAGE_SYNC=true
|
||||||
*
|
*
|
||||||
* @param azureDeployer Infrastructure Deployer instance.
|
* @param azureDeployer Infrastructure Deployer instance.
|
||||||
*/
|
*/
|
||||||
private async openStorageFrontier(deployer: AzureDeployerService) {
|
private async openStorageFrontier(installationDeployer: IGBInstallationDeployer) {
|
||||||
const group = GBConfigService.get('CLOUD_GROUP');
|
const group = GBConfigService.get('CLOUD_GROUP');
|
||||||
const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0];
|
const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0];
|
||||||
await AzureDeployerService.openStorageFirewall(group, serverName);
|
await installationDeployer.openStorageFirewall(group, serverName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,24 +36,20 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
const Fs = require('fs');
|
const Fs = require('fs');
|
||||||
const WaitUntil = require('wait-until');
|
const WaitUntil = require('wait-until');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
const graph = require('@microsoft/microsoft-graph-client');
|
const graph = require('@microsoft/microsoft-graph-client');
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
|
import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
||||||
import { GBError, IGBPackage } from 'botlib';
|
|
||||||
import { AzureSearch } from 'pragmatismo-io-framework';
|
import { AzureSearch } from 'pragmatismo-io-framework';
|
||||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
import { GuaribasPackage } from '../models/GBModel';
|
||||||
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel';
|
|
||||||
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService';
|
||||||
import { KBService } from './../../kb.gbapp/services/KBService';
|
import { KBService } from './../../kb.gbapp/services/KBService';
|
||||||
import { GBConfigService } from './GBConfigService';
|
import { GBConfigService } from './GBConfigService';
|
||||||
import { GBCoreService } from './GBCoreService';
|
|
||||||
import { GBImporter } from './GBImporterService';
|
import { GBImporter } from './GBImporterService';
|
||||||
import { GBVMService } from './GBVMService';
|
import { GBVMService } from './GBVMService';
|
||||||
|
|
||||||
|
@ -73,8 +69,8 @@ export class GBDeployer {
|
||||||
this.importer = importer;
|
this.importer = importer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getConnectionStringFromInstance(instance: GuaribasInstance) {
|
public static getConnectionStringFromInstance(instance: IGBInstance) {
|
||||||
return `Server=tcp:${instance.storageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${
|
return `Server=tcp:torageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${
|
||||||
instance.storageUsername
|
instance.storageUsername
|
||||||
};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
|
};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
|
||||||
}
|
}
|
||||||
|
@ -92,12 +88,12 @@ export class GBDeployer {
|
||||||
let totalPackages = 0;
|
let totalPackages = 0;
|
||||||
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
||||||
let paths = [GBDeployer.deployFolder];
|
let paths = [GBDeployer.deployFolder];
|
||||||
if (additionalPath) {
|
if (additionalPath !== undefined && additionalPath !== '') {
|
||||||
paths = paths.concat(additionalPath.toLowerCase().split(';'));
|
paths = paths.concat(additionalPath.toLowerCase().split(';'));
|
||||||
}
|
}
|
||||||
const botPackages = new Array<string>();
|
const botPackages: string[] = [];
|
||||||
const gbappPackages = new Array<string>();
|
const gbappPackages: string[] = [];
|
||||||
let generalPackages = new Array<string>();
|
let generalPackages: string[] = [];
|
||||||
|
|
||||||
function doIt(path) {
|
function doIt(path) {
|
||||||
const isDirectory = source => Fs.lstatSync(source).isDirectory();
|
const isDirectory = source => Fs.lstatSync(source).isDirectory();
|
||||||
|
@ -109,7 +105,7 @@ export class GBDeployer {
|
||||||
const dirs = getDirectories(path);
|
const dirs = getDirectories(path);
|
||||||
dirs.forEach(element => {
|
dirs.forEach(element => {
|
||||||
if (element.startsWith('.')) {
|
if (element.startsWith('.')) {
|
||||||
logger.info(`Ignoring ${element}...`);
|
GBLog.info(`Ignoring ${element}...`);
|
||||||
} else {
|
} else {
|
||||||
if (element.endsWith('.gbot')) {
|
if (element.endsWith('.gbot')) {
|
||||||
botPackages.push(element);
|
botPackages.push(element);
|
||||||
|
@ -122,9 +118,9 @@ export class GBDeployer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`);
|
GBLog.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`);
|
||||||
paths.forEach(e => {
|
paths.forEach(e => {
|
||||||
logger.info(`Looking in: ${e}...`);
|
GBLog.info(`Looking in: ${e}...`);
|
||||||
doIt(e);
|
doIt(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -136,11 +132,11 @@ export class GBDeployer {
|
||||||
.interval(1000)
|
.interval(1000)
|
||||||
.times(10)
|
.times(10)
|
||||||
.condition(cb => {
|
.condition(cb => {
|
||||||
logger.info(`Waiting for app package deployment...`);
|
GBLog.info(`Waiting for app package deployment...`);
|
||||||
cb(appPackagesProcessed === gbappPackages.length);
|
cb(appPackagesProcessed === gbappPackages.length);
|
||||||
})
|
})
|
||||||
.done(async result => {
|
.done(async () => {
|
||||||
logger.info(`App Package deployment done.`);
|
GBLog.info(`App Package deployment done.`);
|
||||||
|
|
||||||
({ generalPackages, totalPackages } = await this.deployDataPackages(
|
({ generalPackages, totalPackages } = await this.deployDataPackages(
|
||||||
core,
|
core,
|
||||||
|
@ -162,11 +158,9 @@ export class GBDeployer {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public async deployBot(localPath: string): Promise<IGBInstance> {
|
public async deployBot(localPath: string): Promise<IGBInstance> {
|
||||||
const packageType = Path.extname(localPath);
|
|
||||||
const packageName = Path.basename(localPath);
|
const packageName = Path.basename(localPath);
|
||||||
const instance = await this.importer.importIfNotExistsBotPackage(null, packageName, localPath);
|
|
||||||
|
|
||||||
return instance;
|
return await this.importer.importIfNotExistsBotPackage(undefined, packageName, localPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployPackageToStorage(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
public async deployPackageToStorage(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
||||||
|
@ -176,7 +170,7 @@ export class GBDeployer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployFromSharePoint(instanceId: number, path: string) {
|
public async deployFromSharePoint(instanceId: number) {
|
||||||
const adminService = new GBAdminService(this.core);
|
const adminService = new GBAdminService(this.core);
|
||||||
const accessToken = adminService.acquireElevatedToken(instanceId);
|
const accessToken = adminService.acquireElevatedToken(instanceId);
|
||||||
|
|
||||||
|
@ -184,57 +178,30 @@ export class GBDeployer {
|
||||||
|
|
||||||
const client = graph.Client.init({
|
const client = graph.Client.init({
|
||||||
authProvider: done => {
|
authProvider: done => {
|
||||||
done(null, accessToken);
|
done(undefined, accessToken);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const events = await client
|
|
||||||
.api('/me/events')
|
|
||||||
.select('subject,organizer,start,end')
|
|
||||||
.orderby('createdDateTime DESC')
|
|
||||||
.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public deployScriptToStorage(instanceId: number, localPath: string) {}
|
public async deployPackage(min: GBMinInstance, localPath: string) {
|
||||||
|
|
||||||
public deployTheme(localPath: string) {
|
|
||||||
// DISABLED: Until completed, "/ui/public".
|
|
||||||
// FsExtra.copy(localPath, this.workDir + packageName)
|
|
||||||
// .then(() => {
|
|
||||||
// })
|
|
||||||
// .catch(err => {
|
|
||||||
// var gberr = GBError.create(
|
|
||||||
// `GuaribasBusinessError: Error copying package: ${localPath}.`
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deployPackageFromSharePoint(min: GBMinInstance, path: string) {}
|
|
||||||
|
|
||||||
public async deployPackageFromLocalPath(min: GBMinInstance, localPath: string) {
|
|
||||||
const packageType = Path.extname(localPath);
|
const packageType = Path.extname(localPath);
|
||||||
|
|
||||||
switch (packageType) {
|
switch (packageType) {
|
||||||
case '.gbot':
|
case '.gbot':
|
||||||
return this.deployBot(localPath);
|
return this.deployBot(localPath);
|
||||||
|
|
||||||
case '.gbtheme':
|
|
||||||
return this.deployTheme(localPath);
|
|
||||||
|
|
||||||
// PACKAGE: Put in package logic.
|
|
||||||
case '.gbkb':
|
case '.gbkb':
|
||||||
const service = new KBService(this.core.sequelize);
|
const service = new KBService(this.core.sequelize);
|
||||||
return service.deployKb(this.core, this, localPath);
|
|
||||||
|
|
||||||
case '.gbui':
|
return service.deployKb(this.core, this, localPath);
|
||||||
break;
|
|
||||||
|
|
||||||
case '.gbdialog':
|
case '.gbdialog':
|
||||||
const vm = new GBVMService();
|
const vm = new GBVMService();
|
||||||
|
|
||||||
return vm.loadDialogPackage(localPath, min, this.core, this);
|
return vm.loadDialogPackage(localPath, min, this.core, this);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
const err = GBError.create(`Unhandled package type: ${packageType}.`);
|
||||||
Promise.reject(err);
|
Promise.reject(err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -247,16 +214,9 @@ export class GBDeployer {
|
||||||
const p = await this.getPackageByName(instance.instanceId, packageName);
|
const p = await this.getPackageByName(instance.instanceId, packageName);
|
||||||
|
|
||||||
switch (packageType) {
|
switch (packageType) {
|
||||||
case '.gbot':
|
|
||||||
// TODO: this.undeployBot(packageName, localPath)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '.gbtheme':
|
|
||||||
// TODO: this.undeployTheme(packageName, localPath)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '.gbkb':
|
case '.gbkb':
|
||||||
const service = new KBService(this.core.sequelize);
|
const service = new KBService(this.core.sequelize);
|
||||||
|
|
||||||
return service.undeployKbFromStorage(instance, this, p.packageId);
|
return service.undeployKbFromStorage(instance, this, p.packageId);
|
||||||
|
|
||||||
case '.gbui':
|
case '.gbui':
|
||||||
|
@ -266,13 +226,13 @@ export class GBDeployer {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
const err = GBError.create(`Unhandled package type: ${packageType}.`);
|
||||||
Promise.reject(err);
|
Promise.reject(err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async rebuildIndex(instance: GuaribasInstance) {
|
public async rebuildIndex(instance: IGBInstance, searchSchema: any) {
|
||||||
const search = new AzureSearch(
|
const search = new AzureSearch(
|
||||||
instance.searchKey,
|
instance.searchKey,
|
||||||
instance.searchHost,
|
instance.searchHost,
|
||||||
|
@ -286,7 +246,7 @@ export class GBDeployer {
|
||||||
try {
|
try {
|
||||||
await search.deleteDataSource(dsName);
|
await search.deleteDataSource(dsName);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code != 404) {
|
if (err.code !== 404) {
|
||||||
// First time, nothing to delete.
|
// First time, nothing to delete.
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -297,25 +257,26 @@ export class GBDeployer {
|
||||||
try {
|
try {
|
||||||
await search.deleteIndex();
|
await search.deleteIndex();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code != 404) {
|
if (err.code !== 404) {
|
||||||
// First time, nothing to delete.
|
// First time, nothing to delete.
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await search.createIndex(AzureDeployerService.getKBSearchSchema(instance.searchIndex), dsName);
|
await search.createIndex(searchSchema, dsName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPackageByName(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
public async getPackageByName(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
||||||
const where = { packageName: packageName, instanceId: instanceId };
|
const where = { packageName: packageName, instanceId: instanceId };
|
||||||
|
|
||||||
return GuaribasPackage.findOne({
|
return GuaribasPackage.findOne({
|
||||||
where: where
|
where: where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public installDefaultGBUI() {
|
public runOnce() {
|
||||||
const root = 'packages/default.gbui';
|
const root = 'packages/default.gbui';
|
||||||
if (!Fs.existsSync(`${root}/build`)) {
|
if (!Fs.existsSync(`${root}/build`)) {
|
||||||
logger.info(`Preparing default.gbui (it may take some additional time for the first time)...`);
|
GBLog.info(`Preparing default.gbui (it may take some additional time for the first time)...`);
|
||||||
Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true');
|
Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true');
|
||||||
child_process.execSync('npm install', { cwd: root });
|
child_process.execSync('npm install', { cwd: root });
|
||||||
child_process.execSync('npm run build', { cwd: root });
|
child_process.execSync('npm run build', { cwd: root });
|
||||||
|
@ -323,7 +284,7 @@ export class GBDeployer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deployDataPackages(
|
private async deployDataPackages(
|
||||||
core: GBCoreService,
|
core: IGBCoreService,
|
||||||
botPackages: string[],
|
botPackages: string[],
|
||||||
_this: this,
|
_this: this,
|
||||||
generalPackages: string[],
|
generalPackages: string[],
|
||||||
|
@ -342,9 +303,9 @@ export class GBDeployer {
|
||||||
|
|
||||||
botPackages.forEach(e => {
|
botPackages.forEach(e => {
|
||||||
if (e !== 'packages\\boot.gbot') {
|
if (e !== 'packages\\boot.gbot') {
|
||||||
logger.info(`Deploying bot: ${e}...`);
|
GBLog.info(`Deploying bot: ${e}...`);
|
||||||
_this.deployBot(e);
|
_this.deployBot(e);
|
||||||
logger.info(`Bot: ${e} deployed...`);
|
GBLog.info(`Bot: ${e} deployed...`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -353,18 +314,18 @@ export class GBDeployer {
|
||||||
generalPackages = generalPackages.filter(p => !p.endsWith('.git'));
|
generalPackages = generalPackages.filter(p => !p.endsWith('.git'));
|
||||||
generalPackages.forEach(filename => {
|
generalPackages.forEach(filename => {
|
||||||
const filenameOnly = Path.basename(filename);
|
const filenameOnly = Path.basename(filename);
|
||||||
logger.info(`Deploying package: ${filename}...`);
|
GBLog.info(`Deploying package: ${filename}...`);
|
||||||
|
|
||||||
// Handles apps for general bots - .gbapp must stay out of deploy folder.
|
// Handles apps for general bots - .gbapp must stay out of deploy folder.
|
||||||
|
|
||||||
if (Path.extname(filename) === '.gbapp' || Path.extname(filename) === '.gblib') {
|
if (Path.extname(filename) === '.gbapp' || Path.extname(filename) === '.gblib') {
|
||||||
// Themes for bots.
|
// Themes for bots.
|
||||||
} else if (Path.extname(filename) === '.gbtheme') {
|
} else if (Path.extname(filename) === '.gbtheme') {
|
||||||
server.use('/themes/' + filenameOnly, express.static(filename));
|
server.use(`/themes/${filenameOnly}`, express.static(filename));
|
||||||
logger.info(`Theme (.gbtheme) assets accessible at: ${'/themes/' + filenameOnly}.`);
|
GBLog.info(`Theme (.gbtheme) assets accessible at: /themes/${filenameOnly}.`);
|
||||||
} else if (Path.extname(filename) === '.gbkb') {
|
} else if (Path.extname(filename) === '.gbkb') {
|
||||||
server.use('/kb/' + filenameOnly + '/subjects', express.static(UrlJoin(filename, 'subjects')));
|
server.use(`/kb/${filenameOnly}/subjects`, express.static(urlJoin(filename, 'subjects')));
|
||||||
logger.info(`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`);
|
GBLog.info(`KB (.gbkb) assets accessible at: /kb/${filenameOnly}.`);
|
||||||
} else if (Path.extname(filename) === '.gbui') {
|
} else if (Path.extname(filename) === '.gbui') {
|
||||||
// Already Handled
|
// Already Handled
|
||||||
} else if (Path.extname(filename) === '.gbdialog') {
|
} else if (Path.extname(filename) === '.gbdialog') {
|
||||||
|
@ -381,14 +342,14 @@ export class GBDeployer {
|
||||||
.interval(100)
|
.interval(100)
|
||||||
.times(5)
|
.times(5)
|
||||||
.condition(cb => {
|
.condition(cb => {
|
||||||
logger.info(`Waiting for package deployment...`);
|
GBLog.info(`Waiting for package deployment...`);
|
||||||
cb(totalPackages === generalPackages.length);
|
cb(totalPackages === generalPackages.length);
|
||||||
})
|
})
|
||||||
.done(result => {
|
.done(() => {
|
||||||
if (botPackages.length === 0) {
|
if (botPackages.length === 0) {
|
||||||
logger.info('Use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder (no external packages).');
|
GBLog.info('Use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder (no external packages).');
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Package deployment done.`);
|
GBLog.info(`Package deployment done.`);
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -401,17 +362,17 @@ export class GBDeployer {
|
||||||
gbappPackages.forEach(e => {
|
gbappPackages.forEach(e => {
|
||||||
// Skips .gbapp inside deploy folder.
|
// Skips .gbapp inside deploy folder.
|
||||||
if (!e.startsWith('packages')) {
|
if (!e.startsWith('packages')) {
|
||||||
logger.info(`Deploying app: ${e}...`);
|
GBLog.info(`Deploying app: ${e}...`);
|
||||||
|
|
||||||
let folder = Path.join(e, 'node_modules');
|
let folder = Path.join(e, 'node_modules');
|
||||||
if (!Fs.existsSync(folder)) {
|
if (!Fs.existsSync(folder)) {
|
||||||
logger.info(`Installing modules for ${e}...`);
|
GBLog.info(`Installing modules for ${e}...`);
|
||||||
child_process.execSync('npm install', { cwd: e });
|
child_process.execSync('npm install', { cwd: e });
|
||||||
}
|
}
|
||||||
|
|
||||||
folder = Path.join(e, 'dist');
|
folder = Path.join(e, 'dist');
|
||||||
if (!Fs.existsSync()) {
|
if (!Fs.existsSync()) {
|
||||||
logger.info(`Compiling ${e}...`);
|
GBLog.info(`Compiling ${e}...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child_process.execSync(Path.join(e, 'node_modules/.bin/tsc'), { cwd: e });
|
child_process.execSync(Path.join(e, 'node_modules/.bin/tsc'), { cwd: e });
|
||||||
|
@ -420,15 +381,15 @@ export class GBDeployer {
|
||||||
const p = new m.Package();
|
const p = new m.Package();
|
||||||
p.loadPackage(core, core.sequelize);
|
p.loadPackage(core, core.sequelize);
|
||||||
appPackages.push(p);
|
appPackages.push(p);
|
||||||
logger.info(`App (.gbapp) deployed: ${e}.`);
|
GBLog.info(`App (.gbapp) deployed: ${e}.`);
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error(`Error deploying .gbapp package: ${e}\n${err}`);
|
GBLog.error(`Error deploying .gbapp package: ${e}\n${err}`);
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error compiling .gbapp package ${e}:\n${error.stdout.toString()}`);
|
GBLog.error(`Error compiling .gbapp package ${e}:\n${error.stdout.toString()}`);
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,6 +397,7 @@ export class GBDeployer {
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return appPackagesProcessed;
|
return appPackagesProcessed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,13 +36,14 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { IGBCoreService } from 'botlib';
|
||||||
import { IGBCoreService, IGBInstance } from 'botlib';
|
|
||||||
import fs = require('fs');
|
import fs = require('fs');
|
||||||
import path = require('path');
|
import urlJoin = require('url-join');
|
||||||
import { SecService } from '../../security.gblib/services/SecService';
|
|
||||||
import { GuaribasInstance } from '../models/GBModel';
|
import { GuaribasInstance } from '../models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the importing of packages.
|
||||||
|
*/
|
||||||
export class GBImporter {
|
export class GBImporter {
|
||||||
public core: IGBCoreService;
|
public core: IGBCoreService;
|
||||||
|
|
||||||
|
@ -51,25 +52,25 @@ export class GBImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importIfNotExistsBotPackage(botId: string, packageName: string, localPath: string) {
|
public async importIfNotExistsBotPackage(botId: string, packageName: string, localPath: string) {
|
||||||
const packageJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8'));
|
const packageJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
|
||||||
if (!botId) {
|
if (botId === undefined) {
|
||||||
botId = packageJson.botId;
|
botId = packageJson.botId;
|
||||||
}
|
}
|
||||||
const instance = await this.core.loadInstance(botId);
|
const instance = await this.core.loadInstance(botId);
|
||||||
if (instance) {
|
if (instance !== null) {
|
||||||
return instance;
|
return instance;
|
||||||
} else {
|
} else {
|
||||||
return await this.createInstanceInternal(botId, packageName, localPath, packageJson);
|
return await this.createInstanceInternal(botId, localPath, packageJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createInstanceInternal(botId: string, packageName: string, localPath: string, packageJson: any) {
|
private async createInstanceInternal(botId: string, localPath: string, packageJson: any) {
|
||||||
const settings = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'settings.json'), 'utf8'));
|
const settings = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
|
||||||
const servicesJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'services.json'), 'utf8'));
|
const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8'));
|
||||||
|
|
||||||
packageJson = { ...packageJson, ...settings, ...servicesJson };
|
packageJson = { ...packageJson, ...settings, ...servicesJson };
|
||||||
|
|
||||||
if (botId){
|
if (botId !== undefined) {
|
||||||
packageJson.botId = botId;
|
packageJson.botId = botId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,29 +37,40 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
|
const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const request = require('request-promise-native');
|
const request = require('request-promise-native');
|
||||||
const AuthenticationContext = require('adal-node').AuthenticationContext;
|
const AuthenticationContext = require('adal-node').AuthenticationContext;
|
||||||
|
|
||||||
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
||||||
|
|
||||||
import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs';
|
import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance, IGBAdminService, IGBConversationalService, IGBCoreService, IGBPackage } from 'botlib';
|
import {
|
||||||
|
GBDialogStep,
|
||||||
|
GBLog,
|
||||||
|
GBMinInstance,
|
||||||
|
IGBAdminService,
|
||||||
|
IGBConversationalService,
|
||||||
|
IGBCoreService,
|
||||||
|
IGBInstance,
|
||||||
|
IGBPackage
|
||||||
|
} from 'botlib';
|
||||||
|
|
||||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||||
import { GBCorePackage } from '../../core.gbapp';
|
import { GBCorePackage } from '../../core.gbapp';
|
||||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||||
import { GBKBPackage } from '../../kb.gbapp';
|
import { GBKBPackage } from '../../kb.gbapp';
|
||||||
|
import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog';
|
||||||
import { GBSecurityPackage } from '../../security.gblib';
|
import { GBSecurityPackage } from '../../security.gblib';
|
||||||
import { GBWhatsappPackage } from '../../whatsapp.gblib';
|
import { GBWhatsappPackage } from '../../whatsapp.gblib';
|
||||||
import { GuaribasInstance } from '../models/GBModel';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { GBAdminPackage } from './../../admin.gbapp/index';
|
import { GBAdminPackage } from './../../admin.gbapp/index';
|
||||||
import { GBDeployer } from './GBDeployer';
|
import { GBDeployer } from './GBDeployer';
|
||||||
|
|
||||||
/** Minimal service layer for a bot. */
|
/**
|
||||||
|
* Minimal service layer for a bot.
|
||||||
|
*/
|
||||||
export class GBMinService {
|
export class GBMinService {
|
||||||
public core: IGBCoreService;
|
public core: IGBCoreService;
|
||||||
public conversationalService: IGBConversationalService;
|
public conversationalService: IGBConversationalService;
|
||||||
|
@ -94,19 +105,19 @@ export class GBMinService {
|
||||||
*
|
*
|
||||||
* @return Loaded minimal bot instance.
|
* @return Loaded minimal bot instance.
|
||||||
*
|
*
|
||||||
* */
|
*/
|
||||||
|
|
||||||
public async buildMin(
|
public async buildMin(
|
||||||
bootInstance: GuaribasInstance,
|
bootInstance: IGBInstance,
|
||||||
server: any,
|
server: any,
|
||||||
appPackages: IGBPackage[],
|
appPackages: IGBPackage[],
|
||||||
instances: GuaribasInstance[],
|
instances: IGBInstance[],
|
||||||
deployer: GBDeployer
|
deployer: GBDeployer
|
||||||
): Promise<GBMinInstance> {
|
) {
|
||||||
// Serves default UI on root address '/'.
|
// Serves default UI on root address '/'.
|
||||||
|
|
||||||
const uiPackage = 'default.gbui';
|
const uiPackage = 'default.gbui';
|
||||||
server.use('/', express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
server.use('/', express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
instances.map(async instance => {
|
instances.map(async instance => {
|
||||||
|
@ -129,7 +140,7 @@ export class GBMinService {
|
||||||
|
|
||||||
// Install default VBA module.
|
// Install default VBA module.
|
||||||
|
|
||||||
deployer.deployPackageFromLocalPath(min, 'packages/default.gbdialog');
|
// DISABLED: deployer.deployPackage(min, 'packages/default.gbdialog');
|
||||||
|
|
||||||
// Call the loadBot context.activity for all packages.
|
// Call the loadBot context.activity for all packages.
|
||||||
|
|
||||||
|
@ -141,15 +152,14 @@ export class GBMinService {
|
||||||
server.post(url, async (req, res) => {
|
server.post(url, async (req, res) => {
|
||||||
await this.receiver(adapter, req, res, conversationState, min, instance, appPackages);
|
await this.receiver(adapter, req, res, conversationState, min, instance, appPackages);
|
||||||
});
|
});
|
||||||
logger.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
|
GBLog.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
|
||||||
|
|
||||||
// Serves individual URL for each bot user interface.
|
// Serves individual URL for each bot user interface.
|
||||||
|
|
||||||
const uiUrl = `/${instance.botId}`;
|
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}.`);
|
GBLog.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
|
||||||
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.
|
// 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
|
// There they will authenticate and give their consent to allow this app access to
|
||||||
|
@ -166,34 +176,34 @@ export class GBMinService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: GuaribasInstance) {
|
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) {
|
||||||
server.get(`/${min.instance.botId}/token`, async (req, res) => {
|
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(instance.instanceId, 'AntiCSRFAttackState');
|
||||||
if (req.query.state !== state) {
|
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);
|
GBLog.error(msg);
|
||||||
throw new Error(msg);
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
const authenticationContext = new AuthenticationContext(
|
const authenticationContext = new AuthenticationContext(
|
||||||
UrlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
urlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
||||||
);
|
);
|
||||||
const resource = 'https://graph.microsoft.com';
|
const resource = 'https://graph.microsoft.com';
|
||||||
authenticationContext.acquireTokenWithAuthorizationCode(
|
authenticationContext.acquireTokenWithAuthorizationCode(
|
||||||
req.query.code,
|
req.query.code,
|
||||||
UrlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
urlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
||||||
resource,
|
resource,
|
||||||
instance.authenticatorClientId,
|
instance.authenticatorClientId,
|
||||||
instance.authenticatorClientSecret,
|
instance.authenticatorClientSecret,
|
||||||
async (err, token) => {
|
async (err, token) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
const msg = `Error acquiring token: ${err}`;
|
const msg = `Error acquiring token: ${err}`;
|
||||||
logger.error(msg);
|
GBLog.error(msg);
|
||||||
res.send(msg);
|
res.send(msg);
|
||||||
} else {
|
} else {
|
||||||
await this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken);
|
this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken);
|
||||||
await this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken);
|
this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken);
|
||||||
await this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString());
|
this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString());
|
||||||
await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', null);
|
this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', undefined);
|
||||||
res.redirect(min.instance.botEndpoint);
|
res.redirect(min.instance.botEndpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,15 +212,15 @@ export class GBMinService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleOAuthRequests(server: any, min: GBMinInstance) {
|
private handleOAuthRequests(server: any, min: GBMinInstance) {
|
||||||
server.get(`/${min.instance.botId}/auth`, function(req, res) {
|
server.get(`/${min.instance.botId}/auth`, (req, res) => {
|
||||||
let authorizationUrl = UrlJoin(
|
let authorizationUrl = urlJoin(
|
||||||
min.instance.authenticatorAuthorityHostUrl,
|
min.instance.authenticatorAuthorityHostUrl,
|
||||||
min.instance.authenticatorTenant,
|
min.instance.authenticatorTenant,
|
||||||
'/oauth2/authorize'
|
'/oauth2/authorize'
|
||||||
);
|
);
|
||||||
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
|
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
|
||||||
min.instance.authenticatorClientId
|
min.instance.authenticatorClientId
|
||||||
}&redirect_uri=${UrlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
|
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
|
||||||
res.redirect(authorizationUrl);
|
res.redirect(authorizationUrl);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -218,16 +228,16 @@ export class GBMinService {
|
||||||
/**
|
/**
|
||||||
* Returns the instance object to clients requesting bot info.
|
* Returns the instance object to clients requesting bot info.
|
||||||
*/
|
*/
|
||||||
private async sendInstanceToClient(req, bootInstance: GuaribasInstance, res: any, webchatToken: any) {
|
private async sendInstanceToClient(req, bootInstance: IGBInstance, res: any, webchatToken: any) {
|
||||||
let botId = req.params.botId;
|
let botId = req.params.botId;
|
||||||
if (botId === '[default]') {
|
if (botId === '[default]') {
|
||||||
botId = bootInstance.botId;
|
botId = bootInstance.botId;
|
||||||
}
|
}
|
||||||
const instance = await this.core.loadInstance(botId);
|
const instance = await this.core.loadInstance(botId);
|
||||||
if (instance) {
|
if (instance !== undefined) {
|
||||||
const speechToken = await this.getSTSToken(instance);
|
const speechToken = await this.getSTSToken(instance);
|
||||||
let theme = instance.theme;
|
let theme = instance.theme;
|
||||||
if (!theme) {
|
if (theme !== undefined) {
|
||||||
theme = 'default.gbtheme';
|
theme = 'default.gbtheme';
|
||||||
}
|
}
|
||||||
res.send(
|
res.send(
|
||||||
|
@ -245,7 +255,7 @@ export class GBMinService {
|
||||||
} else {
|
} else {
|
||||||
const error = `Instance not found: ${botId}.`;
|
const error = `Instance not found: ${botId}.`;
|
||||||
res.sendStatus(error);
|
res.sendStatus(error);
|
||||||
logger.error(error);
|
GBLog.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,11 +276,13 @@ export class GBMinService {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const json = await request(options);
|
const json = await request(options);
|
||||||
|
|
||||||
return Promise.resolve(JSON.parse(json));
|
return Promise.resolve(JSON.parse(json));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `[botId:${
|
const msg = `[botId:${
|
||||||
instance.botId
|
instance.botId
|
||||||
}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
|
}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
|
||||||
|
|
||||||
return Promise.reject(new Error(msg));
|
return Promise.reject(new Error(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,7 +294,6 @@ export class GBMinService {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private async getSTSToken(instance: any) {
|
private async getSTSToken(instance: any) {
|
||||||
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
|
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
|
||||||
|
@ -296,6 +307,7 @@ export class GBMinService {
|
||||||
return await request(options);
|
return await request(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `Error calling Speech to Text client. Error is: ${error}.`;
|
const msg = `Error calling Speech to Text client. Error is: ${error}.`;
|
||||||
|
|
||||||
return Promise.reject(new Error(msg));
|
return Promise.reject(new Error(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +346,7 @@ export class GBMinService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private invokeLoadBot(appPackages: any[], min: GBMinInstance, server: any) {
|
private invokeLoadBot(appPackages: any[], min: GBMinInstance, server: any) {
|
||||||
const sysPackages = new Array<IGBPackage>();
|
const sysPackages : IGBPackage[] = [];
|
||||||
// NOTE: A semicolon is necessary before this line.
|
// NOTE: A semicolon is necessary before this line.
|
||||||
[
|
[
|
||||||
GBCorePackage,
|
GBCorePackage,
|
||||||
|
@ -342,8 +354,8 @@ export class GBMinService {
|
||||||
GBAdminPackage,
|
GBAdminPackage,
|
||||||
GBKBPackage,
|
GBKBPackage,
|
||||||
GBAnalyticsPackage,
|
GBAnalyticsPackage,
|
||||||
GBCustomerSatisfactionPackage,
|
GBCustomerSatisfactionPackage
|
||||||
GBWhatsappPackage
|
// DISABLED: GBWhatsappPackage
|
||||||
].forEach(sysPackage => {
|
].forEach(sysPackage => {
|
||||||
const p = Object.create(sysPackage.prototype) as IGBPackage;
|
const p = Object.create(sysPackage.prototype) as IGBPackage;
|
||||||
p.loadBot(min);
|
p.loadBot(min);
|
||||||
|
@ -351,21 +363,21 @@ export class GBMinService {
|
||||||
if (sysPackage.name === 'GBWhatsappPackage') {
|
if (sysPackage.name === 'GBWhatsappPackage') {
|
||||||
const url = '/instances/:botId/whatsapp';
|
const url = '/instances/:botId/whatsapp';
|
||||||
server.post(url, (req, res) => {
|
server.post(url, (req, res) => {
|
||||||
p.channel.received(req, res);
|
(p as any).channel.received(req, res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
appPackages.forEach(p => {
|
appPackages.forEach(p => {
|
||||||
p.sysPackages = sysPackages;
|
p.sysPackages = sysPackages;
|
||||||
p.loadBot(min);
|
p.loadBot(min);
|
||||||
if (p.getDialogs !== undefined) {
|
if (p.getDialogs !== undefined) {
|
||||||
let dialogs = p.getDialogs(min);
|
const dialogs = p.getDialogs(min);
|
||||||
dialogs.forEach(dialog => {
|
dialogs.forEach(dialog => {
|
||||||
min.dialogs.add(new WaterfallDialog(dialog.name, dialog.waterfall));
|
min.dialogs.add(new WaterfallDialog(dialog.name, dialog.waterfall));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -382,9 +394,8 @@ export class GBMinService {
|
||||||
) {
|
) {
|
||||||
await adapter.processActivity(req, res, async context => {
|
await adapter.processActivity(req, res, async context => {
|
||||||
// Get loaded user state
|
// Get loaded user state
|
||||||
const state = await conversationState.get(context);
|
const step = await min.dialogs.createContext(context);
|
||||||
const step = await min.dialogs.createContext(context, state);
|
step.context.activity.locale = 'en-US';
|
||||||
step.context.activity.locale = 'en-US'; // TODO: Make dynamic.
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await min.userProfile.get(context, {});
|
const user = await min.userProfile.get(context, {});
|
||||||
|
@ -398,11 +409,11 @@ export class GBMinService {
|
||||||
});
|
});
|
||||||
user.loaded = true;
|
user.loaded = true;
|
||||||
user.subjects = [];
|
user.subjects = [];
|
||||||
user.cb = null;
|
user.cb = undefined;
|
||||||
await min.userProfile.set(step.context, user);
|
await min.userProfile.set(step.context, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
GBLog.info(
|
||||||
`User>: ${context.activity.text} (${context.activity.type}, ${context.activity.name}, ${
|
`User>: ${context.activity.text} (${context.activity.type}, ${context.activity.name}, ${
|
||||||
context.activity.channelId
|
context.activity.channelId
|
||||||
}, {context.activity.value})`
|
}, {context.activity.value})`
|
||||||
|
@ -410,7 +421,7 @@ export class GBMinService {
|
||||||
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];
|
const member = context.activity.membersAdded[0];
|
||||||
if (member.name === 'GeneralBots') {
|
if (member.name === 'GeneralBots') {
|
||||||
logger.info(`Bot added to conversation, starting chat...`);
|
GBLog.info(`Bot added to conversation, starting chat...`);
|
||||||
appPackages.forEach(e => {
|
appPackages.forEach(e => {
|
||||||
e.onNewSession(min, step);
|
e.onNewSession(min, step);
|
||||||
});
|
});
|
||||||
|
@ -418,7 +429,7 @@ export class GBMinService {
|
||||||
|
|
||||||
await step.beginDialog('/');
|
await step.beginDialog('/');
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Member added to conversation: ${member.name}`);
|
GBLog.info(`Member added to conversation: ${member.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes messages.
|
// Processes messages.
|
||||||
|
@ -430,14 +441,12 @@ export class GBMinService {
|
||||||
} else if (context.activity.type === 'event') {
|
} else if (context.activity.type === 'event') {
|
||||||
// Empties dialog stack before going to the target.
|
// Empties dialog stack before going to the target.
|
||||||
|
|
||||||
// TODO: Understand MSFT changes: await step.endAll();
|
|
||||||
|
|
||||||
await this.processEventActivity(context, step);
|
await this.processEventActivity(context, step);
|
||||||
}
|
}
|
||||||
await conversationState.saveChanges(context, true);
|
await conversationState.saveChanges(context, true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`;
|
const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`;
|
||||||
logger.error(msg);
|
GBLog.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 });
|
await step.beginDialog('/ask', { isReturning: true });
|
||||||
|
@ -445,11 +454,11 @@ export class GBMinService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processEventActivity(context, step: any) {
|
private async processEventActivity(context, step: GBDialogStep) {
|
||||||
if (context.activity.name === 'whoAmI') {
|
if (context.activity.name === 'whoAmI') {
|
||||||
await step.beginDialog('/whoAmI');
|
await step.beginDialog('/whoAmI');
|
||||||
} else if (context.activity.name === 'showSubjects') {
|
} else if (context.activity.name === 'showSubjects') {
|
||||||
await step.beginDialog('/menu');
|
await step.beginDialog('/menu', undefined);
|
||||||
} else if (context.activity.name === 'giveFeedback') {
|
} else if (context.activity.name === 'giveFeedback') {
|
||||||
await step.beginDialog('/feedback', {
|
await step.beginDialog('/feedback', {
|
||||||
fromMenu: true
|
fromMenu: true
|
||||||
|
@ -457,7 +466,7 @@ export class GBMinService {
|
||||||
} else if (context.activity.name === 'showFAQ') {
|
} else if (context.activity.name === 'showFAQ') {
|
||||||
await step.beginDialog('/faq');
|
await step.beginDialog('/faq');
|
||||||
} else if (context.activity.name === 'answerEvent') {
|
} else if (context.activity.name === 'answerEvent') {
|
||||||
await step.beginDialog('/answerEvent', {
|
await step.beginDialog('/answerEvent', <AskDialogArgs>{
|
||||||
questionId: context.activity.data,
|
questionId: context.activity.data,
|
||||||
fromFaq: true
|
fromFaq: true
|
||||||
});
|
});
|
||||||
|
@ -473,30 +482,28 @@ export class GBMinService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processMessageActivity(context, min: GBMinInstance, step: any) {
|
private async processMessageActivity(context, min: GBMinInstance, step: GBDialogStep) {
|
||||||
// Direct script invoking by itent name.
|
// Direct script invoking by itent name.
|
||||||
|
|
||||||
let isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
|
const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
|
||||||
|
|
||||||
if (isVMCall) {
|
if (isVMCall) {
|
||||||
let mainMethod = context.activity.text;
|
const mainMethod = context.activity.text;
|
||||||
|
|
||||||
min.sandbox.context = context;
|
min.sandBoxMap[mainMethod].context = context;
|
||||||
min.sandbox.step = step;
|
min.sandBoxMap[mainMethod].step = step;
|
||||||
min.sandbox[mainMethod].bind(min.sandbox);
|
min.sandBoxMap[mainMethod][mainMethod].bind(min.sandBoxMap[mainMethod]);
|
||||||
await min.sandbox[mainMethod]();
|
await min.sandBoxMap[mainMethod][mainMethod]();
|
||||||
} else if (context.activity.text === 'admin') {
|
} else if (context.activity.text === 'admin') {
|
||||||
await step.beginDialog('/admin');
|
await step.beginDialog('/admin');
|
||||||
|
|
||||||
// Checks for /menu JSON signature.
|
// Checks for /menu JSON signature.
|
||||||
} else if (context.activity.text.startsWith('{"title"')) {
|
} else if (context.activity.text.startsWith('{"title"')) {
|
||||||
await step.beginDialog('/menu', {
|
await step.beginDialog('/menu', JSON.parse(context.activity.text));
|
||||||
data: JSON.parse(context.activity.text)
|
|
||||||
});
|
|
||||||
// Otherwise, continue to the active dialog in the stack.
|
// Otherwise, continue to the active dialog in the stack.
|
||||||
} else {
|
} else {
|
||||||
const user = await min.userProfile.get(context, {});
|
const user = await min.userProfile.get(context, {});
|
||||||
if (step.activeDialog) {
|
if (step.activeDialog !== undefined) {
|
||||||
await step.continueDialog();
|
await step.continueDialog();
|
||||||
} else {
|
} else {
|
||||||
await step.beginDialog('/answer', {
|
await step.beginDialog('/answer', {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -33,18 +33,19 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance, IGBCoreService } from 'botlib';
|
import { GBLog, GBMinInstance, GBService, IGBCoreService } from 'botlib';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { GBDeployer } from './GBDeployer';
|
import { GBDeployer } from './GBDeployer';
|
||||||
import { TSCompiler } from './TSCompiler';
|
import { TSCompiler } from './TSCompiler';
|
||||||
import DialogClass from './GBAPIService';
|
|
||||||
|
|
||||||
const walkPromise = require('walk-promise');
|
const walkPromise = require('walk-promise');
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
|
import { DialogClass } from './GBAPIService';
|
||||||
|
//tslint:disable-next-line:no-submodule-imports
|
||||||
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
||||||
var beautify = require('js-beautify').js;
|
const beautify = require('js-beautify').js;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Virtualization services for emulation of BASIC.
|
* @fileoverview Virtualization services for emulation of BASIC.
|
||||||
|
@ -55,11 +56,13 @@ var beautify = require('js-beautify').js;
|
||||||
* translation and enhance classic BASIC experience.
|
* translation and enhance classic BASIC experience.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class GBVMService implements IGBCoreService {
|
/**
|
||||||
|
* Basic services for BASIC manipulation.
|
||||||
|
*/
|
||||||
|
export class GBVMService extends GBService {
|
||||||
private readonly script = new vm.Script();
|
private readonly script = new vm.Script();
|
||||||
|
|
||||||
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
||||||
|
|
||||||
const files = await walkPromise(folder);
|
const files = await walkPromise(folder);
|
||||||
this.addHearDialog(min);
|
this.addHearDialog(min);
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
const mainName = file.name.replace(/\-|\./g, '');
|
const mainName = file.name.replace(/\-|\./g, '');
|
||||||
min.scriptMap[file.name] = mainName;
|
min.scriptMap[file.name] = mainName;
|
||||||
|
|
||||||
const filename = UrlJoin(folder, file.name);
|
const filename = urlJoin(folder, file.name);
|
||||||
fs.watchFile(filename, async () => {
|
fs.watchFile(filename, async () => {
|
||||||
await this.run(filename, min, deployer, mainName);
|
await this.run(filename, min, deployer, mainName);
|
||||||
});
|
});
|
||||||
|
@ -110,6 +113,10 @@ export class GBVMService implements IGBCoreService {
|
||||||
return 'let password = sys().generatePassword()';
|
return 'let password = sys().generatePassword()';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
code = code.replace(/(get)(\s)(.*)/g, ($0, $1, $2) => {
|
||||||
|
return `sys().httpGet (${$2})`;
|
||||||
|
});
|
||||||
|
|
||||||
code = code.replace(/(create a bot farm using)(\s)(.*)/g, ($0, $1, $2, $3) => {
|
code = code.replace(/(create a bot farm using)(\s)(.*)/g, ($0, $1, $2, $3) => {
|
||||||
return `sys().createABotFarmUsing (${$3})`;
|
return `sys().createABotFarmUsing (${$3})`;
|
||||||
});
|
});
|
||||||
|
@ -127,7 +134,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
// Converts General Bots BASIC into regular VBS
|
// Converts General Bots BASIC into regular VBS
|
||||||
|
|
||||||
const basicCode: string = fs.readFileSync(filename, 'utf8');
|
const basicCode: string = fs.readFileSync(filename, 'utf8');
|
||||||
const vbsCode = await this.convertGBASICToVBS(basicCode);
|
const vbsCode = this.convertGBASICToVBS(basicCode);
|
||||||
const vbsFile = `${filename}.compiled`;
|
const vbsFile = `${filename}.compiled`;
|
||||||
fs.writeFileSync(vbsFile, vbsCode, 'utf8');
|
fs.writeFileSync(vbsFile, vbsCode, 'utf8');
|
||||||
|
|
||||||
|
@ -156,9 +163,9 @@ export class GBVMService implements IGBCoreService {
|
||||||
let parsedCode = code;
|
let parsedCode = code;
|
||||||
const hearExp = /(\w+).*hear.*\(\)/;
|
const hearExp = /(\w+).*hear.*\(\)/;
|
||||||
|
|
||||||
let match1;
|
let match1 = hearExp.exec(code);
|
||||||
|
|
||||||
while ((match1 = hearExp.exec(code))) {
|
while (match1 !== undefined) {
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
// Writes async body.
|
// Writes async body.
|
||||||
|
@ -178,8 +185,9 @@ export class GBVMService implements IGBCoreService {
|
||||||
|
|
||||||
let right = 0;
|
let right = 0;
|
||||||
let left = 1;
|
let left = 1;
|
||||||
let match2;
|
let match2 = /\{|\}/.exec(tempCode);
|
||||||
while ((match2 = /\{|\}/.exec(tempCode))) {
|
|
||||||
|
while (match2 !== undefined) {
|
||||||
const c = tempCode.substring(match2.index, match2.index + 1);
|
const c = tempCode.substring(match2.index, match2.index + 1);
|
||||||
|
|
||||||
if (c === '}') {
|
if (c === '}') {
|
||||||
|
@ -194,6 +202,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
if (left === right) {
|
if (left === right) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
match1 = hearExp.exec(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length);
|
parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length);
|
||||||
|
@ -203,19 +212,19 @@ export class GBVMService implements IGBCoreService {
|
||||||
// A interaction will be made for each hear.
|
// A interaction will be made for each hear.
|
||||||
|
|
||||||
code = parsedCode;
|
code = parsedCode;
|
||||||
|
match2 = /\{|\}/.exec(tempCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedCode = this.handleThisAndAwait(parsedCode);
|
parsedCode = this.handleThisAndAwait(parsedCode);
|
||||||
|
|
||||||
parsedCode = beautify(parsedCode, { indent_size: 2, space_in_empty_paren: true })
|
parsedCode = beautify(parsedCode, { indent_size: 2, space_in_empty_paren: true });
|
||||||
fs.writeFileSync(jsfile, parsedCode);
|
fs.writeFileSync(jsfile, parsedCode);
|
||||||
|
|
||||||
const sandbox: DialogClass = new DialogClass(min);
|
const sandbox: DialogClass = new DialogClass(min, deployer);
|
||||||
const context = vm.createContext(sandbox);
|
const context = vm.createContext(sandbox);
|
||||||
vm.runInContext(parsedCode, context);
|
vm.runInContext(parsedCode, context);
|
||||||
min.sandbox = sandbox;
|
min.sandBoxMap[mainName] = sandbox;
|
||||||
await deployer.deployScriptToStorage(min.instanceId, filename);
|
GBLog.info(`[GBVMService] Finished loading of ${filename}`);
|
||||||
logger.info(`[GBVMService] Finished loading of ${filename}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,13 +233,13 @@ export class GBVMService implements IGBCoreService {
|
||||||
|
|
||||||
code = code.replace(/sys\(\)/g, 'this.sys()');
|
code = code.replace(/sys\(\)/g, 'this.sys()');
|
||||||
code = code.replace(/("[^"]*"|'[^']*')|\btalk\b/g, ($0, $1) => {
|
code = code.replace(/("[^"]*"|'[^']*')|\btalk\b/g, ($0, $1) => {
|
||||||
return $1 == undefined ? 'this.talk' : $1;
|
return $1 === undefined ? 'this.talk' : $1;
|
||||||
});
|
});
|
||||||
code = code.replace(/("[^"]*"|'[^']*')|\bhear\b/g, ($0, $1) => {
|
code = code.replace(/("[^"]*"|'[^']*')|\bhear\b/g, ($0, $1) => {
|
||||||
return $1 == undefined ? 'this.hear' : $1;
|
return $1 === undefined ? 'this.hear' : $1;
|
||||||
});
|
});
|
||||||
code = code.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, ($0, $1) => {
|
code = code.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, ($0, $1) => {
|
||||||
return $1 == undefined ? 'this.sendEmail' : $1;
|
return $1 === undefined ? 'this.sendEmail' : $1;
|
||||||
});
|
});
|
||||||
|
|
||||||
// await insertion.
|
// await insertion.
|
||||||
|
@ -245,7 +254,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
min.dialogs.add(
|
min.dialogs.add(
|
||||||
new WaterfallDialog('/hear', [
|
new WaterfallDialog('/hear', [
|
||||||
async step => {
|
async step => {
|
||||||
step.activeDialog.state.cbId = step.options['id'];
|
step.activeDialog.state.cbId = (step.options as any).id;
|
||||||
|
|
||||||
return await step.prompt('textPrompt', {});
|
return await step.prompt('textPrompt', {});
|
||||||
},
|
},
|
||||||
|
@ -255,7 +264,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
|
|
||||||
const cbId = step.activeDialog.state.cbId;
|
const cbId = step.activeDialog.state.cbId;
|
||||||
const cb = min.cbMap[cbId];
|
const cb = min.cbMap[cbId];
|
||||||
cb.bind({ step: step, context: step.context }); // TODO: Necessary or min.sandbox?
|
cb.bind({ step: step, context: step.context });
|
||||||
|
|
||||||
await step.endDialog();
|
await step.endDialog();
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,12 +36,14 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import { GBLog } from 'botlib';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a TypeScript compiler.
|
||||||
|
*/
|
||||||
export class TSCompiler {
|
export class TSCompiler {
|
||||||
|
|
||||||
|
|
||||||
private static shouldIgnoreError(diagnostic) {
|
private static shouldIgnoreError(diagnostic) {
|
||||||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||||
|
|
||||||
|
@ -76,11 +78,11 @@ export class TSCompiler {
|
||||||
if (!TSCompiler.shouldIgnoreError(diagnostic)) {
|
if (!TSCompiler.shouldIgnoreError(diagnostic)) {
|
||||||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||||
|
|
||||||
if (diagnostic.file) {
|
if (diagnostic.file !== undefined) {
|
||||||
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||||
logger.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
GBLog.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
||||||
} else {
|
} else {
|
||||||
logger.error(`${message}`);
|
GBLog.error(`${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import 'mocha';
|
import { GBImporter } from '../services/GBImporterService';
|
||||||
import {GBImporter} from '../services/GBImporterService';
|
|
||||||
|
|
||||||
describe('Hello function', () => {
|
describe('Hello function', () => {
|
||||||
|
|
||||||
it('should return empty test', () => {
|
it('should return empty test', () => {
|
||||||
const service = new GBImporter(null);
|
const service = new GBImporter(undefined);
|
||||||
//service.importIfNotExistsBotPackage(null, null);
|
|
||||||
const result = 0;
|
const result = 0;
|
||||||
expect(result).to.equal(0);
|
expect(result).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -42,9 +42,7 @@ import { GBVMService } from '../services/GBVMService';
|
||||||
describe('Load function', () => {
|
describe('Load function', () => {
|
||||||
it('should fail on invalid file', () => {
|
it('should fail on invalid file', () => {
|
||||||
try {
|
try {
|
||||||
// const service = new GBVMService();
|
const service = new GBVMService();
|
||||||
// TODO: service.loadJS('invalid.file', null, null, null, null);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error).to.equal(0);
|
expect(error).to.equal(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,12 +38,14 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { IGBDialog } from 'botlib';
|
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
import { AzureText } from 'pragmatismo-io-framework';
|
||||||
import { CSService } from '../services/CSService';
|
import { CSService } from '../services/CSService';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for feedback collecting.
|
||||||
|
*/
|
||||||
export class FeedbackDialog extends IGBDialog {
|
export class FeedbackDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -58,13 +60,7 @@ export class FeedbackDialog extends IGBDialog {
|
||||||
new WaterfallDialog('/feedbackNumber', [
|
new WaterfallDialog('/feedbackNumber', [
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
// TODO: Migrate to 4.*+ await step.prompt("choicePrompt", Messages[locale].what_about_me, [
|
|
||||||
// "1",
|
|
||||||
// "2",
|
|
||||||
// "3",
|
|
||||||
// "4",
|
|
||||||
// "5"
|
|
||||||
// ]);
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
|
@ -73,6 +69,7 @@ export class FeedbackDialog extends IGBDialog {
|
||||||
const user = await min.userProfile.get(context, {});
|
const user = await min.userProfile.get(context, {});
|
||||||
await service.updateConversationRate(user.conversation, rate);
|
await service.updateConversationRate(user.conversation, rate);
|
||||||
await step.context.sendActivity(Messages[locale].thanks);
|
await step.context.sendActivity(Messages[locale].thanks);
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
@ -84,29 +81,26 @@ export class FeedbackDialog extends IGBDialog {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].about_suggestions);
|
await step.context.sendActivity(Messages[locale].about_suggestions);
|
||||||
step.activeDialog.state.cbId = step.options['id'];
|
step.activeDialog.state.cbId = (step.options as any).id;
|
||||||
|
|
||||||
return await step.prompt('textPrompt', Messages[locale].what_about_service);
|
return await step.prompt('textPrompt', Messages[locale].what_about_service);
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const rate = await AzureText.getSentiment(
|
const rate = await AzureText.getSentiment(
|
||||||
min.instance.textAnalyticsKey,
|
min.instance.textAnalyticsKey,
|
||||||
min.instance.textAnalyticsEndpoint,
|
min.instance.textAnalyticsEndpoint,
|
||||||
min.conversationalService.getCurrentLanguage(step),
|
min.conversationalService.getCurrentLanguage(step),
|
||||||
step.result
|
step.result
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rate > 0.5) {
|
if (rate > 0.5) {
|
||||||
await step.context.sendActivity(Messages[locale].glad_you_liked);
|
await step.context.sendActivity(Messages[locale].glad_you_liked);
|
||||||
} else {
|
} else {
|
||||||
await step.context.sendActivity(Messages[locale].we_will_improve);
|
await step.context.sendActivity(Messages[locale].we_will_improve);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Record.
|
|
||||||
}
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,15 +36,16 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { CSService } from '../services/CSService';
|
import { CSService } from '../services/CSService';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for collecting quality of answer.
|
||||||
|
*/
|
||||||
export class QualityDialog extends IGBDialog {
|
export class QualityDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -63,11 +64,11 @@ export class QualityDialog extends IGBDialog {
|
||||||
const score = step.result;
|
const score = step.result;
|
||||||
|
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => min.conversationalService.sendEvent(step, 'stop', null),
|
() => min.conversationalService.sendEvent(step, 'stop', undefined),
|
||||||
400
|
400
|
||||||
);
|
);
|
||||||
|
|
||||||
if (score == 0) {
|
if (score === 0) {
|
||||||
await step.context.sendActivity(Messages[locale].im_sorry_lets_try);
|
await step.context.sendActivity(Messages[locale].im_sorry_lets_try);
|
||||||
} else {
|
} else {
|
||||||
await step.context.sendActivity(Messages[locale].great_thanks);
|
await step.context.sendActivity(Messages[locale].great_thanks);
|
||||||
|
@ -79,6 +80,7 @@ export class QualityDialog extends IGBDialog {
|
||||||
);
|
);
|
||||||
await step.replaceDialog('/ask', { isReturning: true });
|
await step.replaceDialog('/ask', { isReturning: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
]));
|
]));
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,32 +36,37 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
import urlJoin = require('url-join');
|
||||||
import { FeedbackDialog } from './dialogs/FeedbackDialog';
|
import { FeedbackDialog } from './dialogs/FeedbackDialog';
|
||||||
import { QualityDialog } from './dialogs/QualityDialog';
|
import { QualityDialog } from './dialogs/QualityDialog';
|
||||||
import { GuaribasQuestionAlternate } from './models/index';
|
import { GuaribasQuestionAlternate } from './models/index';
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for customer-satisfaction.gblib.
|
||||||
|
*/
|
||||||
export class GBCustomerSatisfactionPackage implements IGBPackage {
|
export class GBCustomerSatisfactionPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public getDialogs(min: GBMinInstance) {
|
||||||
core.sequelize.addModels([
|
GBLog.verbose(`getDialogs called.`);
|
||||||
GuaribasQuestionAlternate
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
core.sequelize.addModels([GuaribasQuestionAlternate]);
|
||||||
}
|
}
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
FeedbackDialog.setup(min.bot, min);
|
FeedbackDialog.setup(min.bot, min);
|
||||||
QualityDialog.setup(min.bot, min);
|
QualityDialog.setup(min.bot, min);
|
||||||
}
|
}
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,33 +36,20 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {
|
|
||||||
DataTypeDate,
|
|
||||||
DataTypeDecimal,
|
|
||||||
DataTypes,
|
|
||||||
DataTypeUUIDv4
|
|
||||||
} from 'sequelize';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
BelongsTo,
|
BelongsTo,
|
||||||
BelongsToMany,
|
|
||||||
Column,
|
Column,
|
||||||
CreatedAt,
|
|
||||||
DataType,
|
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
HasMany,
|
|
||||||
IsUUID,
|
|
||||||
Length,
|
|
||||||
Model,
|
Model,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Sequelize,
|
Table} from 'sequelize-typescript';
|
||||||
Table,
|
|
||||||
UpdatedAt
|
|
||||||
} from 'sequelize-typescript';
|
|
||||||
|
|
||||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of saved alternate questions.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
|
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -33,6 +33,9 @@
|
||||||
import { GuaribasConversation } from '../../analytics.gblib/models';
|
import { GuaribasConversation } from '../../analytics.gblib/models';
|
||||||
import { GuaribasQuestionAlternate } from '../models';
|
import { GuaribasQuestionAlternate } from '../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Satisfaction Service Layer.
|
||||||
|
*/
|
||||||
export class CSService {
|
export class CSService {
|
||||||
|
|
||||||
public async resolveQuestionAlternate(
|
public async resolveQuestionAlternate(
|
||||||
|
@ -62,6 +65,7 @@ export class CSService {
|
||||||
rate: number
|
rate: number
|
||||||
): Promise<GuaribasConversation> {
|
): Promise<GuaribasConversation> {
|
||||||
conversation.rate = rate;
|
conversation.rate = rate;
|
||||||
|
|
||||||
return conversation.save();
|
return conversation.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
| |
|
| |
|
||||||
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
||||||
| Licensed under the AGPL-3.0. |
|
| Licensed under the AGPL-3.0. |
|
||||||
| |
|
| |
|
||||||
| According to our dual licensing model, this program can be used either |
|
| According to our dual licensing model, this program can be used either |
|
||||||
| under the terms of the GNU Affero General Public License, version 3, |
|
| under the terms of the GNU Affero General Public License, version 3, |
|
||||||
| or under a proprietary license. |
|
| or under a proprietary license. |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,14 +38,22 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
import { AzureText } from 'pragmatismo-io-framework';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { KBService } from './../services/KBService';
|
import { KBService } from './../services/KBService';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
/**
|
||||||
|
* Dialog arguments.
|
||||||
|
*/
|
||||||
|
export class AskDialogArgs {
|
||||||
|
public questionId: number;
|
||||||
|
public fromFaq: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the ask loop on knowledge base data or delegate to other services.
|
||||||
|
*/
|
||||||
export class AskDialog extends IGBDialog {
|
export class AskDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -55,156 +63,135 @@ export class AskDialog extends IGBDialog {
|
||||||
*/
|
*/
|
||||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
||||||
const service = new KBService(min.core.sequelize);
|
const service = new KBService(min.core.sequelize);
|
||||||
|
min.dialogs.add(new WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min)));
|
||||||
|
min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service)));
|
||||||
|
min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min)));
|
||||||
|
}
|
||||||
|
|
||||||
min.dialogs.add(
|
private static getAskDialog(min: GBMinInstance) {
|
||||||
new WaterfallDialog('/answerEvent', [
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
if (step.options && step.options['questionId']) {
|
const locale = step.context.activity.locale;
|
||||||
const question = await service.getQuestionById(min.instance.instanceId, step.options['questionId']);
|
const user = await min.userProfile.get(step.context, {});
|
||||||
const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
|
user.isAsking = true;
|
||||||
|
if (!user.subjects) {
|
||||||
|
user.subjects = [];
|
||||||
|
}
|
||||||
|
let text;
|
||||||
|
// Three forms of asking.
|
||||||
|
if (step.options && step.options.firstTime) {
|
||||||
|
text = Messages[locale].ask_first_time;
|
||||||
|
} else if (step.options && step.options.isReturning) {
|
||||||
|
text = Messages[locale].anything_else;
|
||||||
|
} else if (user.subjects.length > 0) {
|
||||||
|
text = Messages[locale].which_question;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid use of /ask');
|
||||||
|
}
|
||||||
|
if (text.length > 0) {
|
||||||
|
return await step.prompt('textPrompt', text);
|
||||||
|
}
|
||||||
|
|
||||||
// Sends the answer to all outputs, including projector.
|
return await step.next();
|
||||||
|
},
|
||||||
await service.sendAnswer(min.conversationalService, step, answer);
|
async step => {
|
||||||
|
if (step.result) {
|
||||||
await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/answer', { query: step.result });
|
||||||
}
|
} else {
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
])
|
}
|
||||||
);
|
];
|
||||||
|
}
|
||||||
|
|
||||||
min.dialogs.add(
|
private static getAnswerDialog(min: GBMinInstance, service: KBService) {
|
||||||
new WaterfallDialog('/answer', [
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
const user = await min.userProfile.get(step.context, {});
|
const user = await min.userProfile.get(step.context, {});
|
||||||
let text = step.options['query'];
|
let text = step.options.query;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
throw new Error(`/answer being called with no args query text.`);
|
throw new Error(`/answer being called with no args query text.`);
|
||||||
|
}
|
||||||
|
const locale = step.context.activity.locale;
|
||||||
|
// Stops any content on projector.
|
||||||
|
await min.conversationalService.sendEvent(step, 'stop', undefined);
|
||||||
|
// Handle extra text from FAQ.
|
||||||
|
if (step.options && step.options.query) {
|
||||||
|
text = step.options.query;
|
||||||
|
} else if (step.options && step.options.fromFaq) {
|
||||||
|
await step.context.sendActivity(Messages[locale].going_answer);
|
||||||
|
}
|
||||||
|
// Spells check the input text before sending Search or NLP.
|
||||||
|
if (min.instance.spellcheckerKey !== undefined) {
|
||||||
|
const data = await AzureText.getSpelledText(min.instance.spellcheckerKey, text);
|
||||||
|
if (data !== text) {
|
||||||
|
GBLog.info(`Spelling corrected: ${data}`);
|
||||||
|
text = data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
// Searches KB for the first time.
|
||||||
|
user.lastQuestion = text;
|
||||||
|
await min.userProfile.set(step.context, user);
|
||||||
|
const resultsA = await service.ask(min.instance, text, min.instance.searchScore, user.subjects);
|
||||||
|
|
||||||
// Stops any content on projector.
|
// If there is some result, answer immediately.
|
||||||
|
if (resultsA !== undefined && resultsA.answer !== undefined) {
|
||||||
await min.conversationalService.sendEvent(step, 'stop', null);
|
// Saves some context info.
|
||||||
|
user.isAsking = false;
|
||||||
// Handle extra text from FAQ.
|
user.lastQuestionId = resultsA.questionId;
|
||||||
|
|
||||||
if (step.options && step.options['query']) {
|
|
||||||
text = step.options['query'];
|
|
||||||
} else if (step.options && step.options['fromFaq']) {
|
|
||||||
await step.context.sendActivity(Messages[locale].going_answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spells check the input text before sending Search or NLP.
|
|
||||||
|
|
||||||
if (min.instance.spellcheckerKey) {
|
|
||||||
const data = await AzureText.getSpelledText(min.instance.spellcheckerKey, text);
|
|
||||||
|
|
||||||
if (data != text) {
|
|
||||||
logger.info(`Spelling corrected: ${data}`);
|
|
||||||
text = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Searches KB for the first time.
|
|
||||||
|
|
||||||
user.lastQuestion = text;
|
|
||||||
await min.userProfile.set(step.context, user);
|
await min.userProfile.set(step.context, user);
|
||||||
const resultsA = await service.ask(min.instance, text, min.instance.searchScore, user.subjects);
|
|
||||||
|
|
||||||
|
// Sends the answer to all outputs, including projector.
|
||||||
|
await service.sendAnswer(min.conversationalService, step, resultsA.answer);
|
||||||
|
|
||||||
|
// Goes to ask loop, again.
|
||||||
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
|
} else {
|
||||||
|
// Second time running Search, now with no filter.
|
||||||
|
const resultsB = await service.ask(min.instance, text, min.instance.searchScore, undefined);
|
||||||
// If there is some result, answer immediately.
|
// If there is some result, answer immediately.
|
||||||
|
|
||||||
if (resultsA && resultsA.answer) {
|
if (resultsB !== undefined && resultsB.answer !== undefined) {
|
||||||
// Saves some context info.
|
// Saves some context info.
|
||||||
|
const user2 = await min.userProfile.get(step.context, {});
|
||||||
user.isAsking = false;
|
user2.isAsking = false;
|
||||||
user.lastQuestionId = resultsA.questionId;
|
user2.lastQuestionId = resultsB.questionId;
|
||||||
await min.userProfile.set(step.context, user);
|
await min.userProfile.set(step.context, user2);
|
||||||
|
// Informs user that a broader search will be used.
|
||||||
|
if (user2.subjects.length > 0) {
|
||||||
|
await step.context.sendActivity(Messages[locale].wider_answer);
|
||||||
|
}
|
||||||
// Sends the answer to all outputs, including projector.
|
// Sends the answer to all outputs, including projector.
|
||||||
|
await service.sendAnswer(min.conversationalService, step, resultsB.answer);
|
||||||
await service.sendAnswer(min.conversationalService, step, resultsA.answer);
|
|
||||||
|
|
||||||
// Goes to ask loop, again.
|
|
||||||
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
} else {
|
} else {
|
||||||
// Second time running Search, now with no filter.
|
if (!(await min.conversationalService.routeNLP(step, min, text))) {
|
||||||
|
await step.context.sendActivity(Messages[locale].did_not_find);
|
||||||
|
|
||||||
const resultsB = await service.ask(min.instance, text, min.instance.searchScore, null);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Informs user that a broader search will be used.
|
|
||||||
|
|
||||||
if (user.subjects.length > 0) {
|
|
||||||
const subjectText = `${KBService.getSubjectItemsSeparatedBySpaces(user.subjects)}`;
|
|
||||||
await step.context.sendActivity(Messages[locale].wider_answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends the answer to all outputs, including projector.
|
|
||||||
|
|
||||||
await service.sendAnswer(min.conversationalService, step, resultsB.answer);
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
} else {
|
|
||||||
if (!(await min.conversationalService.routeNLP(step, min, text))) {
|
|
||||||
await step.context.sendActivity(Messages[locale].did_not_find);
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
}
|
||||||
);
|
];
|
||||||
|
}
|
||||||
|
|
||||||
min.dialogs.add(
|
private static getAnswerEventDialog(service: KBService, min: GBMinInstance) {
|
||||||
new WaterfallDialog('/ask', [
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const data = step.options as AskDialogArgs;
|
||||||
const user = await min.userProfile.get(step.context, {});
|
if (data !== undefined && data.questionId !== undefined) {
|
||||||
user.isAsking = true;
|
const question = await service.getQuestionById(min.instance.instanceId, data.questionId);
|
||||||
if (!user.subjects) {
|
const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
|
||||||
user.subjects = [];
|
// Sends the answer to all outputs, including projector.
|
||||||
}
|
await service.sendAnswer(min.conversationalService, step, answer);
|
||||||
let text;
|
await step.replaceDialog('/ask', { isReturning: true });
|
||||||
|
|
||||||
// Three forms of asking.
|
|
||||||
|
|
||||||
if (step.options && step.options['firstTime']) {
|
|
||||||
text = Messages[locale].ask_first_time;
|
|
||||||
} else if (step.options && step.options['isReturning']) {
|
|
||||||
text = Messages[locale].anything_else;
|
|
||||||
} else if (user.subjects.length > 0) {
|
|
||||||
text = Messages[locale].which_question;
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid use of /ask');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text.length > 0) {
|
|
||||||
return await step.prompt('textPrompt', text);
|
|
||||||
}
|
|
||||||
return await step.next();
|
|
||||||
},
|
|
||||||
async step => {
|
|
||||||
if (step.result) {
|
|
||||||
return await step.replaceDialog('/answer', { query: step.result });
|
|
||||||
} else {
|
|
||||||
return await step.next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
return await step.next();
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,11 +38,13 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { KBService } from './../services/KBService';
|
import { KBService } from './../services/KBService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle display of FAQ allowing direct access to KB.
|
||||||
|
*/
|
||||||
export class FaqDialog extends IGBDialog {
|
export class FaqDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -56,15 +58,16 @@ export class FaqDialog extends IGBDialog {
|
||||||
|
|
||||||
min.dialogs.add(new WaterfallDialog('/faq', [
|
min.dialogs.add(new WaterfallDialog('/faq', [
|
||||||
async step => {
|
async step => {
|
||||||
const data = await service.getFaqBySubjectArray('faq', null);
|
const data = await service.getFaqBySubjectArray('faq', undefined);
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
if (data) {
|
if (data !== undefined) {
|
||||||
await min.conversationalService.sendEvent(step, 'play', {
|
await min.conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'bullet',
|
playerType: 'bullet',
|
||||||
data: data.slice(0, 10)
|
data: data.slice(0, 10)
|
||||||
});
|
});
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].see_faq); // TODO: RND messages.
|
await step.context.sendActivity(Messages[locale].see_faq);
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,17 +36,26 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
|
|
||||||
import { BotAdapter, CardFactory, MessageFactory } from 'botbuilder';
|
import { BotAdapter, CardFactory, MessageFactory } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
|
||||||
import { GuaribasSubject } from '../models';
|
import { GuaribasSubject } from '../models';
|
||||||
import { KBService } from '../services/KBService';
|
import { KBService } from '../services/KBService';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog arguments.
|
||||||
|
*/
|
||||||
|
export class MenuDialogArgs {
|
||||||
|
public to: string;
|
||||||
|
public subjectId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialogs for handling Menu control.
|
||||||
|
*/
|
||||||
export class MenuDialog extends IGBDialog {
|
export class MenuDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -58,70 +66,56 @@ export class MenuDialog extends IGBDialog {
|
||||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
||||||
const service = new KBService(min.core.sequelize);
|
const service = new KBService(min.core.sequelize);
|
||||||
|
|
||||||
min.dialogs.add(new WaterfallDialog('/menu', [
|
min.dialogs.add(new WaterfallDialog('/menu', MenuDialog.getMenuDialog(min, service)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getMenuDialog(min: GBMinInstance, service: KBService) {
|
||||||
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
|
const user = await min.userProfile.get(step.context, {});
|
||||||
|
const args: MenuDialogArgs = step.options;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
let rootSubjectId = null;
|
let rootSubjectId = null;
|
||||||
|
|
||||||
if (step.options && step.options['data']) {
|
if (Object.keys(args).length > 0) {
|
||||||
const subject = step.options['data'];
|
|
||||||
|
|
||||||
// If there is a shortcut specified as subject destination, go there.
|
// If there is a shortcut specified as subject destination, go there.
|
||||||
|
if (args.to !== null) {
|
||||||
|
const dialog = args.to.split(':')[1];
|
||||||
|
|
||||||
if (subject.to) {
|
return await step.replaceDialog(`/${dialog}`);
|
||||||
const dialog = subject.to.split(':')[1];
|
|
||||||
await step.replaceDialog('/' + dialog);
|
|
||||||
await step.endDialog();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds to bot a perception of a new subject.
|
user.subjects.push(args);
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
const user = await min.userProfile.get(step.context, {});
|
rootSubjectId = args.subjectId === undefined ? null : args.subjectId;
|
||||||
user.subjects.push(subject);
|
|
||||||
rootSubjectId = subject.subjectId;
|
|
||||||
|
|
||||||
// Whenever a subject is selected, shows a faq about it.
|
// Whenever a subject is selected, shows a faq about it.
|
||||||
|
|
||||||
if (user.subjects.length > 0) {
|
if (user.subjects.length > 0) {
|
||||||
const data = await service.getFaqBySubjectArray(
|
const list = await service.getFaqBySubjectArray('menu', user.subjects);
|
||||||
'menu',
|
|
||||||
user.subjects
|
|
||||||
);
|
|
||||||
await min.conversationalService.sendEvent(step, 'play', {
|
await min.conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'bullet',
|
playerType: 'bullet',
|
||||||
data: data.slice(0, 10)
|
data: list.slice(0, 10)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
user.subjects = [];
|
user.subjects = [];
|
||||||
|
await step.context.sendActivity(Messages[locale].here_is_subjects);
|
||||||
await step.context.sendActivity(Messages[locale].here_is_subjects); // TODO: Handle rnd.
|
|
||||||
user.isAsking = false;
|
user.isAsking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = MessageFactory.text('');
|
const msg = MessageFactory.text('');
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
|
const data = await service.getSubjectItems(min.instance.instanceId, rootSubjectId);
|
||||||
const data = await service.getSubjectItems(
|
|
||||||
min.instance.instanceId,
|
|
||||||
rootSubjectId
|
|
||||||
);
|
|
||||||
|
|
||||||
msg.attachmentLayout = 'carousel';
|
msg.attachmentLayout = 'carousel';
|
||||||
|
data.forEach((item: GuaribasSubject) => {
|
||||||
data.forEach(function(item: GuaribasSubject) {
|
|
||||||
const subject = item;
|
const subject = item;
|
||||||
const card = CardFactory.heroCard(
|
const card = CardFactory.heroCard(
|
||||||
subject.title,
|
subject.title,
|
||||||
subject.description,
|
subject.description,
|
||||||
CardFactory.images([
|
CardFactory.images([urlJoin('/kb', min.instance.kb, 'subjects', 'subject.png')]),
|
||||||
UrlJoin('/kb', min.instance.kb, 'subjects', 'subject.png')
|
|
||||||
]),
|
|
||||||
CardFactory.actions([
|
CardFactory.actions([
|
||||||
{
|
{
|
||||||
channelData: null,
|
|
||||||
type: 'postBack',
|
type: 'postBack',
|
||||||
title: Messages[locale].menu_select,
|
title: Messages[locale].menu_select,
|
||||||
value: JSON.stringify({
|
value: JSON.stringify({
|
||||||
|
@ -134,30 +128,23 @@ export class MenuDialog extends IGBDialog {
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
attachments.push(card);
|
attachments.push(card);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (attachments.length === 0) {
|
if (attachments.length === 0) {
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
|
|
||||||
if (user.subjects && user.subjects.length > 0) {
|
if (user.subjects && user.subjects.length > 0) {
|
||||||
await step.context.sendActivity(
|
await step.context.sendActivity(
|
||||||
Messages[locale].lets_search(
|
Messages[locale].lets_search(KBService.getFormattedSubjectItems(user.subjects))
|
||||||
KBService.getFormattedSubjectItems(user.subjects)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
msg.attachments = attachments;
|
msg.attachments = attachments;
|
||||||
await step.context.sendActivity(msg);
|
await step.context.sendActivity(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
user.isAsking = true;
|
user.isAsking = true;
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
]));
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,43 +36,37 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBPackage } from 'botlib';
|
|
||||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
|
|
||||||
|
|
||||||
import { IGBCoreService } from 'botlib';
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { AskDialog } from './dialogs/AskDialog';
|
import { AskDialog } from './dialogs/AskDialog';
|
||||||
import { FaqDialog } from './dialogs/FaqDialog';
|
import { FaqDialog } from './dialogs/FaqDialog';
|
||||||
import { MenuDialog } from './dialogs/MenuDialog';
|
import { MenuDialog } from './dialogs/MenuDialog';
|
||||||
|
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for kb.gbapp.
|
||||||
|
*/
|
||||||
export class GBKBPackage implements IGBPackage {
|
export class GBKBPackage implements IGBPackage {
|
||||||
|
public sysPackages: IGBPackage[];
|
||||||
public sysPackages: IGBPackage[] = null;
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
|
||||||
core.sequelize.addModels([
|
|
||||||
GuaribasAnswer,
|
|
||||||
GuaribasQuestion,
|
|
||||||
GuaribasSubject
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
core.sequelize.addModels([GuaribasAnswer, GuaribasQuestion, GuaribasSubject]);
|
||||||
}
|
}
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
|
||||||
AskDialog.setup(min.bot, min);
|
AskDialog.setup(min.bot, min);
|
||||||
FaqDialog.setup(min.bot, min);
|
FaqDialog.setup(min.bot, min);
|
||||||
MenuDialog.setup(min.bot, min);
|
MenuDialog.setup(min.bot, min);
|
||||||
|
|
||||||
}
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -60,6 +60,9 @@ import {
|
||||||
} from '../../core.gbapp/models/GBModel';
|
} from '../../core.gbapp/models/GBModel';
|
||||||
import { GuaribasUser } from '../../security.gblib/models';
|
import { GuaribasUser } from '../../security.gblib/models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subjects to group the pair of questions and answers.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasSubject extends Model<GuaribasSubject> {
|
export class GuaribasSubject extends Model<GuaribasSubject> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -111,6 +114,9 @@ export class GuaribasSubject extends Model<GuaribasSubject> {
|
||||||
public package: GuaribasPackage;
|
public package: GuaribasPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A question and its metadata.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -155,6 +161,7 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||||
@UpdatedAt
|
@UpdatedAt
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
|
|
||||||
|
//tslint:disable-next-line:no-use-before-declare
|
||||||
@ForeignKey(() => GuaribasAnswer)
|
@ForeignKey(() => GuaribasAnswer)
|
||||||
@Column
|
@Column
|
||||||
public answerId: number;
|
public answerId: number;
|
||||||
|
@ -174,6 +181,9 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||||
public package: GuaribasPackage;
|
public package: GuaribasPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An answer and its metadata.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasAnswer extends Model<GuaribasAnswer> {
|
export class GuaribasAnswer extends Model<GuaribasAnswer> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -34,31 +34,37 @@
|
||||||
* @fileoverview Knowledge base services and logic.
|
* @fileoverview Knowledge base services and logic.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const Fs = require('fs');
|
const Fs = require('fs');
|
||||||
|
import urlJoin = require('url-join');
|
||||||
const parse = require('bluebird').promisify(require('csv-parse'));
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const marked = require('marked');
|
const marked = require('marked');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const asyncPromise = require('async-promises');
|
const asyncPromise = require('async-promises');
|
||||||
const walkPromise = require('walk-promise');
|
const walkPromise = require('walk-promise');
|
||||||
import { Messages } from '../strings';
|
// tslint:disable-next-line:newline-per-chained-call
|
||||||
|
const parse = require('bluebird').promisify(require('csv-parse'));
|
||||||
|
|
||||||
import { IGBConversationalService, IGBCoreService, IGBInstance } from 'botlib';
|
import { GBDialogStep, GBLog, IGBConversationalService, IGBCoreService, IGBInstance } from 'botlib';
|
||||||
import { AzureSearch } from 'pragmatismo-io-framework';
|
import { AzureSearch } from 'pragmatismo-io-framework';
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
|
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
|
||||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
|
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
|
||||||
|
import { Messages } from '../strings';
|
||||||
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
|
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result for quey on KB data.
|
||||||
|
*/
|
||||||
export class KBServiceSearchResults {
|
export class KBServiceSearchResults {
|
||||||
public answer: GuaribasAnswer;
|
public answer: GuaribasAnswer;
|
||||||
public questionId: number;
|
public questionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All services related to knowledge base management.
|
||||||
|
*/
|
||||||
export class KBService {
|
export class KBService {
|
||||||
public sequelize: Sequelize;
|
public sequelize: Sequelize;
|
||||||
|
|
||||||
|
@ -67,7 +73,7 @@ export class KBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
|
public static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
|
||||||
if (!subjects) {
|
if (subjects === null) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const out = [];
|
const out = [];
|
||||||
|
@ -115,7 +121,7 @@ export class KBService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (question) {
|
if (question !== null) {
|
||||||
const answer = await GuaribasAnswer.findOne({
|
const answer = await GuaribasAnswer.findOne({
|
||||||
where: {
|
where: {
|
||||||
instanceId: instanceId,
|
instanceId: instanceId,
|
||||||
|
@ -126,7 +132,7 @@ export class KBService {
|
||||||
return Promise.resolve({ question: question, answer: answer });
|
return Promise.resolve({ question: question, answer: answer });
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
|
public async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
|
||||||
|
@ -156,40 +162,39 @@ export class KBService {
|
||||||
query = query.replace('/', ' ');
|
query = query.replace('/', ' ');
|
||||||
query = query.replace('\\', ' ');
|
query = query.replace('\\', ' ');
|
||||||
|
|
||||||
if (subjects) {
|
if (subjects !== null) {
|
||||||
const text = KBService.getSubjectItemsSeparatedBySpaces(subjects);
|
const text = KBService.getSubjectItemsSeparatedBySpaces(subjects);
|
||||||
if (text) {
|
if (text !== null) {
|
||||||
query = `${query} ${text}`;
|
query = `${query} ${text}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
query = `${query}&$filter=instanceId eq ${instance.instanceId}`;
|
query = `${query}&$filter=instanceId eq ${instance.instanceId}`;
|
||||||
try {
|
|
||||||
if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
|
// tslint:disable:no-unsafe-any
|
||||||
const service = new AzureSearch(
|
if (instance.searchKey !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
|
||||||
instance.searchKey,
|
const service = new AzureSearch(
|
||||||
instance.searchHost,
|
instance.searchKey,
|
||||||
instance.searchIndex,
|
instance.searchHost,
|
||||||
instance.searchIndexer
|
instance.searchIndex,
|
||||||
);
|
instance.searchIndexer
|
||||||
const results = await service.search(query);
|
);
|
||||||
if (results && results.length > 0 && results[0]['@search.score'] >= searchScore) {
|
const results = await service.search(query);
|
||||||
const value = await this.getAnswerById(instance.instanceId, results[0].answerId);
|
|
||||||
if (value) {
|
if (results && results.length > 0 && results[0]['@search.score'] >= searchScore) {
|
||||||
return Promise.resolve({ answer: value, questionId: results[0].questionId });
|
const value = await this.getAnswerById(instance.instanceId, results[0].answerId);
|
||||||
} else {
|
if (value !== null) {
|
||||||
return Promise.resolve({ answer: null, questionId: 0 });
|
return Promise.resolve({ answer: value, questionId: results[0].questionId });
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const data = await this.getAnswerByText(instance.instanceId, query);
|
|
||||||
if (data) {
|
|
||||||
return Promise.resolve({ answer: data.answer, questionId: data.question.questionId });
|
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ answer: null, questionId: 0 });
|
return Promise.resolve({ answer: undefined, questionId: 0 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (reason) {
|
} else {
|
||||||
return Promise.reject(new Error(reason));
|
const data = await this.getAnswerByText(instance.instanceId, query);
|
||||||
|
if (data) {
|
||||||
|
return Promise.resolve({ answer: data.answer, questionId: data.question.questionId });
|
||||||
|
} else {
|
||||||
|
return Promise.resolve({ answer: undefined, questionId: 0 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,29 +207,32 @@ export class KBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
|
public async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
|
||||||
|
|
||||||
if (subjects) {
|
if (subjects) {
|
||||||
const where = {
|
const where = {
|
||||||
from: from,
|
from: from,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
subject1: null,
|
subject1: null,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
subject2: null,
|
subject2: null,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
subject3: null,
|
subject3: null,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
subject4: null
|
subject4: null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (subjects[0]) {
|
if (subjects[0] && subjects[0].internalId) {
|
||||||
where.subject1 = subjects[0].internalId;
|
where.subject1 = subjects[0].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subjects[1]) {
|
if (subjects[1] && subjects[1].internalId) {
|
||||||
where.subject2 = subjects[1].internalId;
|
where.subject2 = subjects[1].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subjects[2]) {
|
if (subjects[2] && subjects[2].internalId) {
|
||||||
where.subject3 = subjects[2].internalId;
|
where.subject3 = subjects[2].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subjects[3]) {
|
if (subjects[3] && subjects[3].internalId) {
|
||||||
where.subject4 = subjects[3].internalId;
|
where.subject4 = subjects[3].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,12 +278,12 @@ export class KBService {
|
||||||
// Extracts answer from external media if any.
|
// Extracts answer from external media if any.
|
||||||
|
|
||||||
if (answer.indexOf('.md') > -1) {
|
if (answer.indexOf('.md') > -1) {
|
||||||
const mediaFilename = UrlJoin(path.dirname(filePath), '..', 'articles', answer);
|
const mediaFilename = urlJoin(path.dirname(filePath), '..', 'articles', answer);
|
||||||
if (Fs.existsSync(mediaFilename)) {
|
if (Fs.existsSync(mediaFilename)) {
|
||||||
answer = Fs.readFileSync(mediaFilename, 'utf8');
|
answer = Fs.readFileSync(mediaFilename, 'utf8');
|
||||||
format = '.md';
|
format = '.md';
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[GBImporter] File not found: ${mediaFilename}.`);
|
GBLog.info(`[GBImporter] File not found: ${mediaFilename}.`);
|
||||||
answer = '';
|
answer = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,13 +298,13 @@ export class KBService {
|
||||||
let indexer = 0;
|
let indexer = 0;
|
||||||
|
|
||||||
subjectArray.forEach(element => {
|
subjectArray.forEach(element => {
|
||||||
if (indexer == 0) {
|
if (indexer === 0) {
|
||||||
subject1 = subjectArray[indexer].substring(0, 63);
|
subject1 = subjectArray[indexer].substring(0, 63);
|
||||||
} else if (indexer == 1) {
|
} else if (indexer === 1) {
|
||||||
subject2 = subjectArray[indexer].substring(0, 63);
|
subject2 = subjectArray[indexer].substring(0, 63);
|
||||||
} else if (indexer == 2) {
|
} else if (indexer === 2) {
|
||||||
subject3 = subjectArray[indexer].substring(0, 63);
|
subject3 = subjectArray[indexer].substring(0, 63);
|
||||||
} else if (indexer == 3) {
|
} else if (indexer === 3) {
|
||||||
subject4 = subjectArray[indexer].substring(0, 63);
|
subject4 = subjectArray[indexer].substring(0, 63);
|
||||||
}
|
}
|
||||||
indexer++;
|
indexer++;
|
||||||
|
@ -309,7 +317,7 @@ export class KBService {
|
||||||
content: answer,
|
content: answer,
|
||||||
format: format,
|
format: format,
|
||||||
packageId: packageId,
|
packageId: packageId,
|
||||||
prevId: lastQuestionId ? lastQuestionId : 0
|
prevId: lastQuestionId !== null ? lastQuestionId : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const question1 = await GuaribasQuestion.create({
|
const question1 = await GuaribasQuestion.create({
|
||||||
|
@ -325,8 +333,8 @@ export class KBService {
|
||||||
packageId: packageId
|
packageId: packageId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (lastAnswer && lastQuestionId) {
|
if (lastAnswer !== undefined && lastQuestionId !== 0) {
|
||||||
await lastAnswer.updateAttributes({ nextId: lastQuestionId });
|
await lastAnswer.update({ nextId: lastQuestionId });
|
||||||
}
|
}
|
||||||
lastAnswer = answer1;
|
lastAnswer = answer1;
|
||||||
lastQuestionId = question1.questionId;
|
lastQuestionId = question1.questionId;
|
||||||
|
@ -335,21 +343,21 @@ export class KBService {
|
||||||
} else {
|
} else {
|
||||||
// Skips the header.
|
// Skips the header.
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendAnswer(conversationalService: IGBConversationalService, step: any, answer: GuaribasAnswer) {
|
public async sendAnswer(conversationalService: IGBConversationalService, step: GBDialogStep, answer: GuaribasAnswer) {
|
||||||
if (answer.content.endsWith('.mp4')) {
|
if (answer.content.endsWith('.mp4')) {
|
||||||
await conversationalService.sendEvent(step, 'play', {
|
await conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'video',
|
playerType: 'video',
|
||||||
data: answer.content
|
data: answer.content
|
||||||
});
|
});
|
||||||
} else if (answer.content.length > 140 && step.context._activity.channelId === 'webchat') {
|
} else if (answer.content.length > 140 && step.context.activity.channelId === 'webchat') {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].will_answer_projector); // TODO: Handle rnd.
|
await step.context.sendActivity(Messages[locale].will_answer_projector);
|
||||||
let html = answer.content;
|
let html = answer.content;
|
||||||
|
|
||||||
if (answer.format === '.md') {
|
if (answer.format === '.md') {
|
||||||
|
@ -377,7 +385,7 @@ export class KBService {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await step.context.sendActivity(answer.content);
|
await step.context.sendActivity(answer.content);
|
||||||
await conversationalService.sendEvent(step, 'stop', null);
|
await conversationalService.sendEvent(step, 'stop', undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +396,7 @@ export class KBService {
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
// Imports subjects tree into database and return it.
|
// Imports subjects tree into database and return it.
|
||||||
|
|
||||||
await this.importSubjectFile(packageStorage.packageId, UrlJoin(localPath, 'subjects.json'), instance);
|
await this.importSubjectFile(packageStorage.packageId, urlJoin(localPath, 'subjects.json'), instance);
|
||||||
|
|
||||||
// Import all .tsv files in the tabular directory.
|
// Import all .tsv files in the tabular directory.
|
||||||
|
|
||||||
|
@ -396,24 +404,22 @@ export class KBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importKbTabularDirectory(localPath: string, instance: IGBInstance, packageId: number): Promise<any> {
|
public async importKbTabularDirectory(localPath: string, instance: IGBInstance, packageId: number): Promise<any> {
|
||||||
const files = await walkPromise(UrlJoin(localPath, 'tabular'));
|
const files = await walkPromise(urlJoin(localPath, 'tabular'));
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
files.map(async file => {
|
files.map(async file => {
|
||||||
if (file.name.endsWith('.tsv')) {
|
if (file.name.endsWith('.tsv')) {
|
||||||
return this.importKbTabularFile(UrlJoin(file.root, file.name), instance.instanceId, packageId);
|
return this.importKbTabularFile(urlJoin(file.root, file.name), instance.instanceId, packageId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importSubjectFile(packageId: number, filename: string, instance: IGBInstance): Promise<any> {
|
public async importSubjectFile(packageId: number, filename: string, instance: IGBInstance): Promise<any> {
|
||||||
const subjects = JSON.parse(Fs.readFileSync(filename, 'utf8'));
|
const subjectsLoaded = JSON.parse(Fs.readFileSync(filename, 'utf8'));
|
||||||
|
|
||||||
const doIt = async (subjects: GuaribasSubject[], parentSubjectId: number) => {
|
const doIt = async (subjects: GuaribasSubject[], parentSubjectId: number) => {
|
||||||
return asyncPromise.eachSeries(subjects, async item => {
|
return asyncPromise.eachSeries(subjects, async item => {
|
||||||
const mediaFilename = item.id + '.png';
|
|
||||||
|
|
||||||
const value = await GuaribasSubject.create({
|
const value = await GuaribasSubject.create({
|
||||||
internalId: item.id,
|
internalId: item.id,
|
||||||
parentSubjectId: parentSubjectId,
|
parentSubjectId: parentSubjectId,
|
||||||
|
@ -432,7 +438,8 @@ export class KBService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return doIt(subjects.children, null);
|
|
||||||
|
return doIt(subjectsLoaded.children, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async undeployKbFromStorage(instance: IGBInstance, deployer: GBDeployer, packageId: number) {
|
public async undeployKbFromStorage(instance: IGBInstance, deployer: GBDeployer, packageId: number) {
|
||||||
|
@ -449,7 +456,7 @@ export class KBService {
|
||||||
where: { instanceId: instance.instanceId, packageId: packageId }
|
where: { instanceId: instance.instanceId, packageId: packageId }
|
||||||
});
|
});
|
||||||
|
|
||||||
await deployer.rebuildIndex(instance);
|
await deployer.rebuildIndex(instance, new AzureDeployerService(deployer).getKBSearchSchema(instance.searchIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -460,15 +467,15 @@ export class KBService {
|
||||||
public async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
|
public async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
|
||||||
const packageType = Path.extname(localPath);
|
const packageType = Path.extname(localPath);
|
||||||
const packageName = Path.basename(localPath);
|
const packageName = Path.basename(localPath);
|
||||||
logger.info(`[GBDeployer] Opening package: ${localPath}`);
|
GBLog.info(`[GBDeployer] Opening package: ${localPath}`);
|
||||||
const packageObject = JSON.parse(Fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8'));
|
const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
|
||||||
|
|
||||||
const instance = await core.loadInstance(packageObject.botId);
|
const instance = await core.loadInstance(packageObject.botId);
|
||||||
logger.info(`[GBDeployer] Importing: ${localPath}`);
|
GBLog.info(`[GBDeployer] Importing: ${localPath}`);
|
||||||
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
|
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
|
||||||
await this.importKbPackage(localPath, p, instance);
|
await this.importKbPackage(localPath, p, instance);
|
||||||
|
|
||||||
deployer.rebuildIndex(instance);
|
deployer.rebuildIndex(instance, new AzureDeployerService(deployer).getKBSearchSchema(instance.searchIndex));
|
||||||
logger.info(`[GBDeployer] Finished import of ${localPath}`);
|
GBLog.info(`[GBDeployer] Finished import of ${localPath}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,8 @@ export const Messages = {
|
||||||
here_is_subjects: 'Here are some subjects to choose from...',
|
here_is_subjects: 'Here are some subjects to choose from...',
|
||||||
menu_select: 'Select',
|
menu_select: 'Select',
|
||||||
lets_search: query =>
|
lets_search: query =>
|
||||||
`Vamos pesquisar sobre ${query}... O que deseja saber?`,
|
`Lets search for ${query}... What do you want to know?`,
|
||||||
see_faq:
|
see_faq: 'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
|
||||||
'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
|
|
||||||
will_answer_projector:
|
will_answer_projector:
|
||||||
'I\'ll answer on the projector to a better experience...',
|
'I\'ll answer on the projector to a better experience...',
|
||||||
ask_first_time: 'What are you looking for?'
|
ask_first_time: 'What are you looking for?'
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,37 +36,35 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
|
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for the security module.
|
||||||
|
*/
|
||||||
export class GBSecurityPackage implements IGBPackage {
|
export class GBSecurityPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public getDialogs(min: GBMinInstance) {
|
||||||
core.sequelize.addModels([
|
GBLog.verbose(`getDialogs called.`);
|
||||||
GuaribasGroup,
|
|
||||||
GuaribasUser,
|
|
||||||
GuaribasUserGroup
|
|
||||||
]);
|
|
||||||
|
|
||||||
core;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`loadBot called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
}
|
}
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
core.sequelize.addModels([GuaribasGroup, GuaribasUser, GuaribasUserGroup]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,33 +36,23 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {
|
|
||||||
DataTypeDate,
|
|
||||||
DataTypeDecimal,
|
|
||||||
DataTypes,
|
|
||||||
DataTypeUUIDv4
|
|
||||||
} from 'sequelize';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
BelongsTo,
|
BelongsTo,
|
||||||
BelongsToMany,
|
|
||||||
Column,
|
Column,
|
||||||
CreatedAt,
|
|
||||||
DataType,
|
DataType,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
HasMany,
|
|
||||||
IsUUID,
|
|
||||||
Length,
|
Length,
|
||||||
Model,
|
Model,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Sequelize,
|
Table} from 'sequelize-typescript';
|
||||||
Table,
|
|
||||||
UpdatedAt
|
|
||||||
} from 'sequelize-typescript';
|
|
||||||
|
|
||||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A user and its metadata.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasUser extends Model<GuaribasUser> {
|
export class GuaribasUser extends Model<GuaribasUser> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -90,6 +80,9 @@ export class GuaribasUser extends Model<GuaribasUser> {
|
||||||
public instance: GuaribasInstance;
|
public instance: GuaribasInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A group of users.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasGroup extends Model<GuaribasGroup> {
|
export class GuaribasGroup extends Model<GuaribasGroup> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -109,6 +102,9 @@ export class GuaribasGroup extends Model<GuaribasGroup> {
|
||||||
public instance: GuaribasInstance;
|
public instance: GuaribasInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relation of groups and users.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
|
export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
|
||||||
@ForeignKey(() => GuaribasUser)
|
@ForeignKey(() => GuaribasUser)
|
||||||
|
|
|
@ -1,68 +1,31 @@
|
||||||
/*****************************************************************************\
|
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
const Path = require('path');
|
|
||||||
const Fs = require('fs');
|
const Fs = require('fs');
|
||||||
const _ = require('lodash');
|
import urlJoin = require('url-join');
|
||||||
const Parse = require('csv-parse');
|
|
||||||
const Async = require('async');
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
|
|
||||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
import { GBService, IGBInstance } from 'botlib';
|
||||||
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
|
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security service layer.
|
||||||
|
*/
|
||||||
export class SecService extends GBService {
|
export class SecService extends GBService {
|
||||||
|
|
||||||
public async importSecurityFile(localPath: string, instance: IGBInstance) {
|
public async importSecurityFile(localPath: string, instance: IGBInstance) {
|
||||||
const security = JSON.parse(
|
const security = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'security.json'), 'utf8'));
|
||||||
Fs.readFileSync(UrlJoin(localPath, 'security.json'), 'utf8')
|
|
||||||
);
|
|
||||||
security.groups.forEach(group => {
|
security.groups.forEach(group => {
|
||||||
const groupDb = GuaribasGroup.build({
|
const groupDb = GuaribasGroup.build({
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
displayName: group.displayName
|
displayName: group.displayName
|
||||||
});
|
});
|
||||||
groupDb.save().then(groupDb => {
|
groupDb.save().then(g1 => {
|
||||||
group.users.forEach(user => {
|
group.users.forEach(user => {
|
||||||
const userDb = GuaribasUser.build({
|
const userDb = GuaribasUser.build({
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
groupId: groupDb.groupId,
|
groupId: g1.groupId,
|
||||||
userName: user.userName
|
userName: user.userName
|
||||||
});
|
});
|
||||||
userDb.save().then(userDb => {
|
userDb.save().then(user2 => {
|
||||||
const userGroup = GuaribasUserGroup.build();
|
const userGroup = GuaribasUserGroup.build();
|
||||||
userGroup.groupId = groupDb.groupId;
|
userGroup.groupId = g1.groupId;
|
||||||
userGroup.userId = userDb.userId;
|
userGroup.userId = user2.userId;
|
||||||
userGroup.save();
|
userGroup.save();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -78,16 +41,15 @@ export class SecService extends GBService {
|
||||||
channelName: string,
|
channelName: string,
|
||||||
displayName: string
|
displayName: string
|
||||||
): Promise<GuaribasUser> {
|
): Promise<GuaribasUser> {
|
||||||
return new Promise<GuaribasUser>(
|
return new Promise<GuaribasUser>((resolve, reject) => {
|
||||||
(resolve, reject) => {
|
GuaribasUser.findOne({
|
||||||
|
attributes: ['instanceId', 'internalAddress'],
|
||||||
GuaribasUser.findOne({
|
where: {
|
||||||
attributes: ['instanceId', 'internalAddress'],
|
instanceId: instanceId,
|
||||||
where: {
|
userSystemId: userSystemId
|
||||||
instanceId: instanceId,
|
}
|
||||||
userSystemId: userSystemId
|
})
|
||||||
}
|
.then(user => {
|
||||||
}).then(user => {
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = GuaribasUser.build();
|
user = GuaribasUser.build();
|
||||||
}
|
}
|
||||||
|
@ -99,7 +61,8 @@ export class SecService extends GBService {
|
||||||
user.defaultChannel = channelName;
|
user.defaultChannel = channelName;
|
||||||
user.save();
|
user.save();
|
||||||
resolve(user);
|
resolve(user);
|
||||||
}).error(reject);
|
})
|
||||||
});
|
.error(reject);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,39 +36,46 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { WhatsappDirectLine } from './services/WhatsappDirectLine';
|
import { WhatsappDirectLine } from './services/WhatsappDirectLine';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for whatsapp.gblib
|
||||||
|
*/
|
||||||
export class GBWhatsappPackage implements IGBPackage {
|
export class GBWhatsappPackage implements IGBPackage {
|
||||||
|
public sysPackages: IGBPackage[];
|
||||||
|
|
||||||
public sysPackages: IGBPackage[] = null;
|
public channel: WhatsappDirectLine;
|
||||||
public channel: WhatsappDirectLine;
|
|
||||||
|
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
// Only loads engine if it is defined on services.json.
|
||||||
|
|
||||||
|
if (min.instance.whatsappBotKey !== undefined) {
|
||||||
|
this.channel = new WhatsappDirectLine(
|
||||||
|
min.botId,
|
||||||
|
min.instance.whatsappBotKey,
|
||||||
|
min.instance.whatsappServiceKey,
|
||||||
|
min.instance.whatsappServiceNumber,
|
||||||
|
min.instance.whatsappServiceUrl,
|
||||||
|
min.instance.whatsappServiceWebhookUrl
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
}
|
}
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
public loadBot(min: GBMinInstance): void {
|
GBLog.verbose(`loadPackage called.`);
|
||||||
|
}
|
||||||
// Only loads engine if it is defined on services.json.
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
if (min.instance.whatsappBotKey) {
|
}
|
||||||
this.channel = new WhatsappDirectLine(min.botId, min.instance.whatsappBotKey, min.instance.whatsappServiceKey,
|
public unloadBot(min: GBMinInstance): void {
|
||||||
min.instance.whatsappServiceNumber, min.instance.whatsappServiceUrl, min.instance.whatsappServiceWebhookUrl);
|
GBLog.verbose(`unloadBot called.`);
|
||||||
}
|
}
|
||||||
}
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
public unloadBot(min: GBMinInstance): void {
|
}
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,266 +1,218 @@
|
||||||
/*****************************************************************************\
|
import urlJoin = require('url-join');
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
const Path = require('path');
|
|
||||||
const Fs = require('fs');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const Parse = require('csv-parse');
|
|
||||||
const Async = require('async');
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Swagger = require('swagger-client');
|
const Swagger = require('swagger-client');
|
||||||
const rp = require('request-promise');
|
const rp = require('request-promise');
|
||||||
|
import { GBLog, GBService } from 'botlib';
|
||||||
import * as request from 'request-promise-native';
|
import * as request from 'request-promise-native';
|
||||||
|
|
||||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
/**
|
||||||
|
* Support for Whatsapp.
|
||||||
|
*/
|
||||||
export class WhatsappDirectLine extends GBService {
|
export class WhatsappDirectLine extends GBService {
|
||||||
|
public pollInterval = 1000;
|
||||||
|
public directLineClientName = 'DirectLineClient';
|
||||||
|
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||||
|
|
||||||
public pollInterval = 1000;
|
public directLineClient: any;
|
||||||
public directLineClientName = 'DirectLineClient';
|
public whatsappServiceKey: string;
|
||||||
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
public whatsappServiceNumber: string;
|
||||||
|
public whatsappServiceUrl: string;
|
||||||
|
public whatsappServiceWebhookUrl: string;
|
||||||
|
public botId: string;
|
||||||
|
public watermark: string;
|
||||||
|
|
||||||
public directLineClient: any;
|
public conversationIds = {};
|
||||||
public whatsappServiceKey: string;
|
|
||||||
public whatsappServiceNumber: string;
|
|
||||||
public whatsappServiceUrl: string;
|
|
||||||
public whatsappServiceWebhookUrl: string;
|
|
||||||
public botId: string;
|
|
||||||
public watermark: string = null;
|
|
||||||
|
|
||||||
public conversationIds = {};
|
constructor(
|
||||||
|
botId,
|
||||||
|
directLineSecret,
|
||||||
|
whatsappServiceKey,
|
||||||
|
whatsappServiceNumber,
|
||||||
|
whatsappServiceUrl,
|
||||||
|
whatsappServiceWebhookUrl
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
constructor(botId, directLineSecret, whatsappServiceKey, whatsappServiceNumber, whatsappServiceUrl, whatsappServiceWebhookUrl) {
|
this.botId = botId;
|
||||||
|
this.whatsappServiceKey = whatsappServiceKey;
|
||||||
|
this.whatsappServiceNumber = whatsappServiceNumber;
|
||||||
|
this.whatsappServiceUrl = whatsappServiceUrl;
|
||||||
|
this.whatsappServiceWebhookUrl = whatsappServiceWebhookUrl;
|
||||||
|
|
||||||
super();
|
this.directLineClient = rp(this.directLineSpecUrl)
|
||||||
|
.then(spec => {
|
||||||
this.botId = botId;
|
return new Swagger({
|
||||||
this.whatsappServiceKey = whatsappServiceKey;
|
spec: JSON.parse(spec.trim()),
|
||||||
this.whatsappServiceNumber = whatsappServiceNumber;
|
usePromise: true
|
||||||
this.whatsappServiceUrl = whatsappServiceUrl;
|
|
||||||
this.whatsappServiceWebhookUrl = whatsappServiceWebhookUrl;
|
|
||||||
|
|
||||||
// TODO: Migrate to Swagger 3.
|
|
||||||
this.directLineClient = rp(this.directLineSpecUrl)
|
|
||||||
.then((spec) => {
|
|
||||||
return new Swagger({
|
|
||||||
spec: JSON.parse(spec.trim()),
|
|
||||||
usePromise: true
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(async (client) => {
|
|
||||||
client.clientAuthorizations.add('AuthorizationBotConnector',
|
|
||||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' +
|
|
||||||
directLineSecret, 'header'));
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
method: 'POST',
|
|
||||||
url: UrlJoin(this.whatsappServiceUrl, 'webhook'),
|
|
||||||
qs:
|
|
||||||
{
|
|
||||||
token: this.whatsappServiceKey,
|
|
||||||
webhookUrl: `${this.whatsappServiceWebhookUrl}/instances/${this.botId}/whatsapp`,
|
|
||||||
set: true
|
|
||||||
},
|
|
||||||
headers:
|
|
||||||
{
|
|
||||||
'cache-control': 'no-cache'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await request.post(options);
|
|
||||||
logger.info(result);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error initializing 3rd party Whatsapp provider.', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return client;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error('Error initializing DirectLine client', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public received(req, res) {
|
|
||||||
const text = req.body.messages[0].body;
|
|
||||||
const from = req.body.messages[0].author.split('@')[0];
|
|
||||||
const fromName = req.body.messages[0].senderName;
|
|
||||||
|
|
||||||
if (req.body.messages[0].fromMe) {
|
|
||||||
return; // Exit here.
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`GBWhatsapp: Hook called. from: ${from}(${fromName}), text: ${text})`);
|
|
||||||
|
|
||||||
const conversationId = this.conversationIds[from];
|
|
||||||
|
|
||||||
this.directLineClient.then((client) => {
|
|
||||||
|
|
||||||
if (this.conversationIds[from] == null) {
|
|
||||||
|
|
||||||
logger.info(`GBWhatsapp: Starting new conversation on Bot.`);
|
|
||||||
client.Conversations.Conversations_StartConversation()
|
|
||||||
.then((response) => {
|
|
||||||
return response.obj.conversationId;
|
|
||||||
})
|
|
||||||
.then((conversationId) => {
|
|
||||||
|
|
||||||
this.conversationIds[from] = conversationId;
|
|
||||||
this.inputMessage(client, conversationId, text,
|
|
||||||
from, fromName);
|
|
||||||
|
|
||||||
this.pollMessages(client, conversationId, from, fromName);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('Error starting conversation', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.inputMessage(client, conversationId, text,
|
|
||||||
from, fromName);
|
|
||||||
}
|
|
||||||
res.end();
|
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
|
.then(async client => {
|
||||||
|
client.clientAuthorizations.add(
|
||||||
|
'AuthorizationBotConnector',
|
||||||
|
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${directLineSecret}`, 'header')
|
||||||
|
);
|
||||||
|
|
||||||
public inputMessage(client, conversationId, text, from, fromName) {
|
|
||||||
|
|
||||||
client.Conversations.Conversations_PostActivity(
|
|
||||||
{
|
|
||||||
conversationId: conversationId,
|
|
||||||
activity: {
|
|
||||||
textFormat: 'plain',
|
|
||||||
text: text,
|
|
||||||
type: 'message',
|
|
||||||
from: {
|
|
||||||
id: from,
|
|
||||||
name: fromName
|
|
||||||
},
|
|
||||||
replyToId: from
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
logger.error(`GBWhatsapp: Error receiving message: ${err}.`);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public pollMessages(client, conversationId, from, fromName) {
|
|
||||||
|
|
||||||
logger.info(`GBWhatsapp: Starting polling message for conversationId:
|
|
||||||
${conversationId}.`);
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
client.Conversations.Conversations_GetActivities({
|
|
||||||
conversationId:
|
|
||||||
conversationId, watermark: this.watermark
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
this.watermark = response.obj.watermark;
|
|
||||||
return response.obj.activities;
|
|
||||||
})
|
|
||||||
.then((activities) => {
|
|
||||||
this.printMessages(activities, conversationId, from, fromName);
|
|
||||||
});
|
|
||||||
}, this.pollInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public printMessages(activities, conversationId, from, fromName) {
|
|
||||||
|
|
||||||
if (activities && activities.length) {
|
|
||||||
|
|
||||||
// Ignore own messages.
|
|
||||||
|
|
||||||
activities = activities.filter((m) => (m.from.id === 'GeneralBots') && m.type === 'message');
|
|
||||||
|
|
||||||
if (activities.length) {
|
|
||||||
|
|
||||||
// Print other messages.
|
|
||||||
|
|
||||||
activities.forEach(activity => {
|
|
||||||
this.printMessage(activity, conversationId, from, fromName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public printMessage(activity, conversationId, from, fromName) {
|
|
||||||
|
|
||||||
let output = '';
|
|
||||||
|
|
||||||
if (activity.text) {
|
|
||||||
logger.info(`GBWhatsapp: MSG: ${activity.text}`);
|
|
||||||
output = activity.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activity.attachments) {
|
|
||||||
activity.attachments.forEach((attachment) => {
|
|
||||||
switch (attachment.contentType) {
|
|
||||||
case 'application/vnd.microsoft.card.hero':
|
|
||||||
output += `\n${this.renderHeroCard(attachment)}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'image/png':
|
|
||||||
logger.info('Opening the requested image ' + attachment.contentUrl);
|
|
||||||
output += `\n${attachment.contentUrl}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sendToDevice(conversationId, from, fromName, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderHeroCard(attachment) {
|
|
||||||
return `${attachment.content.title} - ${attachment.content.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async sendToDevice(conversationId, to, toName, msg) {
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: UrlJoin(this.whatsappServiceUrl, 'message'),
|
url: urlJoin(this.whatsappServiceUrl, 'webhook'),
|
||||||
qs:
|
qs: {
|
||||||
{
|
token: this.whatsappServiceKey,
|
||||||
token: this.whatsappServiceKey,
|
webhookUrl: `${this.whatsappServiceWebhookUrl}/instances/${this.botId}/whatsapp`,
|
||||||
phone: to,
|
set: true
|
||||||
body: msg
|
},
|
||||||
},
|
headers: {
|
||||||
headers:
|
'cache-control': 'no-cache'
|
||||||
{
|
}
|
||||||
'cache-control': 'no-cache'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await request.get(options);
|
try {
|
||||||
|
const result = request.post(options);
|
||||||
|
GBLog.info(result);
|
||||||
|
} catch (error) {
|
||||||
|
GBLog.error(`Error initializing 3rd party Whatsapp provider(1) ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
GBLog.error(`Error initializing 3rd party Whatsapp provider(2) ${err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public received(req, res) {
|
||||||
|
const text = req.body.messages[0].body;
|
||||||
|
const from = req.body.messages[0].author.split('@')[0];
|
||||||
|
const fromName = req.body.messages[0].senderName;
|
||||||
|
|
||||||
|
if (req.body.messages[0].fromMe) {
|
||||||
|
return; // Exit here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GBLog.info(`GBWhatsapp: Hook called. from: ${from}(${fromName}), text: ${text})`);
|
||||||
|
|
||||||
|
const conversationId = this.conversationIds[from];
|
||||||
|
|
||||||
|
this.directLineClient.then(client => {
|
||||||
|
if (this.conversationIds[from] === undefined) {
|
||||||
|
GBLog.info(`GBWhatsapp: Starting new conversation on Bot.`);
|
||||||
|
client.Conversations.Conversations_StartConversation()
|
||||||
|
.then(response => {
|
||||||
|
return response.obj.conversationId;
|
||||||
|
})
|
||||||
|
.then(generatedConversationId => {
|
||||||
|
this.conversationIds[from] = generatedConversationId;
|
||||||
|
this.inputMessage(client, generatedConversationId, text, from, fromName);
|
||||||
|
|
||||||
|
this.pollMessages(client, generatedConversationId, from, fromName);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
GBLog.error(`Error starting conversation ${err}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.inputMessage(client, conversationId, text, from, fromName);
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public inputMessage(client, conversationId, text, from, fromName) {
|
||||||
|
client.Conversations.Conversations_PostActivity({
|
||||||
|
conversationId: conversationId,
|
||||||
|
activity: {
|
||||||
|
textFormat: 'plain',
|
||||||
|
text: text,
|
||||||
|
type: 'message',
|
||||||
|
from: {
|
||||||
|
id: from,
|
||||||
|
name: fromName
|
||||||
|
},
|
||||||
|
replyToId: from
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
GBLog.error(`GBWhatsapp: Error receiving message: ${err}.`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public pollMessages(client, conversationId, from, fromName) {
|
||||||
|
GBLog.info(`GBWhatsapp: Starting polling message for conversationId:
|
||||||
|
${conversationId}.`);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
client.Conversations.Conversations_GetActivities({
|
||||||
|
conversationId: conversationId,
|
||||||
|
watermark: this.watermark
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.watermark = response.obj.watermark;
|
||||||
|
|
||||||
|
return response.obj.activities;
|
||||||
|
})
|
||||||
|
.then(activities => {
|
||||||
|
this.printMessages(activities, conversationId, from, fromName);
|
||||||
|
});
|
||||||
|
}, this.pollInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public printMessages(activities, conversationId, from, fromName) {
|
||||||
|
if (activities && activities.length) {
|
||||||
|
// Ignore own messages.
|
||||||
|
|
||||||
|
activities = activities.filter(m => m.from.id === 'GeneralBots' && m.type === 'message');
|
||||||
|
|
||||||
|
if (activities.length) {
|
||||||
|
// Print other messages.
|
||||||
|
|
||||||
|
activities.forEach(activity => {
|
||||||
|
this.printMessage(activity, conversationId, from, fromName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public printMessage(activity, conversationId, from, fromName) {
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
if (activity.text) {
|
||||||
|
GBLog.info(`GBWhatsapp: MSG: ${activity.text}`);
|
||||||
|
output = activity.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.attachments) {
|
||||||
|
activity.attachments.forEach(attachment => {
|
||||||
|
switch (attachment.contentType) {
|
||||||
|
case 'application/vnd.microsoft.card.hero':
|
||||||
|
output += `\n${this.renderHeroCard(attachment)}`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'image/png':
|
||||||
|
GBLog.info(`Opening the requested image ${attachment.contentUrl}`);
|
||||||
|
output += `\n${attachment.contentUrl}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GBLog.info(`Unknown content type: ${attachment.contentType}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendToDevice(from, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderHeroCard(attachment) {
|
||||||
|
return `${attachment.content.title} - ${attachment.content.text}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendToDevice(to, msg) {
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
url: urlJoin(this.whatsappServiceUrl, 'message'),
|
||||||
|
qs: {
|
||||||
|
token: this.whatsappServiceKey,
|
||||||
|
phone: to,
|
||||||
|
body: msg
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'cache-control': 'no-cache'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
53
src/app.ts
53
src/app.ts
|
@ -3,7 +3,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,23 +37,20 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const logger = require('./logger');
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
import { IGBInstance, IGBPackage } from 'botlib';
|
|
||||||
|
import { GBLog, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
||||||
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService';
|
||||||
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService';
|
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
import { GuaribasInstance } from '../packages/core.gbapp/models/GBModel';
|
|
||||||
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService';
|
||||||
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService';
|
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService';
|
||||||
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService';
|
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService';
|
||||||
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
|
||||||
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService';
|
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService';
|
||||||
import { GBMinService } from '../packages/core.gbapp/services/GBMinService';
|
import { GBMinService } from '../packages/core.gbapp/services/GBMinService';
|
||||||
import { GBVMService } from '../packages/core.gbapp/services/GBVMService';
|
|
||||||
import { load } from 'dotenv';
|
|
||||||
|
|
||||||
const appPackages = new Array<IGBPackage>();
|
const appPackages: IGBPackage[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General Bots open-core entry point.
|
* General Bots open-core entry point.
|
||||||
|
@ -64,33 +61,29 @@ export class GBServer {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static run() {
|
public static run() {
|
||||||
logger.info(`The Bot Server is in STARTING mode...`);
|
GBLog.info(`The Bot Server is in STARTING mode...`);
|
||||||
|
|
||||||
// Creates a basic HTTP server that will serve several URL, one for each
|
// Creates a basic HTTP server that will serve several URL, one for each
|
||||||
// bot instance. This allows the same server to attend multiple Bot on
|
// bot instance.
|
||||||
// the Marketplace until GB get serverless.
|
|
||||||
|
|
||||||
const port = process.env.port || process.env.PORT || 4242;
|
const port = GBConfigService.getServerPort();
|
||||||
const server = express();
|
const server = express();
|
||||||
|
server.use(bodyParser.json());
|
||||||
server.use(bodyParser.json()); // to support JSON-encoded bodies
|
|
||||||
server.use(
|
server.use(
|
||||||
bodyParser.urlencoded({
|
bodyParser.urlencoded({
|
||||||
// to support URL-encoded bodies
|
|
||||||
extended: true
|
extended: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let bootInstance: IGBInstance;
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
logger.info(`Now accepting connections on ${port}...`);
|
GBLog.info(`Now accepting connections on ${port}...`);
|
||||||
|
|
||||||
// Reads basic configuration, initialize minimal services.
|
// Reads basic configuration, initialize minimal services.
|
||||||
|
|
||||||
GBConfigService.init();
|
GBConfigService.init();
|
||||||
const core = new GBCoreService();
|
const core: IGBCoreService = new GBCoreService();
|
||||||
|
|
||||||
const importer: GBImporter = new GBImporter(core);
|
const importer: GBImporter = new GBImporter(core);
|
||||||
const deployer: GBDeployer = new GBDeployer(core, importer);
|
const deployer: GBDeployer = new GBDeployer(core, importer);
|
||||||
|
@ -100,15 +93,16 @@ export class GBServer {
|
||||||
|
|
||||||
// Ensure that local development proxy is setup.
|
// Ensure that local development proxy is setup.
|
||||||
|
|
||||||
logger.info(`Establishing a development local proxy (ngrok)...`);
|
GBLog.info(`Establishing a development local proxy (ngrok)...`);
|
||||||
const proxyAddress: string = await core.ensureProxy(port);
|
const proxyAddress: string = await core.ensureProxy(port);
|
||||||
|
|
||||||
// Creates a boot instance or load it frmo storage.
|
// Creates a boot instance or load it from storage.
|
||||||
|
|
||||||
let bootInstance: IGBInstance = null;
|
let bootInstance: IGBInstance;
|
||||||
try {
|
try {
|
||||||
await core.initStorage();
|
await core.initStorage();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
GBLog.verbose(`Error initializing storage: ${error}`);
|
||||||
bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress);
|
bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress);
|
||||||
await core.initStorage();
|
await core.initStorage();
|
||||||
}
|
}
|
||||||
|
@ -117,24 +111,28 @@ export class GBServer {
|
||||||
|
|
||||||
// Deploys system and user packages.
|
// Deploys system and user packages.
|
||||||
|
|
||||||
logger.info(`Deploying packages...`);
|
GBLog.info(`Deploying packages...`);
|
||||||
core.loadSysPackages(core);
|
core.loadSysPackages(core);
|
||||||
await core.checkStorage(azureDeployer);
|
await core.checkStorage(azureDeployer);
|
||||||
await deployer.deployPackages(core, server, appPackages);
|
await deployer.deployPackages(core, server, appPackages);
|
||||||
|
|
||||||
// Loads all bot instances.
|
// Loads all bot instances.
|
||||||
|
|
||||||
logger.info(`Publishing instances...`);
|
GBLog.info(`Publishing instances...`);
|
||||||
const packageInstance = await importer.importIfNotExistsBotPackage(
|
const packageInstance = await importer.importIfNotExistsBotPackage(
|
||||||
GBConfigService.get('CLOUD_GROUP'),
|
GBConfigService.get('CLOUD_GROUP'),
|
||||||
'boot.gbot',
|
'boot.gbot',
|
||||||
'packages/boot.gbot'
|
'packages/boot.gbot'
|
||||||
);
|
);
|
||||||
|
if (bootInstance === undefined) {
|
||||||
|
bootInstance = packageInstance;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line:prefer-object-spread
|
||||||
const fullInstance = Object.assign(packageInstance, bootInstance);
|
const fullInstance = Object.assign(packageInstance, bootInstance);
|
||||||
await core.saveInstance(fullInstance);
|
await core.saveInstance(fullInstance);
|
||||||
let instances: GuaribasInstance[] = await core.loadAllInstances(core, azureDeployer, proxyAddress);
|
let instances: IGBInstance[] = await core.loadAllInstances(core, azureDeployer, proxyAddress);
|
||||||
instances = await core.ensureInstances(instances, bootInstance, core);
|
instances = await core.ensureInstances(instances, bootInstance, core);
|
||||||
if (!bootInstance) {
|
if (bootInstance !== undefined) {
|
||||||
bootInstance = instances[0];
|
bootInstance = instances[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,16 +143,15 @@ export class GBServer {
|
||||||
|
|
||||||
// Deployment of local applications for the first time.
|
// Deployment of local applications for the first time.
|
||||||
|
|
||||||
deployer.installDefaultGBUI();
|
deployer.runOnce();
|
||||||
|
|
||||||
logger.info(`The Bot Server is in RUNNING mode...`);
|
GBLog.info(`The Bot Server is in RUNNING mode...`);
|
||||||
|
|
||||||
// Opens Navigator.
|
// Opens Navigator.
|
||||||
|
|
||||||
core.openBrowserInDevelopment();
|
core.openBrowserInDevelopment();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
GBLog.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"*": ["types/*"],
|
||||||
"botlib/*": ["node_modules/botlib/*"],
|
"botlib/*": ["node_modules/botlib/*"],
|
||||||
"pragmatismo-io-framework/*": ["node_modules/pragmatismo-io-framework/*"]
|
"pragmatismo-io-framework/*": ["node_modules/pragmatismo-io-framework/*"]
|
||||||
},
|
},
|
||||||
|
|
82
tslint.json
82
tslint.json
|
@ -1,43 +1,43 @@
|
||||||
{
|
{
|
||||||
"defaultSeverity": "warning",
|
"defaultSeverity": "warning",
|
||||||
"extends": [
|
"extends": ["tslint:recommended", "tslint-microsoft-contrib"],
|
||||||
"tslint:recommended",
|
"linterOptions": {
|
||||||
"tslint-microsoft-contrib"
|
"exclude": [
|
||||||
],
|
"libraries/botframework-connector/src/generated/**/*",
|
||||||
"linterOptions": {
|
"libraries/botframework-schema/**/*",
|
||||||
"exclude":[
|
"./packages/default.gbui/**/*",
|
||||||
"libraries/botframework-connector/src/generated/**/*",
|
"./packages/**/*.gbdialog"
|
||||||
"libraries/botframework-schema/**/*"
|
]
|
||||||
]
|
},
|
||||||
},
|
"rulesDirectory": ["node_modules/tslint-microsoft-contrib"],
|
||||||
"rulesDirectory": [
|
"jsRules": {},
|
||||||
"node_modules/tslint-microsoft-contrib"
|
"rules": {
|
||||||
],
|
"newline-per-chained-call": false,
|
||||||
"jsRules": {},
|
"no-unsafe-any": false,
|
||||||
"rules": {
|
"no-floating-promises": false,
|
||||||
"no-floating-promises": false,
|
"no-var-requires": false,
|
||||||
"no-var-requires":false,
|
"typedef": false,
|
||||||
"typedef":false,
|
"variable-name": false,
|
||||||
"variable-name": false,
|
"no-parameter-properties": false,
|
||||||
"no-parameter-properties": false,
|
"max-line-length": [true, { "limit": 120, "ignore-pattern": "^\\s+\\*" }],
|
||||||
"no-reserved-keywords": false,
|
"await-promise": [true, "Bluebird"],
|
||||||
"no-unnecessary-class":false,
|
"no-reserved-keywords": false,
|
||||||
"no-require-imports": false,
|
"no-unnecessary-class": false,
|
||||||
"function-name": false,
|
"no-require-imports": false,
|
||||||
"no-redundant-jsdoc": false,
|
"function-name": false,
|
||||||
"no-return-await": false,
|
"no-relative-imports": false,
|
||||||
"prefer-type-cast": false,
|
"no-redundant-jsdoc": false,
|
||||||
"no-object-literal-type-assertion":false,
|
"no-return-await": false,
|
||||||
"no-increment-decrement":false,
|
"prefer-type-cast": false,
|
||||||
"no-any":false,
|
"non-literal-fs-path": false,
|
||||||
"interface-name":false,
|
"no-object-literal-type-assertion": false,
|
||||||
"no-this-assignment":false,
|
"no-increment-decrement": false,
|
||||||
"switch-final-break":false,
|
"no-any": false,
|
||||||
"no-parameter-reassignment":false,
|
"interface-name": false,
|
||||||
"export-name":false,
|
"no-this-assignment": false,
|
||||||
"no-relative-imports": false,
|
"switch-final-break": false,
|
||||||
"no-backbone-get-set-outside-model": false,
|
"no-parameter-reassignment": false,
|
||||||
"max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}],
|
"export-name": false,
|
||||||
"await-promise": [true, "Bluebird"]
|
"no-backbone-get-set-outside-model": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue