Merge pull request #90 from rodrigorodriguez/master
fix(general): tslint being applied in all sources.
This commit is contained in:
commit
528e0a90eb
70 changed files with 2046 additions and 2227 deletions
|
@ -5,4 +5,5 @@
|
|||
"arrowParens": "avoid",
|
||||
"semi": 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": {
|
||||
"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-server": "tsc",
|
||||
"build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build",
|
||||
|
@ -38,7 +38,6 @@
|
|||
"ban": "ban",
|
||||
"issues": "git-issues",
|
||||
"license": "license-checker --production --onlyunknown --csv",
|
||||
"pretslint": "npm run pretty",
|
||||
"pretty": "prettier-standard 'src/*.ts' 'packages/**/*.ts'",
|
||||
"secure": "nsp check",
|
||||
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
|
||||
|
@ -48,7 +47,7 @@
|
|||
"commit": "git-cz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/microsoft-graph-client": "1.4.0",
|
||||
"@microsoft/microsoft-graph-client": "1.5.2",
|
||||
"@semantic-release/exec": "^3.3.2",
|
||||
"adal-node": "0.1.28",
|
||||
"async": "2.6.2",
|
||||
|
@ -58,36 +57,37 @@
|
|||
"azure-arm-search": "^1.3.0-preview",
|
||||
"azure-arm-sql": "5.6.0",
|
||||
"azure-arm-website": "5.7.0",
|
||||
"bluebird": "^3.5.3",
|
||||
"bluebird": "^3.5.4",
|
||||
"body-parser": "1.18.3",
|
||||
"botbuilder": "4.1.7",
|
||||
"botbuilder-ai": "4.2.0",
|
||||
"botbuilder-azure": "4.2.0",
|
||||
"botbuilder": "4.3.4",
|
||||
"botbuilder-ai": "4.3.4",
|
||||
"botbuilder-azure": "4.3.4",
|
||||
"botbuilder-choices": "4.0.0-preview1.2",
|
||||
"botbuilder-dialogs": "4.2.0",
|
||||
"botbuilder-dialogs": "4.3.4",
|
||||
"botbuilder-prompts": "4.0.0-preview1.2",
|
||||
"botlib": "0.1.19",
|
||||
"botlib": "0.1.24",
|
||||
"chai": "4.2.0",
|
||||
"child_process": "^1.0.2",
|
||||
"chokidar": "2.1.2",
|
||||
"cli-spinner": "^0.2.8",
|
||||
"csv-parse": "4.3.3",
|
||||
"dotenv-extended": "2.3.0",
|
||||
"chokidar": "2.1.5",
|
||||
"cli-spinner": "^0.2.10",
|
||||
"csv-parse": "4.3.4",
|
||||
"dotenv-extended": "2.4.0",
|
||||
"express": "4.16.4",
|
||||
"express-promise-router": "3.0.3",
|
||||
"fs-extra": "7.0.1",
|
||||
"ip": "^1.1.5",
|
||||
"js-beautify": "^1.8.9",
|
||||
"js-beautify": "^1.9.1",
|
||||
"localize": "0.4.7",
|
||||
"marked": "0.6.1",
|
||||
"marked": "0.6.2",
|
||||
"mocha": "6.0.2",
|
||||
"mocha-typescript": "1.1.17",
|
||||
"ms": "2.1.1",
|
||||
"ms-rest-azure": "2.6.0",
|
||||
"ms-rest-js": "^1.0.1",
|
||||
"nexmo": "2.4.1",
|
||||
"ngrok": "3.1.1",
|
||||
"nyc": "13.3.0",
|
||||
"opn": "5.4.0",
|
||||
"opn": "6.0.0",
|
||||
"pragmatismo-io-framework": "1.0.19",
|
||||
"process-exists": "3.1.0",
|
||||
"public-ip": "^3.0.0",
|
||||
|
@ -95,20 +95,20 @@
|
|||
"request-promise": "4.2.4",
|
||||
"request-promise-native": "1.0.7",
|
||||
"scanf": "^1.0.2",
|
||||
"sequelize": "4.42.1",
|
||||
"sequelize-typescript": "0.6.7",
|
||||
"sequelize": "^5.2.12",
|
||||
"sequelize-typescript": "0.6.9",
|
||||
"shx": "0.3.2",
|
||||
"simple-git": "1.107.0",
|
||||
"simple-git": "1.110.0",
|
||||
"sqlite3": "4.0.6",
|
||||
"strict-password-generator": "^1.1.2",
|
||||
"swagger-client": "3.8.25",
|
||||
"tedious": "5.0.3",
|
||||
"tedious": "6.1.0",
|
||||
"temperature-js": "^0.1.0",
|
||||
"ts-node": "8.0.2",
|
||||
"ts-node": "8.0.3",
|
||||
"typedoc": "0.14.2",
|
||||
"typedoc-plugin-external-module-name": "^2.0.0",
|
||||
"typedoc-plugin-markdown": "^1.1.27",
|
||||
"typescript": "3.3.3333",
|
||||
"typescript": "3.4.1",
|
||||
"url-join": "4.0.0",
|
||||
"vbscript-to-typescript": "^1.0.8",
|
||||
"wait-until": "0.0.2",
|
||||
|
@ -124,7 +124,7 @@
|
|||
"@semantic-release/release-notes-generator": "^7.1.4",
|
||||
"@types/chai": "4.1.7",
|
||||
"@types/mocha": "5.2.6",
|
||||
"@types/sequelize": "4.27.38",
|
||||
"@types/sequelize": "4.27.46",
|
||||
"@types/url-join": "4.0.0",
|
||||
"@types/winston": "2.4.4",
|
||||
"ban-sensitive-files": "1.9.2",
|
||||
|
@ -142,8 +142,8 @@
|
|||
"standard": "12.0.1",
|
||||
"travis-deploy-once": "5.0.11",
|
||||
"ts-loader": "^5.3.3",
|
||||
"tslint": "^5.13.0",
|
||||
"tslint-microsoft-contrib": "^6.0.0"
|
||||
"tslint": "^5.15.0",
|
||||
"tslint-microsoft-contrib": "^6.1.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"env": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,28 +36,24 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
import { BotAdapter } from 'botbuilder';
|
||||
import { WaterfallDialog, WaterfallStep, WaterfallStepContext } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
import urlJoin = require('url-join');
|
||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||
import { GBImporter } from '../../core.gbapp/services/GBImporterService';
|
||||
import { GBAdminService } from '../services/GBAdminService';
|
||||
import { Messages } from '../strings';
|
||||
|
||||
/**
|
||||
* Dialogs for administration tasks.
|
||||
*/
|
||||
export class AdminDialog extends IGBDialog {
|
||||
public static async createFarmCommand(text: any, min: GBMinInstance) {}
|
||||
|
||||
public static async undeployPackageCommand(text: any, min: GBMinInstance) {
|
||||
public static async undeployPamand(text: any, min: GBMinInstance) {
|
||||
const packageName = text.split(' ')[1];
|
||||
const importer = new GBImporter(min.core);
|
||||
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) {
|
||||
|
@ -67,26 +63,20 @@ export class AdminDialog extends IGBDialog {
|
|||
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
||||
const packageName = text.split(' ')[1];
|
||||
|
||||
if (AdminDialog.isSharePointPath(packageName)) {
|
||||
await deployer.deployFromSharePoint(min.instance.instanceId, packageName);
|
||||
} else {
|
||||
if (!AdminDialog.isSharePointPath(packageName)) {
|
||||
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.');
|
||||
}
|
||||
await deployer.deployPackageFromLocalPath(min, UrlJoin(additionalPath, packageName));
|
||||
await deployer.deployPackage(min, urlJoin(additionalPath, packageName));
|
||||
}
|
||||
}
|
||||
|
||||
public static async rebuildIndexPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
||||
await deployer.rebuildIndex(min.instance);
|
||||
}
|
||||
|
||||
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
|
||||
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
|
||||
await deployer.rebuildIndex(
|
||||
min.instance,
|
||||
new AzureDeployerService(deployer).getKBSearchSchema(min.instance.searchIndex)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,7 +85,7 @@ export class AdminDialog extends IGBDialog {
|
|||
* @param bot The bot adapter.
|
||||
* @param min The minimal bot instance data.
|
||||
*/
|
||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
||||
public static setup(min: GBMinInstance) {
|
||||
// Setup services.
|
||||
|
||||
const importer = new GBImporter(min.core);
|
||||
|
@ -113,9 +103,9 @@ export class AdminDialog extends IGBDialog {
|
|||
},
|
||||
async step => {
|
||||
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);
|
||||
|
||||
return await step.prompt('textPrompt', Messages[locale].which_task);
|
||||
|
@ -126,8 +116,9 @@ export class AdminDialog extends IGBDialog {
|
|||
}
|
||||
},
|
||||
async step => {
|
||||
const locale = step.context.activity.locale;
|
||||
const text = step.result;
|
||||
const locale: string = step.context.activity.locale;
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
const text: string = step.result;
|
||||
const cmdName = text.split(' ')[0];
|
||||
|
||||
step.context.sendActivity(Messages[locale].working(cmdName));
|
||||
|
@ -135,28 +126,16 @@ export class AdminDialog extends IGBDialog {
|
|||
|
||||
if (text === 'quit') {
|
||||
return await step.replaceDialog('/');
|
||||
} else if (cmdName === 'createFarm') {
|
||||
await AdminDialog.createFarmCommand(text, deployer);
|
||||
|
||||
return await step.replaceDialog('/admin', { firstRun: false });
|
||||
} else if (cmdName === 'deployPackage') {
|
||||
await AdminDialog.deployPackageCommand(min, text, deployer);
|
||||
|
||||
return await step.replaceDialog('/admin', { firstRun: false });
|
||||
} else if (cmdName === 'redeployPackage') {
|
||||
await AdminDialog.undeployPackageCommand(text, min);
|
||||
await AdminDialog.deployPackageCommand(min, text, deployer);
|
||||
|
||||
return await step.replaceDialog('/admin', { firstRun: false });
|
||||
} else if (cmdName === 'rebuildIndex') {
|
||||
await AdminDialog.rebuildIndexPackageCommand(min, text, 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);
|
||||
await AdminDialog.rebuildIndexPackageCommand(min, deployer);
|
||||
|
||||
return await step.replaceDialog('/admin', { firstRun: false });
|
||||
} else if (cmdName === 'setupSecurity') {
|
||||
|
@ -220,13 +199,13 @@ export class AdminDialog extends IGBDialog {
|
|||
);
|
||||
|
||||
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=${
|
||||
min.instance.authenticatorClientId
|
||||
}&response_type=code&redirect_uri=${UrlJoin(
|
||||
const url = `https://login.microsoftonline.com/${
|
||||
min.instance.authenticatorTenant
|
||||
}/oauth2/authorize?client_id=${min.instance.authenticatorClientId}&response_type=code&redirect_uri=${urlJoin(
|
||||
min.instance.botEndpoint,
|
||||
min.instance.botId,
|
||||
'/token'
|
||||
|
@ -234,10 +213,9 @@ export class AdminDialog extends IGBDialog {
|
|||
|
||||
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';
|
||||
|
||||
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 { AdminDialog } from './dialogs/AdminDialog';
|
||||
import { GuaribasAdmin } from './models/AdminModel';
|
||||
|
||||
/**
|
||||
* The package for admin.gbapp.
|
||||
*/
|
||||
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 {
|
||||
core.sequelize.addModels([GuaribasAdmin]);
|
||||
}
|
||||
|
||||
public loadBot(min: GBMinInstance): void {
|
||||
AdminDialog.setup(min.bot, min);
|
||||
AdminDialog.setup(min);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -39,12 +39,15 @@
|
|||
import {
|
||||
Column,
|
||||
CreatedAt,
|
||||
DataType,
|
||||
Model,
|
||||
Table,
|
||||
UpdatedAt,
|
||||
DataType
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript';
|
||||
|
||||
/**
|
||||
* General settings store.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasAdmin extends Model<GuaribasAdmin> {
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -37,17 +37,17 @@
|
|||
'use strict';
|
||||
|
||||
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 { GuaribasAdmin } from '../models/AdminModel';
|
||||
const UrlJoin = require('url-join');
|
||||
const msRestAzure = require('ms-rest-azure');
|
||||
const PasswordGenerator = require('strict-password-generator').default;
|
||||
|
||||
/**
|
||||
* Services for server administration.
|
||||
*/
|
||||
export class GBAdminService {
|
||||
export class GBAdminService implements IGBAdminService {
|
||||
public static GB_PROMPT: string = 'GeneralBots: ';
|
||||
public static masterBotInstanceId = 0;
|
||||
|
||||
|
@ -65,18 +65,15 @@ export class GBAdminService {
|
|||
|
||||
public static async getADALTokenFromUsername(username: string, password: string) {
|
||||
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) {
|
||||
const credentials = await msRestAzure.loginWithUsernamePassword(username, password);
|
||||
|
||||
return credentials;
|
||||
return await msRestAzure.loginWithUsernamePassword(username, password);
|
||||
}
|
||||
|
||||
public static getRndPassword() {
|
||||
public static getRndPassword(): string {
|
||||
const passwordGenerator = new PasswordGenerator();
|
||||
const options = {
|
||||
upperCaseAlpha: true,
|
||||
|
@ -88,6 +85,7 @@ export class GBAdminService {
|
|||
};
|
||||
let password = passwordGenerator.generatePassword(options);
|
||||
password = password.replace(/[\@\[\=\:\;\?]/g, '#');
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
|
@ -101,11 +99,11 @@ export class GBAdminService {
|
|||
minimumLength: 12,
|
||||
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: {} };
|
||||
options.where = { key: key };
|
||||
let admin = await GuaribasAdmin.findOne(options);
|
||||
|
@ -115,8 +113,7 @@ export class GBAdminService {
|
|||
}
|
||||
admin.value = value;
|
||||
admin.instanceId = instanceId;
|
||||
|
||||
return admin.save();
|
||||
await admin.save();
|
||||
}
|
||||
|
||||
public async updateSecurityInfo(
|
||||
|
@ -125,7 +122,7 @@ export class GBAdminService {
|
|||
authenticatorAuthorityHostUrl: string,
|
||||
authenticatorClientId: string,
|
||||
authenticatorClientSecret: string
|
||||
): Promise<GuaribasInstance> {
|
||||
): Promise<IGBInstance> {
|
||||
const options = { where: {} };
|
||||
options.where = { instanceId: instanceId };
|
||||
const item = await GuaribasInstance.findOne(options);
|
||||
|
@ -137,7 +134,7 @@ export class GBAdminService {
|
|||
return item.save();
|
||||
}
|
||||
|
||||
public async getValue(instanceId: number, key: string) {
|
||||
public async getValue(instanceId: number, key: string): Promise<string> {
|
||||
const options = { where: {} };
|
||||
options.where = { key: key, instanceId: instanceId };
|
||||
const obj = await GuaribasAdmin.findOne(options);
|
||||
|
@ -154,7 +151,7 @@ export class GBAdminService {
|
|||
const accessToken = await this.getValue(instanceId, 'accessToken');
|
||||
resolve(accessToken);
|
||||
} else {
|
||||
const authorizationUrl = UrlJoin(
|
||||
const authorizationUrl = urlJoin(
|
||||
instance.authenticatorAuthorityHostUrl,
|
||||
instance.authenticatorTenant,
|
||||
'/oauth2/authorize'
|
||||
|
@ -169,7 +166,7 @@ export class GBAdminService {
|
|||
instance.authenticatorClientSecret,
|
||||
resource,
|
||||
async (err, res) => {
|
||||
if (err) {
|
||||
if (err !== undefined) {
|
||||
reject(err);
|
||||
} else {
|
||||
const token = res as TokenResponse;
|
||||
|
|
|
@ -16,7 +16,8 @@ export const Messages = {
|
|||
wrong_password: 'Sorry, wrong password. Please, try again.',
|
||||
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_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:'
|
||||
},
|
||||
'pt-BR': {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,18 +36,30 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
|
||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
|
||||
/**
|
||||
* .gblib Package handler.
|
||||
*/
|
||||
export class GBAnalyticsPackage implements IGBPackage {
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
|
||||
public unloadPackage(core: IGBCoreService): void {}
|
||||
public loadBot(min: GBMinInstance): void {}
|
||||
public unloadBot(min: GBMinInstance): void {}
|
||||
public onNewSession(min: GBMinInstance, step: any): void {}
|
||||
public sysPackages: IGBPackage[];
|
||||
public getDialogs(min: GBMinInstance) {
|
||||
GBLog.verbose(`getDialogs called.`);
|
||||
}
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
GBLog.verbose(`loadPackage called.`);
|
||||
}
|
||||
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';
|
||||
|
||||
import {
|
||||
DataTypeDate,
|
||||
DataTypeDecimal,
|
||||
DataTypes,
|
||||
DataTypeUUIDv4
|
||||
} from 'sequelize';
|
||||
|
||||
import {
|
||||
AutoIncrement,
|
||||
|
@ -65,6 +59,56 @@ import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBMod
|
|||
import { GuaribasSubject } from '../../kb.gbapp/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
|
||||
export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||
|
||||
|
@ -106,45 +150,3 @@ export class GuaribasConversation extends Model<GuaribasConversation> {
|
|||
@BelongsTo(() => 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 { GuaribasConversation, GuaribasConversationMessage } from '../models';
|
||||
|
||||
/**
|
||||
* Base services for Bot Analytics.
|
||||
*/
|
||||
export class AnalyticsService {
|
||||
public async createConversation(
|
||||
user: GuaribasUser
|
||||
|
@ -52,7 +55,7 @@ export class AnalyticsService {
|
|||
});
|
||||
}
|
||||
|
||||
public createMessage(
|
||||
public async createMessage(
|
||||
conversation: GuaribasConversation,
|
||||
user: GuaribasUser,
|
||||
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';
|
||||
|
||||
import { IGBInstance } from 'botlib';
|
||||
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
|
||||
import * as fs from 'fs';
|
||||
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||
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');
|
||||
|
||||
/**
|
||||
* Handles command-line dialog for getting info for Boot Bot.
|
||||
*/
|
||||
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.
|
||||
|
||||
if (!fs.existsSync(`.env`)) {
|
||||
|
@ -75,7 +73,7 @@ export class StartDialog {
|
|||
// Connects to the cloud and retrieves subscriptions.
|
||||
|
||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||
const list = await AzureDeployerService.getSubscriptions(credentials);
|
||||
const list = await installationDeployer.getSubscriptions(credentials);
|
||||
|
||||
let subscriptionId: string;
|
||||
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...`);
|
||||
|
||||
|
||||
// Prepares the first instance on bot farm.
|
||||
const instance: IGBInstance = {};
|
||||
const instance = <IGBInstance>{};
|
||||
|
||||
instance.botId = botId;
|
||||
instance.cloudUsername = username;
|
||||
|
@ -123,7 +120,7 @@ export class StartDialog {
|
|||
|
||||
private static retrieveUsername() {
|
||||
let value = GBConfigService.get('CLOUD_USERNAME');
|
||||
if (!value) {
|
||||
if (value === undefined) {
|
||||
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_USERNAME:`);
|
||||
value = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||
}
|
||||
|
@ -133,25 +130,23 @@ export class StartDialog {
|
|||
|
||||
private static retrievePassword() {
|
||||
let password = GBConfigService.get('CLOUD_PASSWORD');
|
||||
if (!password) {
|
||||
if (password === undefined) {
|
||||
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_PASSWORD:`);
|
||||
password = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
private static retrieveBotId() {
|
||||
let botId = GBConfigService.get('BOT_ID');
|
||||
if (!botId) {
|
||||
if (botId === undefined) {
|
||||
process.stdout.write(
|
||||
`${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),
|
||||
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:`);
|
||||
|
||||
// TODO: Update this regexp to match description of it.
|
||||
|
||||
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() {
|
||||
let authoringKey = GBConfigService.get('NLP_AUTHORING_KEY');
|
||||
if (!authoringKey) {
|
||||
if (authoringKey === undefined) {
|
||||
process.stdout.write(
|
||||
`${
|
||||
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() {
|
||||
let appId = GBConfigService.get('MARKETPLACE_ID');
|
||||
process.stdout.write(
|
||||
`Sorry, this part cannot be automated yet due to Microsoft schedule,
|
||||
if (appId === undefined) {
|
||||
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
|
||||
generate manually an App ID and App Secret.\n`
|
||||
);
|
||||
if (!appId) {
|
||||
);
|
||||
process.stdout.write('Generated Application Id (MARKETPLACE_ID):');
|
||||
appId = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||
}
|
||||
|
@ -194,7 +189,7 @@ generate manually an App ID and App Secret.\n`
|
|||
|
||||
private static retrieveAppPassword() {
|
||||
let appPassword = GBConfigService.get('MARKETPLACE_SECRET');
|
||||
if (!appPassword) {
|
||||
if (appPassword === undefined) {
|
||||
process.stdout.write('Generated Password (MARKETPLACE_SECRET):');
|
||||
appPassword = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||
}
|
||||
|
@ -207,7 +202,7 @@ generate manually an App ID and App Secret.\n`
|
|||
const map = {};
|
||||
let index = 1;
|
||||
list.forEach(element => {
|
||||
console.log(`${index}: ${element.displayName} (${element.subscriptionId})`);
|
||||
GBLog.info(`${index}: ${element.displayName} (${element.subscriptionId})`);
|
||||
map[index++] = element;
|
||||
});
|
||||
let subscriptionIndex;
|
||||
|
@ -222,8 +217,8 @@ generate manually an App ID and App Secret.\n`
|
|||
|
||||
private static retrieveLocation() {
|
||||
let location = GBConfigService.get('CLOUD_LOCATION');
|
||||
if (!location) {
|
||||
process.stdout.write("CLOUD_LOCATION (eg. 'westus'):");
|
||||
if (location === undefined) {
|
||||
process.stdout.write('CLOUD_LOCATION (eg. westus):');
|
||||
location = scanf('%s');
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,19 +36,30 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
|
||||
/**
|
||||
* Package for Azure Deployer.
|
||||
*/
|
||||
export class GBAzureDeployerPackage implements IGBPackage {
|
||||
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
|
||||
|
||||
public unloadPackage(core: IGBCoreService): void {}
|
||||
|
||||
public loadBot(min: GBMinInstance): void {}
|
||||
|
||||
public unloadBot(min: GBMinInstance): void {}
|
||||
public onNewSession(min: GBMinInstance, step: any): void {}
|
||||
public sysPackages: IGBPackage[];
|
||||
public getDialogs(min: GBMinInstance) {
|
||||
GBLog.verbose(`getDialogs called.`);
|
||||
}
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
GBLog.verbose(`loadPackage called.`);
|
||||
}
|
||||
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 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -37,39 +37,40 @@
|
|||
'use strict';
|
||||
|
||||
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
||||
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
||||
import { ResourceManagementClient, SubscriptionClient } from 'azure-arm-resource';
|
||||
import { SearchManagementClient } from 'azure-arm-search';
|
||||
import { SqlManagementClient } from 'azure-arm-sql';
|
||||
import { WebSiteManagementClient } from 'azure-arm-website';
|
||||
//tslint:disable-next-line:no-submodule-imports
|
||||
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
||||
import { GBService, IGBInstance } from 'botlib';
|
||||
import { HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
||||
import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer';
|
||||
import * as simplegit from 'simple-git/promise';
|
||||
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
|
||||
import { HttpHeaders, HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
||||
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||
import { GBCorePackage } from '../../../packages/core.gbapp';
|
||||
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 scanf = require('scanf');
|
||||
const git = simplegit();
|
||||
const logger = require('../../../src/logger');
|
||||
const UrlJoin = require('url-join');
|
||||
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
||||
import urlJoin = require('url-join');
|
||||
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
|
||||
const publicIp = require('public-ip');
|
||||
|
||||
export class AzureDeployerService extends GBService {
|
||||
public static apiVersion = '2017-12-01';
|
||||
public static defaultEndPoint = 'http://localhost:4242';
|
||||
/**
|
||||
* Deployer for Microsoft cloud.
|
||||
*/
|
||||
export class AzureDeployerService implements IGBInstallationDeployer {
|
||||
public apiVersion = '2017-12-01';
|
||||
public defaultEndPoint = 'http://localhost:4242';
|
||||
public instance: IGBInstance;
|
||||
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
||||
public webSiteClient: WebSiteManagementClient;
|
||||
public storageClient: SqlManagementClient;
|
||||
public cognitiveClient: CognitiveServicesManagementClient;
|
||||
public searchClient: SearchManagementClient;
|
||||
public static provider = 'Microsoft.BotService';
|
||||
public provider = 'Microsoft.BotService';
|
||||
public subscriptionClient: SubscriptionClient.SubscriptionClient;
|
||||
public accessToken: string;
|
||||
public location: string;
|
||||
|
@ -78,15 +79,28 @@ export class AzureDeployerService extends GBService {
|
|||
public deployer: GBDeployer;
|
||||
|
||||
constructor(deployer: GBDeployer) {
|
||||
super();
|
||||
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);
|
||||
|
||||
return subscriptionClient.subscriptions.list();
|
||||
}
|
||||
|
||||
public static getKBSearchSchema(indexName) {
|
||||
public getKBSearchSchema(indexName) {
|
||||
return {
|
||||
name: indexName,
|
||||
fields: [
|
||||
|
@ -182,21 +196,18 @@ export class AzureDeployerService extends GBService {
|
|||
}
|
||||
],
|
||||
scoringProfiles: [],
|
||||
defaultScoringProfile: null,
|
||||
corsOptions: null
|
||||
defaultScoringProfile: undefined,
|
||||
corsOptions: undefined
|
||||
};
|
||||
}
|
||||
|
||||
public static async updateBotProxy(botId, group, endpoint) {
|
||||
public async updateBotProxy(botId, group, endpoint) {
|
||||
const baseUrl = `https://management.azure.com/`;
|
||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||
|
||||
const accessToken = await GBAdminService.getADALTokenFromUsername(
|
||||
username,
|
||||
password
|
||||
);
|
||||
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
|
||||
const httpClient = new ServiceClient();
|
||||
|
||||
const parameters = {
|
||||
|
@ -207,22 +218,18 @@ export class AzureDeployerService extends GBService {
|
|||
|
||||
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
||||
this.provider
|
||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
||||
const url = UrlJoin(baseUrl, query);
|
||||
const req = this.createRequestObject(
|
||||
url,
|
||||
accessToken,
|
||||
'PATCH',
|
||||
JSON.stringify(parameters)
|
||||
);
|
||||
}/botServices/${botId}?api-version=${this.apiVersion}`;
|
||||
const url = urlJoin(baseUrl, query);
|
||||
const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
|
||||
const res = await httpClient.sendRequest(req);
|
||||
if (!(res.bodyAsJson as any).id) {
|
||||
// CHECK
|
||||
if (!JSON.parse(res.bodyAsText).id) {
|
||||
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 password = GBConfigService.get('CLOUD_PASSWORD');
|
||||
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||
|
@ -239,19 +246,6 @@ export class AzureDeployerService extends GBService {
|
|||
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(
|
||||
proxyAddress: string,
|
||||
instance: IGBInstance,
|
||||
|
@ -261,22 +255,20 @@ export class AzureDeployerService extends GBService {
|
|||
const culture = 'en-us';
|
||||
|
||||
this.initServices(credentials, subscriptionId);
|
||||
|
||||
const spinner = new Spinner('%s');
|
||||
spinner.start();
|
||||
spinner.setSpinnerString('|/-\\');
|
||||
|
||||
let keys: any;
|
||||
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);
|
||||
|
||||
logger.info(`Deploying Bot Server...`);
|
||||
GBLog.info(`Deploying Bot Server...`);
|
||||
const serverFarm = await this.createHostingPlan(name, `${name}-server-plan`, 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 administratorPassword = GBAdminService.getRndPassword();
|
||||
const storageServer = `${name.toLowerCase()}-storage-server`;
|
||||
|
@ -296,7 +288,7 @@ export class AzureDeployerService extends GBService {
|
|||
instance.storageDialect = 'mssql';
|
||||
instance.storageServer = storageServer;
|
||||
|
||||
logger.info(`Deploying Search...`);
|
||||
GBLog.info(`Deploying Search...`);
|
||||
const searchName = `${name}-search`.toLowerCase();
|
||||
await this.createSearch(name, searchName, instance.cloudLocation);
|
||||
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
|
||||
|
@ -304,27 +296,28 @@ export class AzureDeployerService extends GBService {
|
|||
instance.searchIndex = 'azuresql-index';
|
||||
instance.searchIndexer = 'azuresql-indexer';
|
||||
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);
|
||||
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
||||
instance.speechKeyEndpoint = speech.endpoint;
|
||||
instance.speechEndpoint = speech.endpoint;
|
||||
instance.speechKey = keys.key1;
|
||||
|
||||
logger.info(`Deploying SpellChecker...`);
|
||||
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`, instance.cloudLocation);
|
||||
GBLog.info(`Deploying SpellChecker...`);
|
||||
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`);
|
||||
keys = await this.cognitiveClient.accounts.listKeys(name, spellChecker.name);
|
||||
instance.spellCheckerKey = keys.key1;
|
||||
instance.spellCheckerEndpoint = spellChecker.endpoint;
|
||||
instance.spellcheckerKey = keys.key1;
|
||||
instance.spellcheckerEndpoint = spellChecker.endpoint;
|
||||
|
||||
logger.info(`Deploying Text Analytics...`);
|
||||
GBLog.info(`Deploying Text Analytics...`);
|
||||
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
|
||||
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;
|
||||
|
||||
logger.info(`Deploying NLP...`);
|
||||
GBLog.info(`Deploying NLP...`);
|
||||
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
|
||||
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
||||
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.nlpAppId = nlpAppId;
|
||||
|
||||
logger.info(`Deploying Bot...`);
|
||||
instance.botEndpoint = AzureDeployerService.defaultEndPoint;
|
||||
GBLog.info(`Deploying Bot...`);
|
||||
instance.botEndpoint = this.defaultEndPoint;
|
||||
|
||||
instance = await this.internalDeployBot(
|
||||
instance,
|
||||
|
@ -347,27 +340,27 @@ export class AzureDeployerService extends GBService {
|
|||
'global',
|
||||
instance.nlpAppId,
|
||||
instance.nlpKey,
|
||||
instance.appId,
|
||||
instance.appPassword,
|
||||
instance.marketplaceId,
|
||||
instance.marketplacePassword,
|
||||
instance.cloudSubscriptionId
|
||||
);
|
||||
|
||||
spinner.stop();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public async deployToCloud(
|
||||
title,
|
||||
username,
|
||||
password,
|
||||
cloudLocation,
|
||||
authoringKey,
|
||||
appId,
|
||||
appPassword,
|
||||
subscriptionId
|
||||
title: string,
|
||||
username: string,
|
||||
password: string,
|
||||
cloudLocation: string,
|
||||
authoringKey: string,
|
||||
appId: string,
|
||||
appPassword: string,
|
||||
subscriptionId: string
|
||||
) {
|
||||
|
||||
const instance: IGBInstance = {};
|
||||
const instance = <IGBInstance>{};
|
||||
|
||||
instance.botId = title;
|
||||
instance.cloudUsername = username;
|
||||
|
@ -380,10 +373,9 @@ export class AzureDeployerService extends GBService {
|
|||
instance.adminPass = GBAdminService.getRndPassword();
|
||||
|
||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||
this.deployFarm(`http://${instance.botId}.azurewebsites.net`, instance, credentials, subscriptionId);
|
||||
|
||||
// TODO: Copy github to webapp.
|
||||
//const status = await git.status();
|
||||
// tslint:disable-next-line:no-http-string
|
||||
const url = `http://${instance.botId}.azurewebsites.net`;
|
||||
this.deployFarm(url, instance, credentials, subscriptionId);
|
||||
}
|
||||
|
||||
private initServices(credentials: any, subscriptionId: string) {
|
||||
|
@ -395,19 +387,6 @@ export class AzureDeployerService extends GBService {
|
|||
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) {
|
||||
const params = {
|
||||
location: location,
|
||||
|
@ -420,22 +399,16 @@ export class AzureDeployerService extends GBService {
|
|||
}
|
||||
|
||||
private async registerProviders(subscriptionId, baseUrl, accessToken) {
|
||||
const query = `subscriptions/${subscriptionId}/providers/${
|
||||
AzureDeployerService.provider
|
||||
}/register?api-version=2018-02-01`;
|
||||
const requestUrl = UrlJoin(baseUrl, query);
|
||||
const query = `subscriptions/${subscriptionId}/providers/${this.provider}/register?api-version=2018-02-01`;
|
||||
const requestUrl = urlJoin(baseUrl, query);
|
||||
|
||||
const req = new WebResource();
|
||||
req.method = 'POST';
|
||||
req.url = requestUrl;
|
||||
req.headers = {};
|
||||
req.headers = <any>{};
|
||||
req.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||
req.headers['accept-language'] = '*';
|
||||
req.headers.Authorization = 'Bearer ' + accessToken;
|
||||
|
||||
const httpClient = new ServiceClient();
|
||||
const res = await httpClient.sendRequest(req);
|
||||
// TODO: Check res for error.
|
||||
(req.headers as any).Authorization = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -455,7 +428,7 @@ export class AzureDeployerService extends GBService {
|
|||
appId,
|
||||
appPassword,
|
||||
subscriptionId
|
||||
) {
|
||||
): Promise<IGBInstance> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const baseUrl = `https://management.azure.com/`;
|
||||
await this.registerProviders(subscriptionId, baseUrl, accessToken);
|
||||
|
@ -487,25 +460,27 @@ export class AzureDeployerService extends GBService {
|
|||
|
||||
const httpClient = new ServiceClient();
|
||||
let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
||||
AzureDeployerService.provider
|
||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
||||
let url = UrlJoin(baseUrl, query);
|
||||
this.provider
|
||||
}/botServices/${botId}?api-version=${this.apiVersion}`;
|
||||
let url = urlJoin(baseUrl, query);
|
||||
let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters));
|
||||
const res = await httpClient.sendRequest(req);
|
||||
if (!(res.bodyAsJson as any).id) {
|
||||
if (!JSON.parse(res.bodyAsText).id) {
|
||||
reject(res.bodyAsText);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
//tslint:disable-next-line:max-line-length
|
||||
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
|
||||
AzureDeployerService.apiVersion
|
||||
}`;
|
||||
url = UrlJoin(baseUrl, query);
|
||||
req = AzureDeployerService.createRequestObject(url, accessToken, 'GET', JSON.stringify(parameters));
|
||||
this.apiVersion
|
||||
}`;
|
||||
url = urlJoin(baseUrl, query);
|
||||
req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(parameters));
|
||||
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;
|
||||
resolve(instance);
|
||||
} catch (error) {
|
||||
|
@ -529,8 +504,8 @@ export class AzureDeployerService extends GBService {
|
|||
};
|
||||
|
||||
const body = JSON.stringify(parameters);
|
||||
const apps = await this.makeNlpRequest(location, authoringKey, null, 'GET', 'apps');
|
||||
const app = (apps.bodyAsJson as any).filter(x => x.name == name)[0];
|
||||
const apps = await this.makeNlpRequest(location, authoringKey, undefined, 'GET', 'apps');
|
||||
const app = JSON.parse(apps.bodyAsText).filter(x => x.name === name)[0];
|
||||
let id: string;
|
||||
if (!app) {
|
||||
const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps');
|
||||
|
@ -552,10 +527,9 @@ export class AzureDeployerService extends GBService {
|
|||
const req = new WebResource();
|
||||
req.method = method;
|
||||
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
|
||||
req.headers = {};
|
||||
req.headers['Content-Type'] = 'application/json';
|
||||
req.headers['accept-language'] = '*';
|
||||
req.headers['Ocp-Apim-Subscription-Key'] = authoringKey;
|
||||
req.headers.set('Content-Type', 'application/json');
|
||||
req.headers.set('accept-language', '*');
|
||||
req.headers.set('Ocp-Apim-Subscription-Key', authoringKey);
|
||||
req.body = body;
|
||||
const httpClient = new ServiceClient();
|
||||
|
||||
|
@ -601,7 +575,7 @@ export class AzureDeployerService extends GBService {
|
|||
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');
|
||||
}
|
||||
|
||||
|
@ -611,6 +585,7 @@ export class AzureDeployerService extends GBService {
|
|||
|
||||
private async createDeployGroup(name, location) {
|
||||
const params = { location: location };
|
||||
|
||||
return this.resourceClient.resourceGroups.createOrUpdate(name, params);
|
||||
}
|
||||
|
||||
|
@ -633,7 +608,7 @@ export class AzureDeployerService extends GBService {
|
|||
location: location,
|
||||
serverFarmId: farmId
|
||||
};
|
||||
|
||||
return this.webSiteClient.webApps.createOrUpdate(group, name, parameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,30 +36,33 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
|
||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
import { ConsoleDirectLine } from './services/ConsoleDirectLine';
|
||||
|
||||
/**
|
||||
* Package for console.glib.
|
||||
*/
|
||||
export class GBConsolePackage implements IGBPackage {
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
public sysPackages: IGBPackage[];
|
||||
public channel: ConsoleDirectLine;
|
||||
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
public getDialogs(min: GBMinInstance) {
|
||||
GBLog.verbose(`getDialogs called.`);
|
||||
}
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
GBLog.verbose(`loadPackage 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 loadBot(min: GBMinInstance): void {
|
||||
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 rp = require('request-promise');
|
||||
import { GBService } from 'botlib';
|
||||
import { GBLog, GBService } from 'botlib';
|
||||
|
||||
/**
|
||||
* Bot simulator in terminal window.
|
||||
*/
|
||||
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;
|
||||
public directLineSecret = '';
|
||||
public directLineClientName = 'DirectLineClient';
|
||||
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||
constructor(directLineSecret: string) {
|
||||
super();
|
||||
|
||||
constructor(directLineSecret) {
|
||||
super();
|
||||
|
||||
this.directLineSecret = directLineSecret;
|
||||
|
||||
// TODO: Migrate to Swagger 3.
|
||||
const directLineClient = rp(this.directLineSpecUrl)
|
||||
.then(function (spec) {
|
||||
return new Swagger({
|
||||
spec: JSON.parse(spec.trim()),
|
||||
usePromise: true
|
||||
});
|
||||
})
|
||||
.then(function (client) {
|
||||
client.clientAuthorizations.add('AuthorizationBotConnector',
|
||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'));
|
||||
return client;
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('Error initializing DirectLine client', err);
|
||||
});
|
||||
|
||||
// TODO: Remove *this* issue.
|
||||
const _this_ = this;
|
||||
directLineClient.then((client) => {
|
||||
client.Conversations.Conversations_StartConversation()
|
||||
.then(function (response) {
|
||||
return response.obj.conversationId;
|
||||
})
|
||||
.then(function (conversationId) {
|
||||
_this_.sendMessagesFromConsole(client, conversationId);
|
||||
_this_.pollMessages(client, conversationId);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('Error starting conversation', err);
|
||||
});
|
||||
this.directLineSecret = directLineSecret;
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
const directLineClient = rp(this.directLineSpecUrl)
|
||||
.then((spec: string) => {
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
return new Swagger({
|
||||
spec: JSON.parse(spec.trim()),
|
||||
usePromise: true
|
||||
});
|
||||
})
|
||||
.then(client => {
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
client.clientAuthorizations.add(
|
||||
'AuthorizationBotConnector',
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${directLineSecret}`, 'header')
|
||||
);
|
||||
|
||||
return client;
|
||||
})
|
||||
.catch(err => {
|
||||
GBLog.error(`Error initializing DirectLine client ${err}`);
|
||||
});
|
||||
|
||||
const _this_ = this;
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
directLineClient.then(client => {
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
client.Conversations.Conversations_StartConversation()
|
||||
.then(response => {
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
return response.obj.conversationId;
|
||||
})
|
||||
.then(conversationId => {
|
||||
_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> ');
|
||||
stdin.addListener('data', function (e) {
|
||||
const input = e.toString().trim();
|
||||
if (input) {
|
||||
// exit
|
||||
if (input.toLowerCase() === 'exit') {
|
||||
return process.exit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
client.Conversations.Conversations_PostActivity(
|
||||
{
|
||||
conversationId: conversationId,
|
||||
activity: {
|
||||
textFormat: 'plain',
|
||||
text: input,
|
||||
type: 'message',
|
||||
from: {
|
||||
id: _this_.directLineClientName,
|
||||
name: _this_.directLineClientName
|
||||
}
|
||||
}
|
||||
}).catch(function (err) {
|
||||
console.error('Error sending message:', err);
|
||||
});
|
||||
public pollMessages(client, conversationId) {
|
||||
const _this_ = this;
|
||||
GBLog.info(`Starting polling message for conversationId: ${conversationId}`);
|
||||
let watermark;
|
||||
setInterval(() => {
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
|
||||
.then(response => {
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
watermark = response.obj.watermark;
|
||||
|
||||
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 */
|
||||
public pollMessages(client, conversationId) {
|
||||
const _this_ = this;
|
||||
console.log('Starting polling message for conversationId: ' + conversationId);
|
||||
let watermark = null;
|
||||
setInterval(function () {
|
||||
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);
|
||||
}
|
||||
if (activity.attachments) {
|
||||
activity.attachments.forEach(attachment => {
|
||||
switch (attachment.contentType) {
|
||||
case 'application/vnd.microsoft.card.hero':
|
||||
this.renderHeroCard(attachment);
|
||||
break;
|
||||
|
||||
public printMessages(activities, directLineClientName) {
|
||||
case 'image/png':
|
||||
GBLog.info(`Opening the requested image ${attachment.contentUrl}`);
|
||||
open(attachment.contentUrl);
|
||||
break;
|
||||
|
||||
if (activities && activities.length) {
|
||||
// ignore own messages
|
||||
activities = activities.filter(function (m) { return m.from.id !== directLineClientName; });
|
||||
|
||||
if (activities.length) {
|
||||
|
||||
// print other messages
|
||||
activities.forEach(activity => {
|
||||
console.log(activity.text);
|
||||
}, this);
|
||||
|
||||
process.stdout.write('Command> ');
|
||||
}
|
||||
default:
|
||||
GBLog.info(`Unknown contentType: ${attachment.contentType}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// tslint:enable:no-unsafe-any
|
||||
|
||||
public printMessage(activity) {
|
||||
if (activity.text) {
|
||||
console.log(activity.text);
|
||||
}
|
||||
// tslint:disable:no-unsafe-any
|
||||
public renderHeroCard(attachment) {
|
||||
const width = 70;
|
||||
const contentLine = content => {
|
||||
return `${' '.repeat((width - content.length) / 2)}content${' '.repeat((width - content.length) / 2)}`;
|
||||
};
|
||||
|
||||
if (activity.attachments) {
|
||||
activity.attachments.forEach(function (attachment) {
|
||||
switch (attachment.contentType) {
|
||||
case 'application/vnd.microsoft.card.hero':
|
||||
this.renderHeroCard(attachment);
|
||||
break;
|
||||
|
||||
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) + '/');
|
||||
}
|
||||
GBLog.info(`/${'*'.repeat(width + 1)}`);
|
||||
GBLog.info(`*${contentLine(attachment.content.title)}*`);
|
||||
GBLog.info(`*${' '.repeat(width)}*`);
|
||||
GBLog.info(`*${contentLine(attachment.content.text)}*`);
|
||||
GBLog.info(`${'*'.repeat(width + 1)}/`);
|
||||
}
|
||||
// tslint:enable:no-unsafe-any
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -38,10 +38,12 @@
|
|||
|
||||
import { BotAdapter } from 'botbuilder';
|
||||
import {WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
import { Messages } from '../strings';
|
||||
|
||||
/**
|
||||
* Dialog for Welcoming people.
|
||||
*/
|
||||
export class WelcomeDialog extends IGBDialog {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -73,9 +75,9 @@ export class WelcomeDialog extends IGBDialog {
|
|||
await step.replaceDialog('/ask', { firstTime: true });
|
||||
|
||||
if (
|
||||
step.context.activity &&
|
||||
step.context.activity.type == 'message' &&
|
||||
step.context.activity.text != ''
|
||||
step.context.activity !== undefined &&
|
||||
step.context.activity.type === 'message' &&
|
||||
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 { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
import { Messages } from '../strings';
|
||||
|
||||
/**
|
||||
* Dialog for the bot explains about itself.
|
||||
*/
|
||||
export class WhoAmIDialog extends IGBDialog {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -55,7 +56,7 @@ export class WhoAmIDialog extends IGBDialog {
|
|||
const locale = step.context.activity.locale;
|
||||
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 min.conversationalService.sendEvent(step, 'play', {
|
||||
playerType: 'video',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,42 +36,37 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
|
||||
import { GBMinInstance, IGBPackage } from 'botlib';
|
||||
|
||||
import { IGBCoreService} from 'botlib';
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
import { WelcomeDialog } from './dialogs/WelcomeDialog';
|
||||
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
|
||||
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel';
|
||||
|
||||
/**
|
||||
* Package for core.gbapp.
|
||||
*/
|
||||
export class GBCorePackage implements IGBPackage {
|
||||
public static CurrentEngineName = 'guaribas-1.0.0';
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
|
||||
public sysPackages: IGBPackage[];
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
core.sequelize.addModels([
|
||||
GuaribasInstance,
|
||||
GuaribasPackage,
|
||||
GuaribasChannel,
|
||||
GuaribasException
|
||||
]);
|
||||
core.sequelize.addModels([GuaribasInstance, GuaribasPackage, GuaribasChannel, GuaribasException]);
|
||||
}
|
||||
|
||||
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 loadBot(min: GBMinInstance): void {
|
||||
WelcomeDialog.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 = {
|
||||
levels: {
|
||||
error: 0,
|
||||
debug: 1,
|
||||
warn: 2,
|
||||
data: 3,
|
||||
info: 4,
|
||||
verbose: 5,
|
||||
silly: 6,
|
||||
custom: 7
|
||||
},
|
||||
colors: {
|
||||
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;
|
||||
import {
|
||||
AutoIncrement,
|
||||
BelongsTo,
|
||||
Column,
|
||||
CreatedAt,
|
||||
ForeignKey,
|
||||
Model,
|
||||
PrimaryKey,
|
||||
Table,
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript';
|
||||
import { GuaribasInstance } from './GBModel';
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -51,9 +51,13 @@ import {
|
|||
|
||||
import { IGBInstance } from 'botlib';
|
||||
|
||||
/**
|
||||
* Base instance data for a bot.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasInstance extends Model<GuaribasInstance>
|
||||
implements IGBInstance {
|
||||
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Column
|
||||
|
@ -80,8 +84,6 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
|||
@Column
|
||||
public enabledAdmin: boolean;
|
||||
|
||||
/* Services section on bot.json */
|
||||
|
||||
@Column
|
||||
public engineName: string;
|
||||
|
||||
|
@ -152,7 +154,7 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
|||
public speechKey: string;
|
||||
|
||||
@Column
|
||||
public speechKeyEndpoint: string;
|
||||
public speechEndpoint: string;
|
||||
|
||||
@Column
|
||||
public spellcheckerKey: string;
|
||||
|
@ -218,8 +220,6 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
|||
@Column
|
||||
public adminPass: string;
|
||||
|
||||
/* Settings section of bot.json */
|
||||
|
||||
@Column(DataType.FLOAT)
|
||||
public nlpVsSearch: number;
|
||||
|
||||
|
@ -238,6 +238,9 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
|||
public updatedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each packaged listed for use in a bot instance.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasPackage extends Model<GuaribasPackage> {
|
||||
@PrimaryKey
|
||||
|
@ -264,6 +267,9 @@ export class GuaribasPackage extends Model<GuaribasPackage> {
|
|||
public updatedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* A bot channel.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasChannel extends Model<GuaribasChannel> {
|
||||
@PrimaryKey
|
||||
|
@ -283,7 +289,11 @@ export class GuaribasChannel extends Model<GuaribasChannel> {
|
|||
public updatedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception that has been thrown.
|
||||
*/
|
||||
@Table
|
||||
//tslint:disable-next-line:max-classes-per-file
|
||||
export class GuaribasException extends Model<GuaribasException> {
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -34,23 +34,32 @@
|
|||
|
||||
import { TurnContext } from 'botbuilder';
|
||||
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 { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||
import { GBDeployer } from './GBDeployer';
|
||||
|
||||
/**
|
||||
* @fileoverview General Bots server core.
|
||||
*/
|
||||
|
||||
/**
|
||||
* BASIC system class for extra manipulation of bot behaviour.
|
||||
*/
|
||||
class SysClass {
|
||||
public min: GBMinInstance;
|
||||
private readonly deployer: GBDeployer;
|
||||
|
||||
constructor(min: GBMinInstance) {
|
||||
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
||||
this.min = min;
|
||||
this.deployer = deployer;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -59,16 +68,16 @@ class SysClass {
|
|||
}
|
||||
|
||||
public async createABotFarmUsing(
|
||||
botId,
|
||||
username,
|
||||
password,
|
||||
location,
|
||||
nlpAuthoringKey,
|
||||
appId,
|
||||
appPassword,
|
||||
subscriptionId
|
||||
botId: string,
|
||||
username: string,
|
||||
password: string,
|
||||
location: string,
|
||||
nlpAuthoringKey: string,
|
||||
appId: string,
|
||||
appPassword: string,
|
||||
subscriptionId: string
|
||||
) {
|
||||
const service = new AzureDeployerService(this.min.deployer);
|
||||
const service = new AzureDeployerService(this.deployer);
|
||||
await service.deployToCloud(
|
||||
botId,
|
||||
username,
|
||||
|
@ -80,21 +89,42 @@ class SysClass {
|
|||
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 context: TurnContext;
|
||||
public step: WaterfallStepContext;
|
||||
public internalSys: SysClass;
|
||||
|
||||
constructor(min: GBMinInstance) {
|
||||
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
||||
this.min = min;
|
||||
this.internalSys = new SysClass(min);
|
||||
this.internalSys = new SysClass(min, deployer);
|
||||
}
|
||||
|
||||
public sys(): SysClass {
|
||||
|
@ -102,7 +132,7 @@ export default class DialogClass {
|
|||
}
|
||||
|
||||
public async hear(cb) {
|
||||
const idCallback = Math.floor(Math.random() * 1000000000000);
|
||||
const idCallback = crypto.getRandomValues(new Uint32Array(16))[0];
|
||||
this.min.cbMap[idCallback] = cb;
|
||||
await this.step.beginDialog('/hear', { id: idCallback });
|
||||
}
|
||||
|
@ -110,17 +140,4 @@ export default class DialogClass {
|
|||
public async talk(text: string) {
|
||||
return await this.context.sendActivity(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to call any REST API.
|
||||
*/
|
||||
public sendEmail(to, subject, body) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to call any REST API.
|
||||
*/
|
||||
public post(url: string, data) {}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -30,16 +30,29 @@
|
|||
| |
|
||||
\*****************************************************************************/
|
||||
|
||||
const logger = require('../../../src/logger');
|
||||
import * as fs from 'fs';
|
||||
'use strict';
|
||||
|
||||
import { GBLog } from 'botlib';
|
||||
|
||||
/**
|
||||
* @fileoverview General Bots server core.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Base configuration for the server like storage.
|
||||
*/
|
||||
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 {
|
||||
try {
|
||||
require('dotenv-extended').load({
|
||||
|
@ -49,7 +62,7 @@ export class GBConfigService {
|
|||
overrideProcessEnv: true
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
GBLog.error(e.message);
|
||||
process.exit(3);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +70,7 @@ export class GBConfigService {
|
|||
public static get(key: string): string | undefined {
|
||||
let value = GBConfigService.tryGet(key);
|
||||
|
||||
if (!value) {
|
||||
if (value === undefined) {
|
||||
switch (key) {
|
||||
case 'CLOUD_USERNAME':
|
||||
value = undefined;
|
||||
|
@ -108,18 +121,20 @@ export class GBConfigService {
|
|||
value = 'true';
|
||||
break;
|
||||
default:
|
||||
logger.warn(`Invalid key on .env file: '${key}'`);
|
||||
GBLog.warn(`Invalid key on .env file: '${key}'`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static tryGet(key: string) {
|
||||
let value = process.env['container:' + key];
|
||||
if (!value) {
|
||||
public static tryGet(key: string): any {
|
||||
let value = process.env[`container:${key}`];
|
||||
if (value === undefined) {
|
||||
value = process.env[key];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,13 +36,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const logger = require('../../../src/logger');
|
||||
import { MessageFactory } from 'botbuilder';
|
||||
import { MessageFactory, RecognizerResult } from 'botbuilder';
|
||||
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 { Messages } from '../strings';
|
||||
import { GBCoreService } from './GBCoreService';
|
||||
const Nexmo = require('nexmo');
|
||||
|
||||
export interface LanguagePickerSettings {
|
||||
|
@ -50,18 +48,22 @@ export interface LanguagePickerSettings {
|
|||
supportedLocales?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides basic services for handling messages and dispatching to back-end
|
||||
* services like NLP or Search.
|
||||
*/
|
||||
export class GBConversationalService implements IGBConversationalService {
|
||||
public coreService: GBCoreService;
|
||||
public coreService: IGBCoreService;
|
||||
|
||||
constructor(coreService: GBCoreService) {
|
||||
constructor(coreService: IGBCoreService) {
|
||||
this.coreService = coreService;
|
||||
}
|
||||
|
||||
public getCurrentLanguage(step: any) {
|
||||
public getCurrentLanguage(step: GBDialogStep) {
|
||||
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') {
|
||||
const msg = MessageFactory.text('');
|
||||
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> {
|
||||
return new Promise(
|
||||
(resolve: any, reject: any): any => {
|
||||
|
@ -79,6 +82,7 @@ export class GBConversationalService implements IGBConversationalService {
|
|||
apiKey: min.instance.smsKey,
|
||||
apiSecret: min.instance.smsSecret
|
||||
});
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, (err, data) => {
|
||||
if (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.
|
||||
|
||||
let endpoint = min.instance.nlpEndpoint.replace('/luis/v2.0', '');
|
||||
const endpoint = min.instance.nlpEndpoint.replace('/luis/v2.0', '');
|
||||
|
||||
const model = new LuisRecognizer({
|
||||
applicationId: min.instance.nlpAppId,
|
||||
|
@ -101,38 +106,42 @@ export class GBConversationalService implements IGBConversationalService {
|
|||
endpoint: endpoint
|
||||
});
|
||||
|
||||
let nlp: any;
|
||||
let nlp: RecognizerResult;
|
||||
try {
|
||||
nlp = await model.recognize(step.context);
|
||||
} catch (error) {
|
||||
// tslint:disable:no-unsafe-any
|
||||
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);
|
||||
} else {
|
||||
const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${
|
||||
error.statusCode ? error.statusCode : ''
|
||||
} ${error.message}`;
|
||||
} {error.message; }`;
|
||||
|
||||
return Promise.reject(new Error(msg));
|
||||
}
|
||||
// tslint:enable:no-unsafe-any
|
||||
}
|
||||
|
||||
// Resolves intents returned from LUIS.
|
||||
|
||||
const topIntent = LuisRecognizer.topIntent(nlp);
|
||||
if (topIntent) {
|
||||
if (topIntent !== undefined) {
|
||||
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') {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
logger.info(`NLP called: ${intent}, ${entity}`);
|
||||
GBLog.info(`NLP called: ${intent} ${firstEntity}`);
|
||||
|
||||
try {
|
||||
await step.replaceDialog(`/${intent}`, nlp.entities);
|
||||
await step.replaceDialog(` /${intent}`, nlp.entities);
|
||||
|
||||
return Promise.resolve(true);
|
||||
} catch (error) {
|
||||
|
@ -145,7 +154,7 @@ export class GBConversationalService implements IGBConversationalService {
|
|||
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);
|
||||
if (locale !== step.context.activity.locale.split('-')[0]) {
|
||||
switch (locale) {
|
||||
|
@ -158,7 +167,7 @@ export class GBConversationalService implements IGBConversationalService {
|
|||
await step.context.sendActivity(Messages[locale].changing_language);
|
||||
break;
|
||||
default:
|
||||
await step.context.sendActivity(`Unknown language: ${locale}`);
|
||||
await step.context.sendActivity(`; Unknown; language: $;{locale;}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,13 +36,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import { IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
||||
import { GBLog, IGBCoreService, IGBInstallationDeployer, IGBInstance, IGBPackage } from 'botlib';
|
||||
import * as fs from 'fs';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
import { GBAdminPackage } from '../../admin.gbapp/index';
|
||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||
import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog';
|
||||
import { GBCorePackage } from '../../core.gbapp';
|
||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||
import { GBKBPackage } from '../../kb.gbapp';
|
||||
|
@ -50,10 +50,7 @@ import { GBSecurityPackage } from '../../security.gblib';
|
|||
import { GBWhatsappPackage } from '../../whatsapp.gblib/index';
|
||||
import { GuaribasInstance } from '../models/GBModel';
|
||||
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');
|
||||
|
||||
/**
|
||||
|
@ -121,11 +118,11 @@ export class GBCoreService implements IGBCoreService {
|
|||
throw new Error(`Unknown dialect: ${this.dialect}.`);
|
||||
}
|
||||
|
||||
const logging: any =
|
||||
const logging: boolean | Function =
|
||||
GBConfigService.get('STORAGE_LOGGING') === 'true'
|
||||
? (str: string): void => {
|
||||
logger.info(str);
|
||||
}
|
||||
GBLog.info(str);
|
||||
}
|
||||
: false;
|
||||
|
||||
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
|
||||
|
@ -140,9 +137,10 @@ export class GBCoreService implements IGBCoreService {
|
|||
dialect: this.dialect,
|
||||
storage: storage,
|
||||
dialectOptions: {
|
||||
encrypt: encrypt
|
||||
},
|
||||
pool: {
|
||||
options: {
|
||||
encrypt: encrypt
|
||||
}
|
||||
}, pool: {
|
||||
max: 32,
|
||||
min: 8,
|
||||
idle: 40000,
|
||||
|
@ -153,32 +151,36 @@ export class GBCoreService implements IGBCoreService {
|
|||
|
||||
if (this.dialect === 'mssql') {
|
||||
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
|
||||
// tslint:disable:no-unsafe-any
|
||||
this.createTableQuery = this.queryGenerator.createTableQuery;
|
||||
this.queryGenerator.createTableQuery = (tableName, attributes, options) =>
|
||||
this.createTableQueryOverride(tableName, attributes, options);
|
||||
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
|
||||
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
|
||||
this.changeColumnQueryOverride(tableName, attributes);
|
||||
// tslint:enable:no-unsafe-any
|
||||
}
|
||||
}
|
||||
|
||||
public async checkStorage(azureDeployer: AzureDeployerService) {
|
||||
public async checkStorage(installationDeployer: IGBInstallationDeployer) {
|
||||
try {
|
||||
await this.sequelize.authenticate();
|
||||
} 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') {
|
||||
await this.openStorageFrontier(azureDeployer);
|
||||
await this.openStorageFrontier(installationDeployer);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
// tslint:ensable:no-unsafe-any
|
||||
}
|
||||
}
|
||||
|
||||
public async syncDatabaseStructure() {
|
||||
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
|
||||
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
|
||||
logger.info('Syncing database...');
|
||||
GBLog.info('Syncing database...');
|
||||
|
||||
return this.sequelize.sync({
|
||||
alter: alter,
|
||||
|
@ -186,21 +188,21 @@ export class GBCoreService implements IGBCoreService {
|
|||
});
|
||||
} else {
|
||||
const msg = `Database synchronization is disabled.`;
|
||||
logger.info(msg);
|
||||
GBLog.info(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all items to start several listeners.
|
||||
*/
|
||||
public async loadInstances(): Promise<IGBInstance> {
|
||||
public async loadInstances(): Promise<IGBInstance[]> {
|
||||
return GuaribasInstance.findAll({});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 } };
|
||||
|
||||
return GuaribasInstance.findOne(options);
|
||||
|
@ -240,12 +242,19 @@ STORAGE_SYNC=true
|
|||
|
||||
public async ensureProxy(port): Promise<string> {
|
||||
try {
|
||||
const ngrok = require('ngrok');
|
||||
return await ngrok.connect({ port: port });
|
||||
if (fs.existsSync('node_modules/ngrok/bin/ngrok.exe')) {
|
||||
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) {
|
||||
// There are false positive from ngrok regarding to no memory, but it's just
|
||||
// lack of connection.
|
||||
logger.verbose(error);
|
||||
GBLog.verbose(error);
|
||||
throw new Error('Error connecting to remote ngrok server, please check network connection.');
|
||||
}
|
||||
}
|
||||
|
@ -267,34 +276,42 @@ STORAGE_SYNC=true
|
|||
* @param azureDeployer
|
||||
* @param proxyAddress
|
||||
*/
|
||||
public async loadAllInstances(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
||||
logger.info(`Loading instances from storage...`);
|
||||
let instances: GuaribasInstance[];
|
||||
public async loadAllInstances(
|
||||
core: IGBCoreService,
|
||||
installationDeployer: IGBInstallationDeployer,
|
||||
proxyAddress: string
|
||||
) {
|
||||
GBLog.info(`Loading instances from storage...`);
|
||||
let instances: IGBInstance[];
|
||||
try {
|
||||
instances = await core.loadInstances();
|
||||
const instance = instances[0];
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
||||
await AzureDeployerService.updateBotProxy(
|
||||
GBLog.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
||||
await installationDeployer.updateBotProxy(
|
||||
instance.botId,
|
||||
instance.botId,
|
||||
`${proxyAddress}/api/messages/${instance.botId}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// Check if storage is empty and needs formatting.
|
||||
const isInvalidObject = error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE.
|
||||
if (isInvalidObject) {
|
||||
if (GBConfigService.get('STORAGE_SYNC') != 'true') {
|
||||
throw new Error(
|
||||
`Operating storage is out of sync or there is a storage connection error.
|
||||
Try setting STORAGE_SYNC to true in .env file. Error: ${error.message}.`
|
||||
);
|
||||
} else {
|
||||
logger.info(`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`);
|
||||
}
|
||||
} else {
|
||||
if (error.parent === undefined) {
|
||||
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 core
|
||||
*/
|
||||
public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) {
|
||||
if (!instances) {
|
||||
const instance: IGBInstance = {};
|
||||
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {
|
||||
if (instances === undefined) {
|
||||
const instance = new GuaribasInstance();
|
||||
await instance.save();
|
||||
instances = await core.loadInstances();
|
||||
}
|
||||
|
@ -329,10 +346,10 @@ STORAGE_SYNC=true
|
|||
GBCorePackage,
|
||||
GBSecurityPackage,
|
||||
GBKBPackage,
|
||||
GBCustomerSatisfactionPackage,
|
||||
GBWhatsappPackage
|
||||
GBCustomerSatisfactionPackage
|
||||
// GBWhatsappPackage
|
||||
].forEach(e => {
|
||||
logger.info(`Loading sys package: ${e.name}...`);
|
||||
GBLog.info(`Loading sys package: ${e.name}...`);
|
||||
const p = Object.create(e.prototype) as IGBPackage;
|
||||
p.loadPackage(core, core.sequelize);
|
||||
});
|
||||
|
@ -347,18 +364,27 @@ STORAGE_SYNC=true
|
|||
}
|
||||
}
|
||||
|
||||
public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
||||
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
||||
public async createBootInstance(
|
||||
core: GBCoreService,
|
||||
installationDeployer: IGBInstallationDeployer,
|
||||
proxyAddress: string
|
||||
) {
|
||||
GBLog.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
||||
try {
|
||||
let { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance();
|
||||
instance = await azureDeployer.deployFarm(proxyAddress, instance, credentials, subscriptionId);
|
||||
core.writeEnv(instance);
|
||||
logger.info(`File .env written, starting General Bots...`);
|
||||
const { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance(installationDeployer);
|
||||
const changedInstance = await installationDeployer.deployFarm(
|
||||
proxyAddress,
|
||||
instance,
|
||||
credentials,
|
||||
subscriptionId
|
||||
);
|
||||
core.writeEnv(changedInstance);
|
||||
GBLog.info(`File .env written, starting General Bots...`);
|
||||
GBConfigService.init();
|
||||
|
||||
return instance;
|
||||
return changedInstance;
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
GBLog.warn(
|
||||
`In case of error, please cleanup any infrastructure objects
|
||||
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]);
|
||||
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
|
||||
const matches = re1.exec(sql);
|
||||
if (matches) {
|
||||
if (matches !== null) {
|
||||
const table = matches[1];
|
||||
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
|
||||
sql = sql.replace(
|
||||
re2,
|
||||
(match: string, ...args: any[]): string => {
|
||||
return 'CONSTRAINT [' + table + '_pk] ' + match;
|
||||
return `CONSTRAINT [${table}_pk] ${match}`;
|
||||
}
|
||||
);
|
||||
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
|
||||
|
@ -407,15 +433,17 @@ STORAGE_SYNC=true
|
|||
(match: string, ...args: any[]): string => {
|
||||
const fkcols = args[0];
|
||||
let fkname = table;
|
||||
let matches = re4.exec(fkcols);
|
||||
while (matches != undefined) {
|
||||
fkname += '_' + matches[1];
|
||||
matches = re4.exec(fkcols);
|
||||
let matches2 = re4.exec(fkcols);
|
||||
while (matches2 !== null) {
|
||||
fkname += `_${matches2[1]}`;
|
||||
matches2 = re4.exec(fkcols);
|
||||
}
|
||||
return 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')';
|
||||
|
||||
return `CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
@ -431,7 +459,7 @@ STORAGE_SYNC=true
|
|||
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [tableName, attributes]);
|
||||
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
|
||||
const matches = re1.exec(sql);
|
||||
if (matches) {
|
||||
if (matches !== null) {
|
||||
const table = matches[1];
|
||||
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
|
||||
const re3 = /\[([^\]]*)\]/g;
|
||||
|
@ -440,15 +468,17 @@ STORAGE_SYNC=true
|
|||
(match: string, ...args: any[]): string => {
|
||||
const fkcols = args[2];
|
||||
let fkname = table;
|
||||
let matches = re3.exec(fkcols);
|
||||
while (matches != undefined) {
|
||||
fkname += '_' + matches[1];
|
||||
matches = re3.exec(fkcols);
|
||||
let matches2 = re3.exec(fkcols);
|
||||
while (matches2 !== null) {
|
||||
fkname += `_${matches2[1]}`;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -457,9 +487,9 @@ STORAGE_SYNC=true
|
|||
*
|
||||
* @param azureDeployer Infrastructure Deployer instance.
|
||||
*/
|
||||
private async openStorageFrontier(deployer: AzureDeployerService) {
|
||||
private async openStorageFrontier(installationDeployer: IGBInstallationDeployer) {
|
||||
const group = GBConfigService.get('CLOUD_GROUP');
|
||||
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';
|
||||
|
||||
const logger = require('../../../src/logger');
|
||||
const Path = require('path');
|
||||
const UrlJoin = require('url-join');
|
||||
import urlJoin = require('url-join');
|
||||
const Fs = require('fs');
|
||||
const WaitUntil = require('wait-until');
|
||||
const express = require('express');
|
||||
const child_process = require('child_process');
|
||||
const graph = require('@microsoft/microsoft-graph-client');
|
||||
|
||||
import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
|
||||
import { GBError, IGBPackage } from 'botlib';
|
||||
import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
||||
import { AzureSearch } from 'pragmatismo-io-framework';
|
||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel';
|
||||
import { GuaribasPackage } from '../models/GBModel';
|
||||
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService';
|
||||
import { KBService } from './../../kb.gbapp/services/KBService';
|
||||
import { GBConfigService } from './GBConfigService';
|
||||
import { GBCoreService } from './GBCoreService';
|
||||
import { GBImporter } from './GBImporterService';
|
||||
import { GBVMService } from './GBVMService';
|
||||
|
||||
|
@ -73,8 +69,8 @@ export class GBDeployer {
|
|||
this.importer = importer;
|
||||
}
|
||||
|
||||
public static getConnectionStringFromInstance(instance: GuaribasInstance) {
|
||||
return `Server=tcp:${instance.storageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${
|
||||
public static getConnectionStringFromInstance(instance: IGBInstance) {
|
||||
return `Server=tcp:torageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${
|
||||
instance.storageUsername
|
||||
};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
|
||||
}
|
||||
|
@ -92,12 +88,12 @@ export class GBDeployer {
|
|||
let totalPackages = 0;
|
||||
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
||||
let paths = [GBDeployer.deployFolder];
|
||||
if (additionalPath) {
|
||||
if (additionalPath !== undefined && additionalPath !== '') {
|
||||
paths = paths.concat(additionalPath.toLowerCase().split(';'));
|
||||
}
|
||||
const botPackages = new Array<string>();
|
||||
const gbappPackages = new Array<string>();
|
||||
let generalPackages = new Array<string>();
|
||||
const botPackages: string[] = [];
|
||||
const gbappPackages: string[] = [];
|
||||
let generalPackages: string[] = [];
|
||||
|
||||
function doIt(path) {
|
||||
const isDirectory = source => Fs.lstatSync(source).isDirectory();
|
||||
|
@ -109,7 +105,7 @@ export class GBDeployer {
|
|||
const dirs = getDirectories(path);
|
||||
dirs.forEach(element => {
|
||||
if (element.startsWith('.')) {
|
||||
logger.info(`Ignoring ${element}...`);
|
||||
GBLog.info(`Ignoring ${element}...`);
|
||||
} else {
|
||||
if (element.endsWith('.gbot')) {
|
||||
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 => {
|
||||
logger.info(`Looking in: ${e}...`);
|
||||
GBLog.info(`Looking in: ${e}...`);
|
||||
doIt(e);
|
||||
});
|
||||
|
||||
|
@ -136,11 +132,11 @@ export class GBDeployer {
|
|||
.interval(1000)
|
||||
.times(10)
|
||||
.condition(cb => {
|
||||
logger.info(`Waiting for app package deployment...`);
|
||||
GBLog.info(`Waiting for app package deployment...`);
|
||||
cb(appPackagesProcessed === gbappPackages.length);
|
||||
})
|
||||
.done(async result => {
|
||||
logger.info(`App Package deployment done.`);
|
||||
.done(async () => {
|
||||
GBLog.info(`App Package deployment done.`);
|
||||
|
||||
({ generalPackages, totalPackages } = await this.deployDataPackages(
|
||||
core,
|
||||
|
@ -162,11 +158,9 @@ export class GBDeployer {
|
|||
*/
|
||||
|
||||
public async deployBot(localPath: string): Promise<IGBInstance> {
|
||||
const packageType = Path.extname(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> {
|
||||
|
@ -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 accessToken = adminService.acquireElevatedToken(instanceId);
|
||||
|
||||
|
@ -184,57 +178,30 @@ export class GBDeployer {
|
|||
|
||||
const client = graph.Client.init({
|
||||
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 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) {
|
||||
public async deployPackage(min: GBMinInstance, localPath: string) {
|
||||
const packageType = Path.extname(localPath);
|
||||
|
||||
switch (packageType) {
|
||||
case '.gbot':
|
||||
return this.deployBot(localPath);
|
||||
|
||||
case '.gbtheme':
|
||||
return this.deployTheme(localPath);
|
||||
|
||||
// PACKAGE: Put in package logic.
|
||||
case '.gbkb':
|
||||
const service = new KBService(this.core.sequelize);
|
||||
return service.deployKb(this.core, this, localPath);
|
||||
|
||||
case '.gbui':
|
||||
break;
|
||||
return service.deployKb(this.core, this, localPath);
|
||||
|
||||
case '.gbdialog':
|
||||
const vm = new GBVMService();
|
||||
|
||||
return vm.loadDialogPackage(localPath, min, this.core, this);
|
||||
|
||||
default:
|
||||
const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
||||
const err = GBError.create(`Unhandled package type: ${packageType}.`);
|
||||
Promise.reject(err);
|
||||
break;
|
||||
}
|
||||
|
@ -247,16 +214,9 @@ export class GBDeployer {
|
|||
const p = await this.getPackageByName(instance.instanceId, packageName);
|
||||
|
||||
switch (packageType) {
|
||||
case '.gbot':
|
||||
// TODO: this.undeployBot(packageName, localPath)
|
||||
break;
|
||||
|
||||
case '.gbtheme':
|
||||
// TODO: this.undeployTheme(packageName, localPath)
|
||||
break;
|
||||
|
||||
case '.gbkb':
|
||||
const service = new KBService(this.core.sequelize);
|
||||
|
||||
return service.undeployKbFromStorage(instance, this, p.packageId);
|
||||
|
||||
case '.gbui':
|
||||
|
@ -266,13 +226,13 @@ export class GBDeployer {
|
|||
break;
|
||||
|
||||
default:
|
||||
const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
||||
const err = GBError.create(`Unhandled package type: ${packageType}.`);
|
||||
Promise.reject(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async rebuildIndex(instance: GuaribasInstance) {
|
||||
public async rebuildIndex(instance: IGBInstance, searchSchema: any) {
|
||||
const search = new AzureSearch(
|
||||
instance.searchKey,
|
||||
instance.searchHost,
|
||||
|
@ -286,7 +246,7 @@ export class GBDeployer {
|
|||
try {
|
||||
await search.deleteDataSource(dsName);
|
||||
} catch (err) {
|
||||
if (err.code != 404) {
|
||||
if (err.code !== 404) {
|
||||
// First time, nothing to delete.
|
||||
throw err;
|
||||
}
|
||||
|
@ -297,25 +257,26 @@ export class GBDeployer {
|
|||
try {
|
||||
await search.deleteIndex();
|
||||
} catch (err) {
|
||||
if (err.code != 404) {
|
||||
if (err.code !== 404) {
|
||||
// First time, nothing to delete.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await search.createIndex(AzureDeployerService.getKBSearchSchema(instance.searchIndex), dsName);
|
||||
await search.createIndex(searchSchema, dsName);
|
||||
}
|
||||
|
||||
public async getPackageByName(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
||||
const where = { packageName: packageName, instanceId: instanceId };
|
||||
|
||||
return GuaribasPackage.findOne({
|
||||
where: where
|
||||
});
|
||||
}
|
||||
|
||||
public installDefaultGBUI() {
|
||||
public runOnce() {
|
||||
const root = 'packages/default.gbui';
|
||||
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');
|
||||
child_process.execSync('npm install', { cwd: root });
|
||||
child_process.execSync('npm run build', { cwd: root });
|
||||
|
@ -323,7 +284,7 @@ export class GBDeployer {
|
|||
}
|
||||
|
||||
private async deployDataPackages(
|
||||
core: GBCoreService,
|
||||
core: IGBCoreService,
|
||||
botPackages: string[],
|
||||
_this: this,
|
||||
generalPackages: string[],
|
||||
|
@ -342,9 +303,9 @@ export class GBDeployer {
|
|||
|
||||
botPackages.forEach(e => {
|
||||
if (e !== 'packages\\boot.gbot') {
|
||||
logger.info(`Deploying bot: ${e}...`);
|
||||
GBLog.info(`Deploying bot: ${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.forEach(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.
|
||||
|
||||
if (Path.extname(filename) === '.gbapp' || Path.extname(filename) === '.gblib') {
|
||||
// Themes for bots.
|
||||
} else if (Path.extname(filename) === '.gbtheme') {
|
||||
server.use('/themes/' + filenameOnly, express.static(filename));
|
||||
logger.info(`Theme (.gbtheme) assets accessible at: ${'/themes/' + filenameOnly}.`);
|
||||
server.use(`/themes/${filenameOnly}`, express.static(filename));
|
||||
GBLog.info(`Theme (.gbtheme) assets accessible at: /themes/${filenameOnly}.`);
|
||||
} else if (Path.extname(filename) === '.gbkb') {
|
||||
server.use('/kb/' + filenameOnly + '/subjects', express.static(UrlJoin(filename, 'subjects')));
|
||||
logger.info(`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`);
|
||||
server.use(`/kb/${filenameOnly}/subjects`, express.static(urlJoin(filename, 'subjects')));
|
||||
GBLog.info(`KB (.gbkb) assets accessible at: /kb/${filenameOnly}.`);
|
||||
} else if (Path.extname(filename) === '.gbui') {
|
||||
// Already Handled
|
||||
} else if (Path.extname(filename) === '.gbdialog') {
|
||||
|
@ -381,14 +342,14 @@ export class GBDeployer {
|
|||
.interval(100)
|
||||
.times(5)
|
||||
.condition(cb => {
|
||||
logger.info(`Waiting for package deployment...`);
|
||||
GBLog.info(`Waiting for package deployment...`);
|
||||
cb(totalPackages === generalPackages.length);
|
||||
})
|
||||
.done(result => {
|
||||
.done(() => {
|
||||
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 {
|
||||
logger.info(`Package deployment done.`);
|
||||
GBLog.info(`Package deployment done.`);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
@ -401,17 +362,17 @@ export class GBDeployer {
|
|||
gbappPackages.forEach(e => {
|
||||
// Skips .gbapp inside deploy folder.
|
||||
if (!e.startsWith('packages')) {
|
||||
logger.info(`Deploying app: ${e}...`);
|
||||
GBLog.info(`Deploying app: ${e}...`);
|
||||
|
||||
let folder = Path.join(e, 'node_modules');
|
||||
if (!Fs.existsSync(folder)) {
|
||||
logger.info(`Installing modules for ${e}...`);
|
||||
GBLog.info(`Installing modules for ${e}...`);
|
||||
child_process.execSync('npm install', { cwd: e });
|
||||
}
|
||||
|
||||
folder = Path.join(e, 'dist');
|
||||
if (!Fs.existsSync()) {
|
||||
logger.info(`Compiling ${e}...`);
|
||||
GBLog.info(`Compiling ${e}...`);
|
||||
|
||||
try {
|
||||
child_process.execSync(Path.join(e, 'node_modules/.bin/tsc'), { cwd: e });
|
||||
|
@ -420,15 +381,15 @@ export class GBDeployer {
|
|||
const p = new m.Package();
|
||||
p.loadPackage(core, core.sequelize);
|
||||
appPackages.push(p);
|
||||
logger.info(`App (.gbapp) deployed: ${e}.`);
|
||||
GBLog.info(`App (.gbapp) deployed: ${e}.`);
|
||||
appPackagesProcessed++;
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Error deploying .gbapp package: ${e}\n${err}`);
|
||||
GBLog.error(`Error deploying .gbapp package: ${e}\n${err}`);
|
||||
appPackagesProcessed++;
|
||||
});
|
||||
} 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++;
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +397,7 @@ export class GBDeployer {
|
|||
appPackagesProcessed++;
|
||||
}
|
||||
});
|
||||
|
||||
return appPackagesProcessed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,13 +36,14 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
import { IGBCoreService, IGBInstance } from 'botlib';
|
||||
import { IGBCoreService } from 'botlib';
|
||||
import fs = require('fs');
|
||||
import path = require('path');
|
||||
import { SecService } from '../../security.gblib/services/SecService';
|
||||
import urlJoin = require('url-join');
|
||||
import { GuaribasInstance } from '../models/GBModel';
|
||||
|
||||
/**
|
||||
* Handles the importing of packages.
|
||||
*/
|
||||
export class GBImporter {
|
||||
public core: IGBCoreService;
|
||||
|
||||
|
@ -51,25 +52,25 @@ export class GBImporter {
|
|||
}
|
||||
|
||||
public async importIfNotExistsBotPackage(botId: string, packageName: string, localPath: string) {
|
||||
const packageJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8'));
|
||||
if (!botId) {
|
||||
const packageJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
|
||||
if (botId === undefined) {
|
||||
botId = packageJson.botId;
|
||||
}
|
||||
const instance = await this.core.loadInstance(botId);
|
||||
if (instance) {
|
||||
if (instance !== null) {
|
||||
return instance;
|
||||
} 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) {
|
||||
const settings = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'settings.json'), 'utf8'));
|
||||
const servicesJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'services.json'), 'utf8'));
|
||||
private async createInstanceInternal(botId: string, localPath: string, packageJson: any) {
|
||||
const settings = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
|
||||
const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8'));
|
||||
|
||||
packageJson = { ...packageJson, ...settings, ...servicesJson };
|
||||
|
||||
if (botId){
|
||||
if (botId !== undefined) {
|
||||
packageJson.botId = botId;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -37,29 +37,40 @@
|
|||
'use strict';
|
||||
|
||||
const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
|
||||
const UrlJoin = require('url-join');
|
||||
import urlJoin = require('url-join');
|
||||
const express = require('express');
|
||||
const logger = require('../../../src/logger');
|
||||
|
||||
const request = require('request-promise-native');
|
||||
const AuthenticationContext = require('adal-node').AuthenticationContext;
|
||||
|
||||
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
||||
|
||||
import { 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 { GBCorePackage } from '../../core.gbapp';
|
||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||
import { GBKBPackage } from '../../kb.gbapp';
|
||||
import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog';
|
||||
import { GBSecurityPackage } from '../../security.gblib';
|
||||
import { GBWhatsappPackage } from '../../whatsapp.gblib';
|
||||
import { GuaribasInstance } from '../models/GBModel';
|
||||
import { Messages } from '../strings';
|
||||
import { GBAdminPackage } from './../../admin.gbapp/index';
|
||||
import { GBDeployer } from './GBDeployer';
|
||||
|
||||
/** Minimal service layer for a bot. */
|
||||
|
||||
/**
|
||||
* Minimal service layer for a bot.
|
||||
*/
|
||||
export class GBMinService {
|
||||
public core: IGBCoreService;
|
||||
public conversationalService: IGBConversationalService;
|
||||
|
@ -94,19 +105,19 @@ export class GBMinService {
|
|||
*
|
||||
* @return Loaded minimal bot instance.
|
||||
*
|
||||
* */
|
||||
*/
|
||||
|
||||
public async buildMin(
|
||||
bootInstance: GuaribasInstance,
|
||||
bootInstance: IGBInstance,
|
||||
server: any,
|
||||
appPackages: IGBPackage[],
|
||||
instances: GuaribasInstance[],
|
||||
instances: IGBInstance[],
|
||||
deployer: GBDeployer
|
||||
): Promise<GBMinInstance> {
|
||||
) {
|
||||
// Serves default UI on root address '/'.
|
||||
|
||||
const uiPackage = 'default.gbui';
|
||||
server.use('/', express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||
server.use('/', express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||
|
||||
await Promise.all(
|
||||
instances.map(async instance => {
|
||||
|
@ -129,7 +140,7 @@ export class GBMinService {
|
|||
|
||||
// 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.
|
||||
|
||||
|
@ -141,15 +152,14 @@ export class GBMinService {
|
|||
server.post(url, async (req, res) => {
|
||||
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.
|
||||
|
||||
const uiUrl = `/${instance.botId}`;
|
||||
server.use(uiUrl, express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||
server.use(uiUrl, express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||
|
||||
logger.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
|
||||
const state = `${instance.instanceId}${Math.floor(Math.random() * 1000000000)}`;
|
||||
GBLog.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
|
||||
|
||||
// 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
|
||||
|
@ -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) => {
|
||||
const state = await min.adminService.getValue(min.instance.instanceId, 'AntiCSRFAttackState');
|
||||
const state = await min.adminService.getValue(instance.instanceId, 'AntiCSRFAttackState');
|
||||
if (req.query.state !== state) {
|
||||
const msg = 'WARNING: state field was not provided as anti-CSRF token';
|
||||
logger.error(msg);
|
||||
GBLog.error(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
const authenticationContext = new AuthenticationContext(
|
||||
UrlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
||||
urlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
||||
);
|
||||
const resource = 'https://graph.microsoft.com';
|
||||
authenticationContext.acquireTokenWithAuthorizationCode(
|
||||
req.query.code,
|
||||
UrlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
||||
urlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
||||
resource,
|
||||
instance.authenticatorClientId,
|
||||
instance.authenticatorClientSecret,
|
||||
async (err, token) => {
|
||||
if (err) {
|
||||
const msg = `Error acquiring token: ${err}`;
|
||||
logger.error(msg);
|
||||
GBLog.error(msg);
|
||||
res.send(msg);
|
||||
} else {
|
||||
await this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken);
|
||||
await this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken);
|
||||
await this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString());
|
||||
await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', null);
|
||||
this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken);
|
||||
this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken);
|
||||
this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString());
|
||||
this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', undefined);
|
||||
res.redirect(min.instance.botEndpoint);
|
||||
}
|
||||
}
|
||||
|
@ -202,15 +212,15 @@ export class GBMinService {
|
|||
}
|
||||
|
||||
private handleOAuthRequests(server: any, min: GBMinInstance) {
|
||||
server.get(`/${min.instance.botId}/auth`, function(req, res) {
|
||||
let authorizationUrl = UrlJoin(
|
||||
server.get(`/${min.instance.botId}/auth`, (req, res) => {
|
||||
let authorizationUrl = urlJoin(
|
||||
min.instance.authenticatorAuthorityHostUrl,
|
||||
min.instance.authenticatorTenant,
|
||||
'/oauth2/authorize'
|
||||
);
|
||||
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
@ -218,16 +228,16 @@ export class GBMinService {
|
|||
/**
|
||||
* 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;
|
||||
if (botId === '[default]') {
|
||||
botId = bootInstance.botId;
|
||||
}
|
||||
const instance = await this.core.loadInstance(botId);
|
||||
if (instance) {
|
||||
if (instance !== undefined) {
|
||||
const speechToken = await this.getSTSToken(instance);
|
||||
let theme = instance.theme;
|
||||
if (!theme) {
|
||||
if (theme !== undefined) {
|
||||
theme = 'default.gbtheme';
|
||||
}
|
||||
res.send(
|
||||
|
@ -245,7 +255,7 @@ export class GBMinService {
|
|||
} else {
|
||||
const error = `Instance not found: ${botId}.`;
|
||||
res.sendStatus(error);
|
||||
logger.error(error);
|
||||
GBLog.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,11 +276,13 @@ export class GBMinService {
|
|||
|
||||
try {
|
||||
const json = await request(options);
|
||||
|
||||
return Promise.resolve(JSON.parse(json));
|
||||
} catch (error) {
|
||||
const msg = `[botId:${
|
||||
instance.botId
|
||||
}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
|
||||
|
||||
return Promise.reject(new Error(msg));
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +294,6 @@ export class GBMinService {
|
|||
*
|
||||
*/
|
||||
private async getSTSToken(instance: any) {
|
||||
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
|
||||
|
||||
const options = {
|
||||
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
|
||||
|
@ -296,6 +307,7 @@ export class GBMinService {
|
|||
return await request(options);
|
||||
} catch (error) {
|
||||
const msg = `Error calling Speech to Text client. Error is: ${error}.`;
|
||||
|
||||
return Promise.reject(new Error(msg));
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +346,7 @@ export class GBMinService {
|
|||
}
|
||||
|
||||
private invokeLoadBot(appPackages: any[], min: GBMinInstance, server: any) {
|
||||
const sysPackages = new Array<IGBPackage>();
|
||||
const sysPackages : IGBPackage[] = [];
|
||||
// NOTE: A semicolon is necessary before this line.
|
||||
[
|
||||
GBCorePackage,
|
||||
|
@ -342,8 +354,8 @@ export class GBMinService {
|
|||
GBAdminPackage,
|
||||
GBKBPackage,
|
||||
GBAnalyticsPackage,
|
||||
GBCustomerSatisfactionPackage,
|
||||
GBWhatsappPackage
|
||||
GBCustomerSatisfactionPackage
|
||||
// DISABLED: GBWhatsappPackage
|
||||
].forEach(sysPackage => {
|
||||
const p = Object.create(sysPackage.prototype) as IGBPackage;
|
||||
p.loadBot(min);
|
||||
|
@ -351,21 +363,21 @@ export class GBMinService {
|
|||
if (sysPackage.name === 'GBWhatsappPackage') {
|
||||
const url = '/instances/:botId/whatsapp';
|
||||
server.post(url, (req, res) => {
|
||||
p.channel.received(req, res);
|
||||
(p as any).channel.received(req, res);
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
appPackages.forEach(p => {
|
||||
p.sysPackages = sysPackages;
|
||||
p.loadBot(min);
|
||||
if (p.getDialogs !== undefined) {
|
||||
let dialogs = p.getDialogs(min);
|
||||
const dialogs = p.getDialogs(min);
|
||||
dialogs.forEach(dialog => {
|
||||
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 => {
|
||||
// Get loaded user state
|
||||
const state = await conversationState.get(context);
|
||||
const step = await min.dialogs.createContext(context, state);
|
||||
step.context.activity.locale = 'en-US'; // TODO: Make dynamic.
|
||||
const step = await min.dialogs.createContext(context);
|
||||
step.context.activity.locale = 'en-US';
|
||||
|
||||
try {
|
||||
const user = await min.userProfile.get(context, {});
|
||||
|
@ -398,11 +409,11 @@ export class GBMinService {
|
|||
});
|
||||
user.loaded = true;
|
||||
user.subjects = [];
|
||||
user.cb = null;
|
||||
user.cb = undefined;
|
||||
await min.userProfile.set(step.context, user);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
GBLog.info(
|
||||
`User>: ${context.activity.text} (${context.activity.type}, ${context.activity.name}, ${
|
||||
context.activity.channelId
|
||||
}, {context.activity.value})`
|
||||
|
@ -410,7 +421,7 @@ export class GBMinService {
|
|||
if (context.activity.type === 'conversationUpdate' && context.activity.membersAdded.length > 0) {
|
||||
const member = context.activity.membersAdded[0];
|
||||
if (member.name === 'GeneralBots') {
|
||||
logger.info(`Bot added to conversation, starting chat...`);
|
||||
GBLog.info(`Bot added to conversation, starting chat...`);
|
||||
appPackages.forEach(e => {
|
||||
e.onNewSession(min, step);
|
||||
});
|
||||
|
@ -418,7 +429,7 @@ export class GBMinService {
|
|||
|
||||
await step.beginDialog('/');
|
||||
} else {
|
||||
logger.info(`Member added to conversation: ${member.name}`);
|
||||
GBLog.info(`Member added to conversation: ${member.name}`);
|
||||
}
|
||||
|
||||
// Processes messages.
|
||||
|
@ -430,14 +441,12 @@ export class GBMinService {
|
|||
} else if (context.activity.type === 'event') {
|
||||
// Empties dialog stack before going to the target.
|
||||
|
||||
// TODO: Understand MSFT changes: await step.endAll();
|
||||
|
||||
await this.processEventActivity(context, step);
|
||||
}
|
||||
await conversationState.saveChanges(context, true);
|
||||
} catch (error) {
|
||||
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.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') {
|
||||
await step.beginDialog('/whoAmI');
|
||||
} else if (context.activity.name === 'showSubjects') {
|
||||
await step.beginDialog('/menu');
|
||||
await step.beginDialog('/menu', undefined);
|
||||
} else if (context.activity.name === 'giveFeedback') {
|
||||
await step.beginDialog('/feedback', {
|
||||
fromMenu: true
|
||||
|
@ -457,7 +466,7 @@ export class GBMinService {
|
|||
} else if (context.activity.name === 'showFAQ') {
|
||||
await step.beginDialog('/faq');
|
||||
} else if (context.activity.name === 'answerEvent') {
|
||||
await step.beginDialog('/answerEvent', {
|
||||
await step.beginDialog('/answerEvent', <AskDialogArgs>{
|
||||
questionId: context.activity.data,
|
||||
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.
|
||||
|
||||
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) {
|
||||
let mainMethod = context.activity.text;
|
||||
const mainMethod = context.activity.text;
|
||||
|
||||
min.sandbox.context = context;
|
||||
min.sandbox.step = step;
|
||||
min.sandbox[mainMethod].bind(min.sandbox);
|
||||
await min.sandbox[mainMethod]();
|
||||
min.sandBoxMap[mainMethod].context = context;
|
||||
min.sandBoxMap[mainMethod].step = step;
|
||||
min.sandBoxMap[mainMethod][mainMethod].bind(min.sandBoxMap[mainMethod]);
|
||||
await min.sandBoxMap[mainMethod][mainMethod]();
|
||||
} else if (context.activity.text === 'admin') {
|
||||
await step.beginDialog('/admin');
|
||||
|
||||
// Checks for /menu JSON signature.
|
||||
} else if (context.activity.text.startsWith('{"title"')) {
|
||||
await step.beginDialog('/menu', {
|
||||
data: JSON.parse(context.activity.text)
|
||||
});
|
||||
await step.beginDialog('/menu', JSON.parse(context.activity.text));
|
||||
// Otherwise, continue to the active dialog in the stack.
|
||||
} else {
|
||||
const user = await min.userProfile.get(context, {});
|
||||
if (step.activeDialog) {
|
||||
if (step.activeDialog !== undefined) {
|
||||
await step.continueDialog();
|
||||
} else {
|
||||
await step.beginDialog('/answer', {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -33,18 +33,19 @@
|
|||
'use strict';
|
||||
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance, IGBCoreService } from 'botlib';
|
||||
import { GBLog, GBMinInstance, GBService, IGBCoreService } from 'botlib';
|
||||
import * as fs from 'fs';
|
||||
import { GBDeployer } from './GBDeployer';
|
||||
import { TSCompiler } from './TSCompiler';
|
||||
import DialogClass from './GBAPIService';
|
||||
|
||||
const walkPromise = require('walk-promise');
|
||||
const logger = require('../../../src/logger');
|
||||
|
||||
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');
|
||||
var beautify = require('js-beautify').js;
|
||||
const beautify = require('js-beautify').js;
|
||||
|
||||
/**
|
||||
* @fileoverview Virtualization services for emulation of BASIC.
|
||||
|
@ -55,11 +56,13 @@ var beautify = require('js-beautify').js;
|
|||
* 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();
|
||||
|
||||
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
||||
|
||||
const files = await walkPromise(folder);
|
||||
this.addHearDialog(min);
|
||||
|
||||
|
@ -74,7 +77,7 @@ export class GBVMService implements IGBCoreService {
|
|||
const mainName = file.name.replace(/\-|\./g, '');
|
||||
min.scriptMap[file.name] = mainName;
|
||||
|
||||
const filename = UrlJoin(folder, file.name);
|
||||
const filename = urlJoin(folder, file.name);
|
||||
fs.watchFile(filename, async () => {
|
||||
await this.run(filename, min, deployer, mainName);
|
||||
});
|
||||
|
@ -110,6 +113,10 @@ export class GBVMService implements IGBCoreService {
|
|||
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) => {
|
||||
return `sys().createABotFarmUsing (${$3})`;
|
||||
});
|
||||
|
@ -127,7 +134,7 @@ export class GBVMService implements IGBCoreService {
|
|||
// Converts General Bots BASIC into regular VBS
|
||||
|
||||
const basicCode: string = fs.readFileSync(filename, 'utf8');
|
||||
const vbsCode = await this.convertGBASICToVBS(basicCode);
|
||||
const vbsCode = this.convertGBASICToVBS(basicCode);
|
||||
const vbsFile = `${filename}.compiled`;
|
||||
fs.writeFileSync(vbsFile, vbsCode, 'utf8');
|
||||
|
||||
|
@ -156,9 +163,9 @@ export class GBVMService implements IGBCoreService {
|
|||
let parsedCode = code;
|
||||
const hearExp = /(\w+).*hear.*\(\)/;
|
||||
|
||||
let match1;
|
||||
let match1 = hearExp.exec(code);
|
||||
|
||||
while ((match1 = hearExp.exec(code))) {
|
||||
while (match1 !== undefined) {
|
||||
let pos = 0;
|
||||
|
||||
// Writes async body.
|
||||
|
@ -178,8 +185,9 @@ export class GBVMService implements IGBCoreService {
|
|||
|
||||
let right = 0;
|
||||
let left = 1;
|
||||
let match2;
|
||||
while ((match2 = /\{|\}/.exec(tempCode))) {
|
||||
let match2 = /\{|\}/.exec(tempCode);
|
||||
|
||||
while (match2 !== undefined) {
|
||||
const c = tempCode.substring(match2.index, match2.index + 1);
|
||||
|
||||
if (c === '}') {
|
||||
|
@ -194,6 +202,7 @@ export class GBVMService implements IGBCoreService {
|
|||
if (left === right) {
|
||||
break;
|
||||
}
|
||||
match1 = hearExp.exec(code);
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
code = parsedCode;
|
||||
match2 = /\{|\}/.exec(tempCode);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const sandbox: DialogClass = new DialogClass(min);
|
||||
const sandbox: DialogClass = new DialogClass(min, deployer);
|
||||
const context = vm.createContext(sandbox);
|
||||
vm.runInContext(parsedCode, context);
|
||||
min.sandbox = sandbox;
|
||||
await deployer.deployScriptToStorage(min.instanceId, filename);
|
||||
logger.info(`[GBVMService] Finished loading of ${filename}`);
|
||||
min.sandBoxMap[mainName] = sandbox;
|
||||
GBLog.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(/("[^"]*"|'[^']*')|\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) => {
|
||||
return $1 == undefined ? 'this.hear' : $1;
|
||||
return $1 === undefined ? 'this.hear' : $1;
|
||||
});
|
||||
code = code.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, ($0, $1) => {
|
||||
return $1 == undefined ? 'this.sendEmail' : $1;
|
||||
return $1 === undefined ? 'this.sendEmail' : $1;
|
||||
});
|
||||
|
||||
// await insertion.
|
||||
|
@ -245,7 +254,7 @@ export class GBVMService implements IGBCoreService {
|
|||
min.dialogs.add(
|
||||
new WaterfallDialog('/hear', [
|
||||
async step => {
|
||||
step.activeDialog.state.cbId = step.options['id'];
|
||||
step.activeDialog.state.cbId = (step.options as any).id;
|
||||
|
||||
return await step.prompt('textPrompt', {});
|
||||
},
|
||||
|
@ -255,7 +264,7 @@ export class GBVMService implements IGBCoreService {
|
|||
|
||||
const cbId = step.activeDialog.state.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();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,12 +36,14 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import { GBLog } from 'botlib';
|
||||
import * as ts from 'typescript';
|
||||
const logger = require('../../../src/logger');
|
||||
|
||||
/**
|
||||
* Wrapper for a TypeScript compiler.
|
||||
*/
|
||||
export class TSCompiler {
|
||||
|
||||
|
||||
private static shouldIgnoreError(diagnostic) {
|
||||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||
|
||||
|
@ -76,11 +78,11 @@ export class TSCompiler {
|
|||
if (!TSCompiler.shouldIgnoreError(diagnostic)) {
|
||||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||
|
||||
if (diagnostic.file) {
|
||||
if (diagnostic.file !== undefined) {
|
||||
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 {
|
||||
logger.error(`${message}`);
|
||||
GBLog.error(`${message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
import {GBImporter} from '../services/GBImporterService';
|
||||
import { GBImporter } from '../services/GBImporterService';
|
||||
|
||||
describe('Hello function', () => {
|
||||
|
||||
it('should return empty test', () => {
|
||||
const service = new GBImporter(null);
|
||||
//service.importIfNotExistsBotPackage(null, null);
|
||||
const service = new GBImporter(undefined);
|
||||
const result = 0;
|
||||
expect(result).to.equal(0);
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -42,9 +42,7 @@ import { GBVMService } from '../services/GBVMService';
|
|||
describe('Load function', () => {
|
||||
it('should fail on invalid file', () => {
|
||||
try {
|
||||
// const service = new GBVMService();
|
||||
// TODO: service.loadJS('invalid.file', null, null, null, null);
|
||||
|
||||
const service = new GBVMService();
|
||||
} catch (error) {
|
||||
expect(error).to.equal(0);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -38,12 +38,14 @@
|
|||
|
||||
import { BotAdapter } from 'botbuilder';
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
import { AzureText } from 'pragmatismo-io-framework';
|
||||
import { CSService } from '../services/CSService';
|
||||
import { Messages } from '../strings';
|
||||
|
||||
/**
|
||||
* Dialog for feedback collecting.
|
||||
*/
|
||||
export class FeedbackDialog extends IGBDialog {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -58,13 +60,7 @@ export class FeedbackDialog extends IGBDialog {
|
|||
new WaterfallDialog('/feedbackNumber', [
|
||||
async step => {
|
||||
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();
|
||||
},
|
||||
async step => {
|
||||
|
@ -73,6 +69,7 @@ export class FeedbackDialog extends IGBDialog {
|
|||
const user = await min.userProfile.get(context, {});
|
||||
await service.updateConversationRate(user.conversation, rate);
|
||||
await step.context.sendActivity(Messages[locale].thanks);
|
||||
|
||||
return await step.next();
|
||||
}
|
||||
])
|
||||
|
@ -84,29 +81,26 @@ export class FeedbackDialog extends IGBDialog {
|
|||
const locale = step.context.activity.locale;
|
||||
|
||||
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);
|
||||
},
|
||||
async step => {
|
||||
|
||||
const locale = step.context.activity.locale;
|
||||
const rate = await AzureText.getSentiment(
|
||||
min.instance.textAnalyticsKey,
|
||||
min.instance.textAnalyticsKey,
|
||||
min.instance.textAnalyticsEndpoint,
|
||||
min.conversationalService.getCurrentLanguage(step),
|
||||
step.result
|
||||
step.result
|
||||
);
|
||||
|
||||
if (rate > 0.5) {
|
||||
await step.context.sendActivity(Messages[locale].glad_you_liked);
|
||||
} else {
|
||||
await step.context.sendActivity(Messages[locale].we_will_improve);
|
||||
}
|
||||
|
||||
// TODO: Record.
|
||||
}
|
||||
return await step.replaceDialog('/ask', { isReturning: true });
|
||||
|
||||
}
|
||||
])
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,15 +36,16 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
|
||||
import { BotAdapter } from 'botbuilder';
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { CSService } from '../services/CSService';
|
||||
import { Messages } from '../strings';
|
||||
const logger = require('../../../src/logger');
|
||||
|
||||
/**
|
||||
* Dialog for collecting quality of answer.
|
||||
*/
|
||||
export class QualityDialog extends IGBDialog {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -63,11 +64,11 @@ export class QualityDialog extends IGBDialog {
|
|||
const score = step.result;
|
||||
|
||||
setTimeout(
|
||||
() => min.conversationalService.sendEvent(step, 'stop', null),
|
||||
() => min.conversationalService.sendEvent(step, 'stop', undefined),
|
||||
400
|
||||
);
|
||||
|
||||
if (score == 0) {
|
||||
if (score === 0) {
|
||||
await step.context.sendActivity(Messages[locale].im_sorry_lets_try);
|
||||
} else {
|
||||
await step.context.sendActivity(Messages[locale].great_thanks);
|
||||
|
@ -79,6 +80,7 @@ export class QualityDialog extends IGBDialog {
|
|||
);
|
||||
await step.replaceDialog('/ask', { isReturning: true });
|
||||
}
|
||||
|
||||
return await step.next();
|
||||
}
|
||||
]));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,32 +36,37 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import urlJoin = require('url-join');
|
||||
import { FeedbackDialog } from './dialogs/FeedbackDialog';
|
||||
import { QualityDialog } from './dialogs/QualityDialog';
|
||||
import { GuaribasQuestionAlternate } from './models/index';
|
||||
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
|
||||
/**
|
||||
* Package for customer-satisfaction.gblib.
|
||||
*/
|
||||
export class GBCustomerSatisfactionPackage implements IGBPackage {
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
core.sequelize.addModels([
|
||||
GuaribasQuestionAlternate
|
||||
]);
|
||||
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 {
|
||||
core.sequelize.addModels([GuaribasQuestionAlternate]);
|
||||
}
|
||||
public loadBot(min: GBMinInstance): void {
|
||||
FeedbackDialog.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';
|
||||
|
||||
import {
|
||||
DataTypeDate,
|
||||
DataTypeDecimal,
|
||||
DataTypes,
|
||||
DataTypeUUIDv4
|
||||
} from 'sequelize';
|
||||
|
||||
import {
|
||||
AutoIncrement,
|
||||
BelongsTo,
|
||||
BelongsToMany,
|
||||
Column,
|
||||
CreatedAt,
|
||||
DataType,
|
||||
ForeignKey,
|
||||
HasMany,
|
||||
IsUUID,
|
||||
Length,
|
||||
Model,
|
||||
PrimaryKey,
|
||||
Sequelize,
|
||||
Table,
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript';
|
||||
Table} from 'sequelize-typescript';
|
||||
|
||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||
|
||||
/**
|
||||
* List of saved alternate questions.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -33,6 +33,9 @@
|
|||
import { GuaribasConversation } from '../../analytics.gblib/models';
|
||||
import { GuaribasQuestionAlternate } from '../models';
|
||||
|
||||
/**
|
||||
* Customer Satisfaction Service Layer.
|
||||
*/
|
||||
export class CSService {
|
||||
|
||||
public async resolveQuestionAlternate(
|
||||
|
@ -62,6 +65,7 @@ export class CSService {
|
|||
rate: number
|
||||
): Promise<GuaribasConversation> {
|
||||
conversation.rate = rate;
|
||||
|
||||
return conversation.save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -38,14 +38,22 @@
|
|||
|
||||
import { BotAdapter } from 'botbuilder';
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
|
||||
import { AzureText } from 'pragmatismo-io-framework';
|
||||
import { Messages } from '../strings';
|
||||
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 {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -55,156 +63,135 @@ export class AskDialog extends IGBDialog {
|
|||
*/
|
||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
||||
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(
|
||||
new WaterfallDialog('/answerEvent', [
|
||||
async step => {
|
||||
if (step.options && step.options['questionId']) {
|
||||
const question = await service.getQuestionById(min.instance.instanceId, step.options['questionId']);
|
||||
const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
|
||||
private static getAskDialog(min: GBMinInstance) {
|
||||
return [
|
||||
async step => {
|
||||
const locale = step.context.activity.locale;
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
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.
|
||||
|
||||
await service.sendAnswer(min.conversationalService, step, answer);
|
||||
|
||||
await step.replaceDialog('/ask', { isReturning: true });
|
||||
}
|
||||
return await step.next();
|
||||
},
|
||||
async step => {
|
||||
if (step.result) {
|
||||
return await step.replaceDialog('/answer', { query: step.result });
|
||||
} else {
|
||||
return await step.next();
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
min.dialogs.add(
|
||||
new WaterfallDialog('/answer', [
|
||||
async step => {
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
let text = step.options['query'];
|
||||
if (!text) {
|
||||
throw new Error(`/answer being called with no args query text.`);
|
||||
private static getAnswerDialog(min: GBMinInstance, service: KBService) {
|
||||
return [
|
||||
async step => {
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
let text = step.options.query;
|
||||
if (!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.
|
||||
|
||||
await min.conversationalService.sendEvent(step, 'stop', null);
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
// If there is some result, answer immediately.
|
||||
if (resultsA !== undefined && resultsA.answer !== undefined) {
|
||||
// Saves some context info.
|
||||
user.isAsking = false;
|
||||
user.lastQuestionId = resultsA.questionId;
|
||||
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 (resultsA && resultsA.answer) {
|
||||
if (resultsB !== undefined && resultsB.answer !== undefined) {
|
||||
// Saves some context info.
|
||||
|
||||
user.isAsking = false;
|
||||
user.lastQuestionId = resultsA.questionId;
|
||||
await min.userProfile.set(step.context, user);
|
||||
|
||||
const user2 = await min.userProfile.get(step.context, {});
|
||||
user2.isAsking = false;
|
||||
user2.lastQuestionId = resultsB.questionId;
|
||||
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.
|
||||
|
||||
await service.sendAnswer(min.conversationalService, step, resultsA.answer);
|
||||
|
||||
// Goes to ask loop, again.
|
||||
await service.sendAnswer(min.conversationalService, step, resultsB.answer);
|
||||
|
||||
return await step.replaceDialog('/ask', { isReturning: true });
|
||||
} 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 });
|
||||
} 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(
|
||||
new WaterfallDialog('/ask', [
|
||||
async step => {
|
||||
const locale = step.context.activity.locale;
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
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);
|
||||
}
|
||||
return await step.next();
|
||||
},
|
||||
async step => {
|
||||
if (step.result) {
|
||||
return await step.replaceDialog('/answer', { query: step.result });
|
||||
} else {
|
||||
return await step.next();
|
||||
}
|
||||
private static getAnswerEventDialog(service: KBService, min: GBMinInstance) {
|
||||
return [
|
||||
async step => {
|
||||
const data = step.options as AskDialogArgs;
|
||||
if (data !== undefined && data.questionId !== undefined) {
|
||||
const question = await service.getQuestionById(min.instance.instanceId, data.questionId);
|
||||
const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
|
||||
// Sends the answer to all outputs, including projector.
|
||||
await service.sendAnswer(min.conversationalService, step, answer);
|
||||
await step.replaceDialog('/ask', { isReturning: true });
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
|
||||
return await step.next();
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -38,11 +38,13 @@
|
|||
|
||||
import { BotAdapter } from 'botbuilder';
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
import { Messages } from '../strings';
|
||||
import { KBService } from './../services/KBService';
|
||||
|
||||
/**
|
||||
* Handle display of FAQ allowing direct access to KB.
|
||||
*/
|
||||
export class FaqDialog extends IGBDialog {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -56,15 +58,16 @@ export class FaqDialog extends IGBDialog {
|
|||
|
||||
min.dialogs.add(new WaterfallDialog('/faq', [
|
||||
async step => {
|
||||
const data = await service.getFaqBySubjectArray('faq', null);
|
||||
const data = await service.getFaqBySubjectArray('faq', undefined);
|
||||
const locale = step.context.activity.locale;
|
||||
if (data) {
|
||||
if (data !== undefined) {
|
||||
await min.conversationalService.sendEvent(step, 'play', {
|
||||
playerType: 'bullet',
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
|
||||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -37,17 +36,26 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
import urlJoin = require('url-join');
|
||||
|
||||
import { BotAdapter, CardFactory, MessageFactory } from 'botbuilder';
|
||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||
import { IGBDialog } from 'botlib';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { AzureText } from 'pragmatismo-io-framework';
|
||||
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||
import { GuaribasSubject } from '../models';
|
||||
import { KBService } from '../services/KBService';
|
||||
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 {
|
||||
/**
|
||||
* Setup dialogs flows and define services call.
|
||||
|
@ -58,70 +66,56 @@ export class MenuDialog extends IGBDialog {
|
|||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
||||
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 => {
|
||||
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;
|
||||
|
||||
if (step.options && step.options['data']) {
|
||||
const subject = step.options['data'];
|
||||
|
||||
if (Object.keys(args).length > 0) {
|
||||
// If there is a shortcut specified as subject destination, go there.
|
||||
if (args.to !== null) {
|
||||
const dialog = args.to.split(':')[1];
|
||||
|
||||
if (subject.to) {
|
||||
const dialog = subject.to.split(':')[1];
|
||||
await step.replaceDialog('/' + dialog);
|
||||
await step.endDialog();
|
||||
return;
|
||||
return await step.replaceDialog(`/${dialog}`);
|
||||
}
|
||||
|
||||
// Adds to bot a perception of a new subject.
|
||||
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
user.subjects.push(subject);
|
||||
rootSubjectId = subject.subjectId;
|
||||
user.subjects.push(args);
|
||||
// tslint:disable-next-line: no-null-keyword
|
||||
rootSubjectId = args.subjectId === undefined ? null : args.subjectId;
|
||||
|
||||
// Whenever a subject is selected, shows a faq about it.
|
||||
|
||||
if (user.subjects.length > 0) {
|
||||
const data = await service.getFaqBySubjectArray(
|
||||
'menu',
|
||||
user.subjects
|
||||
);
|
||||
const list = await service.getFaqBySubjectArray('menu', user.subjects);
|
||||
await min.conversationalService.sendEvent(step, 'play', {
|
||||
playerType: 'bullet',
|
||||
data: data.slice(0, 10)
|
||||
data: list.slice(0, 10)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
user.subjects = [];
|
||||
|
||||
await step.context.sendActivity(Messages[locale].here_is_subjects); // TODO: Handle rnd.
|
||||
await step.context.sendActivity(Messages[locale].here_is_subjects);
|
||||
user.isAsking = false;
|
||||
}
|
||||
|
||||
const msg = MessageFactory.text('');
|
||||
const attachments = [];
|
||||
|
||||
const data = await service.getSubjectItems(
|
||||
min.instance.instanceId,
|
||||
rootSubjectId
|
||||
);
|
||||
|
||||
const data = await service.getSubjectItems(min.instance.instanceId, rootSubjectId);
|
||||
msg.attachmentLayout = 'carousel';
|
||||
|
||||
data.forEach(function(item: GuaribasSubject) {
|
||||
data.forEach((item: GuaribasSubject) => {
|
||||
const subject = item;
|
||||
const card = CardFactory.heroCard(
|
||||
subject.title,
|
||||
subject.description,
|
||||
CardFactory.images([
|
||||
UrlJoin('/kb', min.instance.kb, 'subjects', 'subject.png')
|
||||
]),
|
||||
CardFactory.images([urlJoin('/kb', min.instance.kb, 'subjects', 'subject.png')]),
|
||||
CardFactory.actions([
|
||||
{
|
||||
channelData: null,
|
||||
{
|
||||
type: 'postBack',
|
||||
title: Messages[locale].menu_select,
|
||||
value: JSON.stringify({
|
||||
|
@ -134,30 +128,23 @@ export class MenuDialog extends IGBDialog {
|
|||
}
|
||||
])
|
||||
);
|
||||
|
||||
attachments.push(card);
|
||||
});
|
||||
|
||||
if (attachments.length === 0) {
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
|
||||
if (user.subjects && user.subjects.length > 0) {
|
||||
await step.context.sendActivity(
|
||||
Messages[locale].lets_search(
|
||||
KBService.getFormattedSubjectItems(user.subjects)
|
||||
)
|
||||
Messages[locale].lets_search(KBService.getFormattedSubjectItems(user.subjects))
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
msg.attachments = attachments;
|
||||
await step.context.sendActivity(msg);
|
||||
}
|
||||
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
user.isAsking = true;
|
||||
|
||||
return await step.next();
|
||||
}
|
||||
]));
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,43 +36,37 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
|
||||
import { GBMinInstance, IGBPackage } from 'botlib';
|
||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
|
||||
|
||||
import { IGBCoreService } from 'botlib';
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
import { AskDialog } from './dialogs/AskDialog';
|
||||
import { FaqDialog } from './dialogs/FaqDialog';
|
||||
import { MenuDialog } from './dialogs/MenuDialog';
|
||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
|
||||
|
||||
/**
|
||||
* Package for kb.gbapp.
|
||||
*/
|
||||
export class GBKBPackage implements IGBPackage {
|
||||
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
core.sequelize.addModels([
|
||||
GuaribasAnswer,
|
||||
GuaribasQuestion,
|
||||
GuaribasSubject
|
||||
]);
|
||||
|
||||
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 {
|
||||
core.sequelize.addModels([GuaribasAnswer, GuaribasQuestion, GuaribasSubject]);
|
||||
}
|
||||
public loadBot(min: GBMinInstance): void {
|
||||
|
||||
AskDialog.setup(min.bot, min);
|
||||
FaqDialog.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';
|
||||
import { GuaribasUser } from '../../security.gblib/models';
|
||||
|
||||
/**
|
||||
* Subjects to group the pair of questions and answers.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasSubject extends Model<GuaribasSubject> {
|
||||
@PrimaryKey
|
||||
|
@ -111,6 +114,9 @@ export class GuaribasSubject extends Model<GuaribasSubject> {
|
|||
public package: GuaribasPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* A question and its metadata.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||
@PrimaryKey
|
||||
|
@ -155,6 +161,7 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
|||
@UpdatedAt
|
||||
public updatedAt: Date;
|
||||
|
||||
//tslint:disable-next-line:no-use-before-declare
|
||||
@ForeignKey(() => GuaribasAnswer)
|
||||
@Column
|
||||
public answerId: number;
|
||||
|
@ -174,6 +181,9 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
|||
public package: GuaribasPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* An answer and its metadata.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasAnswer extends Model<GuaribasAnswer> {
|
||||
@PrimaryKey
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -34,31 +34,37 @@
|
|||
* @fileoverview Knowledge base services and logic.
|
||||
*/
|
||||
|
||||
const logger = require('../../../src/logger');
|
||||
const Path = require('path');
|
||||
const Fs = require('fs');
|
||||
|
||||
const parse = require('bluebird').promisify(require('csv-parse'));
|
||||
const UrlJoin = require('url-join');
|
||||
import urlJoin = require('url-join');
|
||||
const marked = require('marked');
|
||||
const path = require('path');
|
||||
const asyncPromise = require('async-promises');
|
||||
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 { Sequelize } from 'sequelize-typescript';
|
||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
|
||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
|
||||
import { Messages } from '../strings';
|
||||
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
|
||||
|
||||
/**
|
||||
* Result for quey on KB data.
|
||||
*/
|
||||
export class KBServiceSearchResults {
|
||||
public answer: GuaribasAnswer;
|
||||
public questionId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* All services related to knowledge base management.
|
||||
*/
|
||||
export class KBService {
|
||||
public sequelize: Sequelize;
|
||||
|
||||
|
@ -67,7 +73,7 @@ export class KBService {
|
|||
}
|
||||
|
||||
public static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
|
||||
if (!subjects) {
|
||||
if (subjects === null) {
|
||||
return '';
|
||||
}
|
||||
const out = [];
|
||||
|
@ -115,7 +121,7 @@ export class KBService {
|
|||
}
|
||||
});
|
||||
|
||||
if (question) {
|
||||
if (question !== null) {
|
||||
const answer = await GuaribasAnswer.findOne({
|
||||
where: {
|
||||
instanceId: instanceId,
|
||||
|
@ -126,7 +132,7 @@ export class KBService {
|
|||
return Promise.resolve({ question: question, answer: answer });
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
|
||||
|
@ -156,40 +162,39 @@ export class KBService {
|
|||
query = query.replace('/', ' ');
|
||||
query = query.replace('\\', ' ');
|
||||
|
||||
if (subjects) {
|
||||
if (subjects !== null) {
|
||||
const text = KBService.getSubjectItemsSeparatedBySpaces(subjects);
|
||||
if (text) {
|
||||
if (text !== null) {
|
||||
query = `${query} ${text}`;
|
||||
}
|
||||
}
|
||||
query = `${query}&$filter=instanceId eq ${instance.instanceId}`;
|
||||
try {
|
||||
if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
|
||||
const service = new AzureSearch(
|
||||
instance.searchKey,
|
||||
instance.searchHost,
|
||||
instance.searchIndex,
|
||||
instance.searchIndexer
|
||||
);
|
||||
const results = await service.search(query);
|
||||
if (results && results.length > 0 && results[0]['@search.score'] >= searchScore) {
|
||||
const value = await this.getAnswerById(instance.instanceId, results[0].answerId);
|
||||
if (value) {
|
||||
return Promise.resolve({ answer: value, questionId: results[0].questionId });
|
||||
} else {
|
||||
return Promise.resolve({ answer: null, questionId: 0 });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const data = await this.getAnswerByText(instance.instanceId, query);
|
||||
if (data) {
|
||||
return Promise.resolve({ answer: data.answer, questionId: data.question.questionId });
|
||||
|
||||
// tslint:disable:no-unsafe-any
|
||||
if (instance.searchKey !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
|
||||
const service = new AzureSearch(
|
||||
instance.searchKey,
|
||||
instance.searchHost,
|
||||
instance.searchIndex,
|
||||
instance.searchIndexer
|
||||
);
|
||||
const results = await service.search(query);
|
||||
|
||||
if (results && results.length > 0 && results[0]['@search.score'] >= searchScore) {
|
||||
const value = await this.getAnswerById(instance.instanceId, results[0].answerId);
|
||||
if (value !== null) {
|
||||
return Promise.resolve({ answer: value, questionId: results[0].questionId });
|
||||
} else {
|
||||
return Promise.resolve({ answer: null, questionId: 0 });
|
||||
return Promise.resolve({ answer: undefined, questionId: 0 });
|
||||
}
|
||||
}
|
||||
} catch (reason) {
|
||||
return Promise.reject(new Error(reason));
|
||||
} else {
|
||||
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[]> {
|
||||
|
||||
if (subjects) {
|
||||
const where = {
|
||||
from: from,
|
||||
// tslint:disable-next-line: no-null-keyword
|
||||
subject1: null,
|
||||
// tslint:disable-next-line: no-null-keyword
|
||||
subject2: null,
|
||||
// tslint:disable-next-line: no-null-keyword
|
||||
subject3: null,
|
||||
// tslint:disable-next-line: no-null-keyword
|
||||
subject4: null
|
||||
};
|
||||
|
||||
if (subjects[0]) {
|
||||
if (subjects[0] && subjects[0].internalId) {
|
||||
where.subject1 = subjects[0].internalId;
|
||||
}
|
||||
|
||||
if (subjects[1]) {
|
||||
if (subjects[1] && subjects[1].internalId) {
|
||||
where.subject2 = subjects[1].internalId;
|
||||
}
|
||||
|
||||
if (subjects[2]) {
|
||||
if (subjects[2] && subjects[2].internalId) {
|
||||
where.subject3 = subjects[2].internalId;
|
||||
}
|
||||
|
||||
if (subjects[3]) {
|
||||
if (subjects[3] && subjects[3].internalId) {
|
||||
where.subject4 = subjects[3].internalId;
|
||||
}
|
||||
|
||||
|
@ -270,12 +278,12 @@ export class KBService {
|
|||
// Extracts answer from external media if any.
|
||||
|
||||
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)) {
|
||||
answer = Fs.readFileSync(mediaFilename, 'utf8');
|
||||
format = '.md';
|
||||
} else {
|
||||
logger.info(`[GBImporter] File not found: ${mediaFilename}.`);
|
||||
GBLog.info(`[GBImporter] File not found: ${mediaFilename}.`);
|
||||
answer = '';
|
||||
}
|
||||
}
|
||||
|
@ -290,13 +298,13 @@ export class KBService {
|
|||
let indexer = 0;
|
||||
|
||||
subjectArray.forEach(element => {
|
||||
if (indexer == 0) {
|
||||
if (indexer === 0) {
|
||||
subject1 = subjectArray[indexer].substring(0, 63);
|
||||
} else if (indexer == 1) {
|
||||
} else if (indexer === 1) {
|
||||
subject2 = subjectArray[indexer].substring(0, 63);
|
||||
} else if (indexer == 2) {
|
||||
} else if (indexer === 2) {
|
||||
subject3 = subjectArray[indexer].substring(0, 63);
|
||||
} else if (indexer == 3) {
|
||||
} else if (indexer === 3) {
|
||||
subject4 = subjectArray[indexer].substring(0, 63);
|
||||
}
|
||||
indexer++;
|
||||
|
@ -309,7 +317,7 @@ export class KBService {
|
|||
content: answer,
|
||||
format: format,
|
||||
packageId: packageId,
|
||||
prevId: lastQuestionId ? lastQuestionId : 0
|
||||
prevId: lastQuestionId !== null ? lastQuestionId : 0
|
||||
});
|
||||
|
||||
const question1 = await GuaribasQuestion.create({
|
||||
|
@ -325,8 +333,8 @@ export class KBService {
|
|||
packageId: packageId
|
||||
});
|
||||
|
||||
if (lastAnswer && lastQuestionId) {
|
||||
await lastAnswer.updateAttributes({ nextId: lastQuestionId });
|
||||
if (lastAnswer !== undefined && lastQuestionId !== 0) {
|
||||
await lastAnswer.update({ nextId: lastQuestionId });
|
||||
}
|
||||
lastAnswer = answer1;
|
||||
lastQuestionId = question1.questionId;
|
||||
|
@ -335,21 +343,21 @@ export class KBService {
|
|||
} else {
|
||||
// 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')) {
|
||||
await conversationalService.sendEvent(step, 'play', {
|
||||
playerType: 'video',
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (answer.format === '.md') {
|
||||
|
@ -377,7 +385,7 @@ export class KBService {
|
|||
});
|
||||
} else {
|
||||
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> {
|
||||
// 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.
|
||||
|
||||
|
@ -396,24 +404,22 @@ export class KBService {
|
|||
}
|
||||
|
||||
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(
|
||||
files.map(async file => {
|
||||
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> {
|
||||
const subjects = JSON.parse(Fs.readFileSync(filename, 'utf8'));
|
||||
const subjectsLoaded = JSON.parse(Fs.readFileSync(filename, 'utf8'));
|
||||
|
||||
const doIt = async (subjects: GuaribasSubject[], parentSubjectId: number) => {
|
||||
return asyncPromise.eachSeries(subjects, async item => {
|
||||
const mediaFilename = item.id + '.png';
|
||||
|
||||
const value = await GuaribasSubject.create({
|
||||
internalId: item.id,
|
||||
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) {
|
||||
|
@ -449,7 +456,7 @@ export class KBService {
|
|||
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) {
|
||||
const packageType = Path.extname(localPath);
|
||||
const packageName = Path.basename(localPath);
|
||||
logger.info(`[GBDeployer] Opening package: ${localPath}`);
|
||||
const packageObject = JSON.parse(Fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8'));
|
||||
GBLog.info(`[GBDeployer] Opening package: ${localPath}`);
|
||||
const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
|
||||
|
||||
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);
|
||||
await this.importKbPackage(localPath, p, instance);
|
||||
|
||||
deployer.rebuildIndex(instance);
|
||||
logger.info(`[GBDeployer] Finished import of ${localPath}`);
|
||||
deployer.rebuildIndex(instance, new AzureDeployerService(deployer).getKBSearchSchema(instance.searchIndex));
|
||||
GBLog.info(`[GBDeployer] Finished import of ${localPath}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,8 @@ export const Messages = {
|
|||
here_is_subjects: 'Here are some subjects to choose from...',
|
||||
menu_select: 'Select',
|
||||
lets_search: query =>
|
||||
`Vamos pesquisar sobre ${query}... O que deseja saber?`,
|
||||
see_faq:
|
||||
'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
|
||||
`Lets search for ${query}... What do you want to know?`,
|
||||
see_faq: 'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
|
||||
will_answer_projector:
|
||||
'I\'ll answer on the projector to a better experience...',
|
||||
ask_first_time: 'What are you looking for?'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,37 +36,35 @@
|
|||
|
||||
'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 { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
|
||||
|
||||
/**
|
||||
* Package for the security module.
|
||||
*/
|
||||
export class GBSecurityPackage implements IGBPackage {
|
||||
public sysPackages: IGBPackage[] = null;
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
core.sequelize.addModels([
|
||||
GuaribasGroup,
|
||||
GuaribasUser,
|
||||
GuaribasUserGroup
|
||||
]);
|
||||
|
||||
core;
|
||||
public sysPackages: IGBPackage[];
|
||||
public getDialogs(min: GBMinInstance) {
|
||||
GBLog.verbose(`getDialogs called.`);
|
||||
}
|
||||
|
||||
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.`);
|
||||
}
|
||||
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';
|
||||
|
||||
import {
|
||||
DataTypeDate,
|
||||
DataTypeDecimal,
|
||||
DataTypes,
|
||||
DataTypeUUIDv4
|
||||
} from 'sequelize';
|
||||
|
||||
import {
|
||||
AutoIncrement,
|
||||
BelongsTo,
|
||||
BelongsToMany,
|
||||
Column,
|
||||
CreatedAt,
|
||||
DataType,
|
||||
ForeignKey,
|
||||
HasMany,
|
||||
IsUUID,
|
||||
Length,
|
||||
Model,
|
||||
PrimaryKey,
|
||||
Sequelize,
|
||||
Table,
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript';
|
||||
Table} from 'sequelize-typescript';
|
||||
|
||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||
|
||||
/**
|
||||
* A user and its metadata.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasUser extends Model<GuaribasUser> {
|
||||
@PrimaryKey
|
||||
|
@ -90,6 +80,9 @@ export class GuaribasUser extends Model<GuaribasUser> {
|
|||
public instance: GuaribasInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of users.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasGroup extends Model<GuaribasGroup> {
|
||||
@PrimaryKey
|
||||
|
@ -109,6 +102,9 @@ export class GuaribasGroup extends Model<GuaribasGroup> {
|
|||
public instance: GuaribasInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relation of groups and users.
|
||||
*/
|
||||
@Table
|
||||
export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
|
||||
@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 _ = require('lodash');
|
||||
const Parse = require('csv-parse');
|
||||
const Async = require('async');
|
||||
const UrlJoin = require('url-join');
|
||||
const logger = require('../../../src/logger');
|
||||
import urlJoin = require('url-join');
|
||||
|
||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
||||
import { GBService, IGBInstance } from 'botlib';
|
||||
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
|
||||
|
||||
/**
|
||||
* Security service layer.
|
||||
*/
|
||||
export class SecService extends GBService {
|
||||
|
||||
public async importSecurityFile(localPath: string, instance: IGBInstance) {
|
||||
const security = JSON.parse(
|
||||
Fs.readFileSync(UrlJoin(localPath, 'security.json'), 'utf8')
|
||||
);
|
||||
const security = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'security.json'), 'utf8'));
|
||||
security.groups.forEach(group => {
|
||||
const groupDb = GuaribasGroup.build({
|
||||
instanceId: instance.instanceId,
|
||||
displayName: group.displayName
|
||||
});
|
||||
groupDb.save().then(groupDb => {
|
||||
groupDb.save().then(g1 => {
|
||||
group.users.forEach(user => {
|
||||
const userDb = GuaribasUser.build({
|
||||
instanceId: instance.instanceId,
|
||||
groupId: groupDb.groupId,
|
||||
groupId: g1.groupId,
|
||||
userName: user.userName
|
||||
});
|
||||
userDb.save().then(userDb => {
|
||||
userDb.save().then(user2 => {
|
||||
const userGroup = GuaribasUserGroup.build();
|
||||
userGroup.groupId = groupDb.groupId;
|
||||
userGroup.userId = userDb.userId;
|
||||
userGroup.groupId = g1.groupId;
|
||||
userGroup.userId = user2.userId;
|
||||
userGroup.save();
|
||||
});
|
||||
});
|
||||
|
@ -78,16 +41,15 @@ export class SecService extends GBService {
|
|||
channelName: string,
|
||||
displayName: string
|
||||
): Promise<GuaribasUser> {
|
||||
return new Promise<GuaribasUser>(
|
||||
(resolve, reject) => {
|
||||
|
||||
GuaribasUser.findOne({
|
||||
attributes: ['instanceId', 'internalAddress'],
|
||||
where: {
|
||||
instanceId: instanceId,
|
||||
userSystemId: userSystemId
|
||||
}
|
||||
}).then(user => {
|
||||
return new Promise<GuaribasUser>((resolve, reject) => {
|
||||
GuaribasUser.findOne({
|
||||
attributes: ['instanceId', 'internalAddress'],
|
||||
where: {
|
||||
instanceId: instanceId,
|
||||
userSystemId: userSystemId
|
||||
}
|
||||
})
|
||||
.then(user => {
|
||||
if (!user) {
|
||||
user = GuaribasUser.build();
|
||||
}
|
||||
|
@ -99,7 +61,8 @@ export class SecService extends GBService {
|
|||
user.defaultChannel = channelName;
|
||||
user.save();
|
||||
resolve(user);
|
||||
}).error(reject);
|
||||
});
|
||||
})
|
||||
.error(reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
|
@ -36,39 +36,46 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const UrlJoin = require('url-join');
|
||||
|
||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
|
||||
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { Sequelize } from 'sequelize-typescript';
|
||||
import { WhatsappDirectLine } from './services/WhatsappDirectLine';
|
||||
|
||||
/**
|
||||
* Package for whatsapp.gblib
|
||||
*/
|
||||
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 loadBot(min: GBMinInstance): void {
|
||||
|
||||
// Only loads engine if it is defined on services.json.
|
||||
|
||||
if (min.instance.whatsappBotKey) {
|
||||
this.channel = new WhatsappDirectLine(min.botId, min.instance.whatsappBotKey, min.instance.whatsappServiceKey,
|
||||
min.instance.whatsappServiceNumber, min.instance.whatsappServiceUrl, min.instance.whatsappServiceWebhookUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public unloadBot(min: GBMinInstance): void {
|
||||
|
||||
}
|
||||
public onNewSession(min: GBMinInstance, step: any): void {
|
||||
|
||||
}
|
||||
public getDialogs(min: GBMinInstance) {
|
||||
GBLog.verbose(`getDialogs called.`);
|
||||
}
|
||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||
GBLog.verbose(`loadPackage 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.`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,266 +1,218 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| |
|
||||
| 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. |
|
||||
| |
|
||||
\*****************************************************************************/
|
||||
import urlJoin = require('url-join');
|
||||
|
||||
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 rp = require('request-promise');
|
||||
import { GBLog, GBService } from 'botlib';
|
||||
import * as request from 'request-promise-native';
|
||||
|
||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
||||
|
||||
/**
|
||||
* Support for Whatsapp.
|
||||
*/
|
||||
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 directLineClientName = 'DirectLineClient';
|
||||
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||
public directLineClient: any;
|
||||
public whatsappServiceKey: string;
|
||||
public whatsappServiceNumber: string;
|
||||
public whatsappServiceUrl: string;
|
||||
public whatsappServiceWebhookUrl: string;
|
||||
public botId: string;
|
||||
public watermark: string;
|
||||
|
||||
public directLineClient: any;
|
||||
public whatsappServiceKey: string;
|
||||
public whatsappServiceNumber: string;
|
||||
public whatsappServiceUrl: string;
|
||||
public whatsappServiceWebhookUrl: string;
|
||||
public botId: string;
|
||||
public watermark: string = null;
|
||||
public conversationIds = {};
|
||||
|
||||
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.botId = botId;
|
||||
this.whatsappServiceKey = whatsappServiceKey;
|
||||
this.whatsappServiceNumber = whatsappServiceNumber;
|
||||
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();
|
||||
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')
|
||||
);
|
||||
|
||||
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 = {
|
||||
method: 'POST',
|
||||
url: UrlJoin(this.whatsappServiceUrl, 'message'),
|
||||
qs:
|
||||
{
|
||||
token: this.whatsappServiceKey,
|
||||
phone: to,
|
||||
body: msg
|
||||
},
|
||||
headers:
|
||||
{
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
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'
|
||||
}
|
||||
};
|
||||
|
||||
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';
|
||||
|
||||
const logger = require('./logger');
|
||||
const express = require('express');
|
||||
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 { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService';
|
||||
import { GuaribasInstance } from '../packages/core.gbapp/models/GBModel';
|
||||
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService';
|
||||
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService';
|
||||
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService';
|
||||
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
|
||||
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService';
|
||||
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.
|
||||
|
@ -64,33 +61,29 @@ export class GBServer {
|
|||
*/
|
||||
|
||||
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
|
||||
// bot instance. This allows the same server to attend multiple Bot on
|
||||
// the Marketplace until GB get serverless.
|
||||
// bot instance.
|
||||
|
||||
const port = process.env.port || process.env.PORT || 4242;
|
||||
const port = GBConfigService.getServerPort();
|
||||
const server = express();
|
||||
|
||||
server.use(bodyParser.json()); // to support JSON-encoded bodies
|
||||
server.use(bodyParser.json());
|
||||
server.use(
|
||||
bodyParser.urlencoded({
|
||||
// to support URL-encoded bodies
|
||||
extended: true
|
||||
})
|
||||
);
|
||||
|
||||
let bootInstance: IGBInstance;
|
||||
server.listen(port, () => {
|
||||
(async () => {
|
||||
try {
|
||||
logger.info(`Now accepting connections on ${port}...`);
|
||||
GBLog.info(`Now accepting connections on ${port}...`);
|
||||
|
||||
// Reads basic configuration, initialize minimal services.
|
||||
|
||||
GBConfigService.init();
|
||||
const core = new GBCoreService();
|
||||
const core: IGBCoreService = new GBCoreService();
|
||||
|
||||
const importer: GBImporter = new GBImporter(core);
|
||||
const deployer: GBDeployer = new GBDeployer(core, importer);
|
||||
|
@ -100,15 +93,16 @@ export class GBServer {
|
|||
|
||||
// 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);
|
||||
|
||||
// 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 {
|
||||
await core.initStorage();
|
||||
} catch (error) {
|
||||
GBLog.verbose(`Error initializing storage: ${error}`);
|
||||
bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress);
|
||||
await core.initStorage();
|
||||
}
|
||||
|
@ -117,24 +111,28 @@ export class GBServer {
|
|||
|
||||
// Deploys system and user packages.
|
||||
|
||||
logger.info(`Deploying packages...`);
|
||||
GBLog.info(`Deploying packages...`);
|
||||
core.loadSysPackages(core);
|
||||
await core.checkStorage(azureDeployer);
|
||||
await deployer.deployPackages(core, server, appPackages);
|
||||
|
||||
// Loads all bot instances.
|
||||
|
||||
logger.info(`Publishing instances...`);
|
||||
GBLog.info(`Publishing instances...`);
|
||||
const packageInstance = await importer.importIfNotExistsBotPackage(
|
||||
GBConfigService.get('CLOUD_GROUP'),
|
||||
'boot.gbot',
|
||||
'packages/boot.gbot'
|
||||
);
|
||||
if (bootInstance === undefined) {
|
||||
bootInstance = packageInstance;
|
||||
}
|
||||
// tslint:disable-next-line:prefer-object-spread
|
||||
const fullInstance = Object.assign(packageInstance, bootInstance);
|
||||
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);
|
||||
if (!bootInstance) {
|
||||
if (bootInstance !== undefined) {
|
||||
bootInstance = instances[0];
|
||||
}
|
||||
|
||||
|
@ -145,16 +143,15 @@ export class GBServer {
|
|||
|
||||
// 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.
|
||||
|
||||
core.openBrowserInDevelopment();
|
||||
|
||||
} catch (err) {
|
||||
logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
||||
GBLog.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"resolveJsonModule": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"*": ["types/*"],
|
||||
"botlib/*": ["node_modules/botlib/*"],
|
||||
"pragmatismo-io-framework/*": ["node_modules/pragmatismo-io-framework/*"]
|
||||
},
|
||||
|
|
82
tslint.json
82
tslint.json
|
@ -1,43 +1,43 @@
|
|||
{
|
||||
"defaultSeverity": "warning",
|
||||
"extends": [
|
||||
"tslint:recommended",
|
||||
"tslint-microsoft-contrib"
|
||||
],
|
||||
"linterOptions": {
|
||||
"exclude":[
|
||||
"libraries/botframework-connector/src/generated/**/*",
|
||||
"libraries/botframework-schema/**/*"
|
||||
]
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-microsoft-contrib"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"no-floating-promises": false,
|
||||
"no-var-requires":false,
|
||||
"typedef":false,
|
||||
"variable-name": false,
|
||||
"no-parameter-properties": false,
|
||||
"no-reserved-keywords": false,
|
||||
"no-unnecessary-class":false,
|
||||
"no-require-imports": false,
|
||||
"function-name": false,
|
||||
"no-redundant-jsdoc": false,
|
||||
"no-return-await": false,
|
||||
"prefer-type-cast": false,
|
||||
"no-object-literal-type-assertion":false,
|
||||
"no-increment-decrement":false,
|
||||
"no-any":false,
|
||||
"interface-name":false,
|
||||
"no-this-assignment":false,
|
||||
"switch-final-break":false,
|
||||
"no-parameter-reassignment":false,
|
||||
"export-name":false,
|
||||
"no-relative-imports": false,
|
||||
"no-backbone-get-set-outside-model": false,
|
||||
"max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}],
|
||||
"await-promise": [true, "Bluebird"]
|
||||
}
|
||||
"defaultSeverity": "warning",
|
||||
"extends": ["tslint:recommended", "tslint-microsoft-contrib"],
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"libraries/botframework-connector/src/generated/**/*",
|
||||
"libraries/botframework-schema/**/*",
|
||||
"./packages/default.gbui/**/*",
|
||||
"./packages/**/*.gbdialog"
|
||||
]
|
||||
},
|
||||
"rulesDirectory": ["node_modules/tslint-microsoft-contrib"],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"newline-per-chained-call": false,
|
||||
"no-unsafe-any": false,
|
||||
"no-floating-promises": false,
|
||||
"no-var-requires": false,
|
||||
"typedef": false,
|
||||
"variable-name": false,
|
||||
"no-parameter-properties": false,
|
||||
"max-line-length": [true, { "limit": 120, "ignore-pattern": "^\\s+\\*" }],
|
||||
"await-promise": [true, "Bluebird"],
|
||||
"no-reserved-keywords": false,
|
||||
"no-unnecessary-class": false,
|
||||
"no-require-imports": false,
|
||||
"function-name": false,
|
||||
"no-relative-imports": false,
|
||||
"no-redundant-jsdoc": false,
|
||||
"no-return-await": false,
|
||||
"prefer-type-cast": false,
|
||||
"non-literal-fs-path": false,
|
||||
"no-object-literal-type-assertion": false,
|
||||
"no-increment-decrement": false,
|
||||
"no-any": false,
|
||||
"interface-name": false,
|
||||
"no-this-assignment": false,
|
||||
"switch-final-break": false,
|
||||
"no-parameter-reassignment": false,
|
||||
"export-name": false,
|
||||
"no-backbone-get-set-outside-model": false
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue