fix(core): Bot boot logic being fixed.

This commit is contained in:
Rodrigo Rodriguez (pragmatismo.io) 2018-11-27 22:56:11 -02:00
parent c1db8be0c0
commit 1761e06061
23 changed files with 461 additions and 493 deletions

View file

@ -1,8 +1,9 @@
{
"trailingComma": "all",
"trailingComma": "none",
"tabWidth": 2,
"printWidth": 80,
"printWidth": 120,
"arrowParens": "avoid",
"semi": true,
"singleQuote": true
}

34
package-lock.json generated
View file

@ -2966,9 +2966,9 @@
}
},
"bluebird": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz",
"integrity": "sha1-G+CQjgVKdRdUVJwnBInBUF1KsVo="
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
"integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
},
"body-parser": {
"version": "1.18.3",
@ -7105,13 +7105,11 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"optional": true
"bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -7124,18 +7122,15 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@ -7238,8 +7233,7 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@ -7249,7 +7243,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -7262,20 +7255,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@ -7292,7 +7282,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -7365,8 +7354,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@ -7376,7 +7364,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -7482,7 +7469,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",

View file

@ -58,6 +58,7 @@
"azure-arm-search": "^1.3.0-preview",
"azure-arm-sql": "5.6.0",
"azure-arm-website": "5.7.0",
"bluebird": "^3.5.3",
"body-parser": "1.18.3",
"botbuilder": "^4.1.5",
"botbuilder-ai": "^4.1.5",

View file

@ -63,11 +63,11 @@ export class AdminDialog extends IGBDialog {
);
}
public static async deployPackageCommand(text: string, deployer: GBDeployer) {
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
const packageName = text.split(' ')[1];
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
await deployer.deployPackageFromLocalPath(
UrlJoin(additionalPath, packageName)
await deployer.deployPackageFromLocalPath(min,
UrlJoin(additionalPath, packageName)
);
}
/**
@ -119,11 +119,11 @@ export class AdminDialog extends IGBDialog {
await AdminDialog.createFarmCommand(text, deployer);
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'deployPackage') {
await AdminDialog.deployPackageCommand(text, deployer);
await AdminDialog.deployPackageCommand(min, text, deployer);
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'redeployPackage') {
await AdminDialog.undeployPackageCommand(text, min);
await AdminDialog.deployPackageCommand(text, deployer);
await AdminDialog.deployPackageCommand(min, text, deployer);
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'undeployPackage') {
await AdminDialog.undeployPackageCommand(text, min);

View file

@ -0,0 +1,12 @@
{
"version": "1.0.0",
"theme": "default.gbtheme",
"ui": "default.gbui",
"kb": "default.gbkb",
"title": "Default General Bot",
"description": "Default General Bot",
"whoAmIVideo": "TODO.mp4",
"author": "pragmatismo.io",
"license": "AGPL",
"engineName": "guaribas-1.0.0"
}

View file

@ -0,0 +1,3 @@
{
"groups": [{}]
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,6 @@
{
"enabledAdmin": "true",
"searchScore": ".15",
"nlpScore": ".15",
"nlpVsSearch": ".4"
}

View file

@ -46,7 +46,7 @@ export class DialogClass {
this.min = min;
}
public async expectMessage(text: string): Promise<any> {
public async hear(text: string): Promise<any> {
return new Promise((resolve, reject) => {
this.min.dialogs.add(
new WaterfallDialog('/vmExpect', [
@ -63,7 +63,11 @@ export class DialogClass {
});
}
public sendMessage(text: string) {
public post(url: string, data) {
}
public talk(text: string) {
this.min.dialogs.add(
new WaterfallDialog('/vmSend', [
async step => {

View file

@ -31,18 +31,15 @@
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
* @fileoverview Conversation handling and external service calls.
*/
'use strict';
const logger = require('../../../src/logger');
import { any } from 'bluebird';
import { MessageFactory } from 'botbuilder';
import { LuisRecognizer } from 'botbuilder-ai';
import { IGBConversationalService } from 'botlib';
import { GBMinInstance } from 'botlib';
import { GBMinInstance, IGBConversationalService } from 'botlib';
import { AzureText } from 'pragmatismo-io-framework';
import { Messages } from '../strings';
import { GBCoreService } from './GBCoreService';
@ -70,33 +67,27 @@ export class GBConversationalService implements IGBConversationalService {
msg.value = value;
msg.type = 'event';
msg.name = name;
return step.context.sendActivity(msg);
}
}
public async sendSms(
min: GBMinInstance,
mobile: string,
text: string
): Promise<any> {
return new Promise((resolve: any, reject: any): any => {
const nexmo = new Nexmo({
apiKey: min.instance.smsKey,
apiSecret: min.instance.smsSecret
});
nexmo.message.sendSms(
min.instance.smsServiceNumber,
mobile,
text,
(err, data) => {
public async sendSms(min: GBMinInstance, mobile: string, text: string): Promise<any> {
return new Promise(
(resolve: any, reject: any): any => {
const nexmo = new Nexmo({
apiKey: min.instance.smsKey,
apiSecret: min.instance.smsSecret
});
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
}
);
});
});
}
);
}
public async routeNLP(step: any, min: GBMinInstance, text: string): Promise<boolean> {
@ -112,15 +103,17 @@ export class GBConversationalService implements IGBConversationalService {
try {
nlp = await model.recognize(step.context);
} catch (error) {
if (error.statusCode == 404) {
logger.warn ('NLP application still not publish and there are no other options for answering.');
if (error.statusCode === 404) {
logger.warn('NLP application still not publish and there are no other options for answering.');
return Promise.resolve(false);
} else {
const msg = `Error calling NLP server, check if you have a published model and assigned keys on the service. Error: ${
error.statusCode ? error.statusCode : ''
} ${error.message}`;
return Promise.reject(new Error(msg)); }
const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${
error.statusCode ? error.statusCode : ''
} ${error.message}`;
return Promise.reject(new Error(msg));
}
}
// Resolves intents returned from LUIS.
@ -128,37 +121,31 @@ export class GBConversationalService implements IGBConversationalService {
const topIntent = LuisRecognizer.topIntent(nlp);
if (topIntent) {
const intent = topIntent;
const entity =
nlp.entities && nlp.entities.length > 0
? nlp.entities[0].entity.toUpperCase()
: null;
const entity = nlp.entities && nlp.entities.length > 0 ? nlp.entities[0].entity.toUpperCase() : null;
if (intent === 'None') {
return Promise.resolve(false);
}
logger.info('NLP called:' + intent + ', ' + entity);
logger.info(`NLP called: ${intent}, ${entity}`);
try {
await step.replace('/' + intent, nlp.entities);
await step.replace(`/${intent}`, nlp.entities);
return Promise.resolve(true);
} catch (error) {
const msg = `Error finding dialog associated to NLP event: ${intent}: ${
error.message
}`;
const msg = `Error finding dialog associated to NLP event: ${intent}: ${error.message}`;
return Promise.reject(new Error(msg));
}
}
return Promise.resolve(false);
}
public async checkLanguage(step, min, text) {
const locale = await AzureText.getLocale(
min.instance.textAnalyticsKey,
min.instance.textAnalyticsEndpoint,
text
);
if (locale != step.context.activity.locale.split('-')[0]) {
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, text);
if (locale !== step.context.activity.locale.split('-')[0]) {
switch (locale) {
case 'pt':
step.context.activity.locale = 'pt-BR';

View file

@ -39,17 +39,18 @@
import { IGBCoreService, 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 { GBCorePackage } from '../../core.gbapp';
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
import { GBKBPackage } from '../../kb.gbapp';
import { GBSecurityPackage } from '../../security.gblib';
import { GBWhatsappPackage } from '../../whatsapp.gblib/index';
import { GuaribasInstance } from '../models/GBModel';
import { GBConfigService } from './GBConfigService';
import { AzureDeployerService } from 'packages/azuredeployer.gbapp/services/AzureDeployerService';
import { GBAnalyticsPackage } from 'packages/analytics.gblib';
import { GBAdminPackage } from 'packages/admin.gbapp/index';
import { GBCorePackage } from 'packages/core.gbapp';
import { GBCustomerSatisfactionPackage } from 'packages/customer-satisfaction.gbapp';
import { GBKBPackage } from 'packages/kb.gbapp';
import { GBSecurityPackage } from 'packages/security.gblib';
import { GBWhatsappPackage } from 'packages/whatsapp.gblib/index';
import { GBImporter } from './GBImporterService';
const logger = require('../../../src/logger');
const opn = require('opn');
@ -76,11 +77,7 @@ export class GBCoreService implements IGBCoreService {
/**
* Custom create table query.
*/
private createTableQuery: (
tableName: string,
attributes: any,
options: any,
) => string;
private createTableQuery: (tableName: string, attributes: any, options: any) => string;
/**
* Custom change column query.
@ -102,78 +99,79 @@ export class GBCoreService implements IGBCoreService {
/**
* Gets database config and connect to storage.
*/
public async initDatabase(): Promise<any> {
return new Promise(
(resolve: any, reject: any): any => {
try {
this.dialect = GBConfigService.get('STORAGE_DIALECT');
let host: string | undefined;
let database: string | undefined;
let username: string | undefined;
let password: string | undefined;
let storage: string | undefined;
public async initStorage(): Promise<any> {
this.dialect = GBConfigService.get('STORAGE_DIALECT');
if (this.dialect === 'mssql') {
host = GBConfigService.get('STORAGE_SERVER');
database = GBConfigService.get('STORAGE_NAME');
username = GBConfigService.get('STORAGE_USERNAME');
password = GBConfigService.get('STORAGE_PASSWORD');
} else if (this.dialect === 'sqlite') {
storage = GBConfigService.get('STORAGE_STORAGE');
} else {
reject(`Unknown dialect: ${this.dialect}.`);
let host: string | undefined;
let database: string | undefined;
let username: string | undefined;
let password: string | undefined;
let storage: string | undefined;
if (this.dialect === 'mssql') {
host = GBConfigService.get('STORAGE_SERVER');
database = GBConfigService.get('STORAGE_NAME');
username = GBConfigService.get('STORAGE_USERNAME');
password = GBConfigService.get('STORAGE_PASSWORD');
} else if (this.dialect === 'sqlite') {
storage = GBConfigService.get('STORAGE_STORAGE');
} else {
throw new Error(`Unknown dialect: ${this.dialect}.`);
}
const logging: any =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
logger.info(str);
}
: false;
const logging: any =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
logger.info(str);
}
: false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
const encrypt: boolean =
GBConfigService.get('STORAGE_ENCRYPT') === 'true';
this.sequelize = new Sequelize({
host: host,
database: database,
username: username,
password: password,
logging: logging,
operatorsAliases: false,
dialect: this.dialect,
storage: storage,
dialectOptions: {
encrypt: encrypt,
},
pool: {
max: 32,
min: 8,
idle: 40000,
evict: 40000,
acquire: 40000,
},
});
if (this.dialect === 'mssql') {
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
this.createTableQuery = this.queryGenerator.createTableQuery;
this.queryGenerator.createTableQuery = (
tableName,
attributes,
options,
) => this.createTableQueryOverride(tableName, attributes, options);
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
this.changeColumnQueryOverride(tableName, attributes);
}
resolve();
} catch (error) {
reject(error);
}
this.sequelize = new Sequelize({
host: host,
database: database,
username: username,
password: password,
logging: logging,
operatorsAliases: false,
dialect: this.dialect,
storage: storage,
dialectOptions: {
encrypt: encrypt
},
);
pool: {
max: 32,
min: 8,
idle: 40000,
evict: 40000,
acquire: 40000
}
});
if (this.dialect === 'mssql') {
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
this.createTableQuery = this.queryGenerator.createTableQuery;
this.queryGenerator.createTableQuery = (tableName, attributes, options) =>
this.createTableQueryOverride(tableName, attributes, options);
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
this.changeColumnQueryOverride(tableName, attributes);
}
}
public async checkStorage(azureDeployer: AzureDeployerService) {
try {
await this.sequelize.authenticate();
} catch (error) {
logger.info('Opening storage firewall on infrastructure...');
if (error.parent.code === 'ELOGIN') {
await this.openStorageFrontier(azureDeployer);
} else {
throw error;
}
}
}
public async syncDatabaseStructure() {
@ -181,9 +179,10 @@ export class GBCoreService implements IGBCoreService {
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
const force = GBConfigService.get('STORAGE_SYNC_FORCE') === 'true';
logger.info('Syncing database...');
return this.sequelize.sync({
alter: alter,
force: force,
force: force
});
} else {
const msg = 'Database synchronization is disabled.';
@ -203,6 +202,7 @@ export class GBCoreService implements IGBCoreService {
*/
public async loadInstanceById(instanceId: string): Promise<IGBInstance> {
const options = { where: { instanceId: instanceId } };
return GuaribasInstance.findOne(options);
}
@ -211,63 +211,182 @@ export class GBCoreService implements IGBCoreService {
*/
public async loadInstance(botId: string): Promise<IGBInstance> {
const options = { where: {} };
options.where = { botId: botId };
if (botId !== '[default]') {
options.where = { botId: botId };
}
return GuaribasInstance.findOne(options);
return await GuaribasInstance.findOne(options);
}
public async writeEnv(instance: IGBInstance) {
const env =
`ADDITIONAL_DEPLOY_PATH=\n` +
`ADMIN_PASS=${instance.adminPass}\n` +
`CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}\n` +
`CLOUD_LOCATION=${instance.cloudLocation}\n` +
`CLOUD_GROUP=${instance.botId}\n` +
`CLOUD_USERNAME=${instance.cloudUsername}\n` +
`CLOUD_PASSWORD=${instance.cloudPassword}\n` +
`MARKETPLACE_ID=${instance.marketplaceId}\n` +
`MARKETPLACE_SECRET=${instance.marketplacePassword}\n` +
`NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}\n` +
`STORAGE_DIALECT=${instance.storageDialect}\n` +
`STORAGE_SERVER=${instance.storageServer}.database.windows.net\n` +
`STORAGE_NAME=${instance.storageName}\n` +
`STORAGE_USERNAME=${instance.storageUsername}\n` +
`STORAGE_PASSWORD=${instance.storagePassword}\n` +
`STORAGE_SYNC=true\n`;
const env = `ADDITIONAL_DEPLOY_PATH=
ADMIN_PASS=${instance.adminPass}
CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}
CLOUD_LOCATION=${instance.cloudLocation}
CLOUD_GROUP=${instance.botId}
CLOUD_USERNAME=${instance.cloudUsername}
CLOUD_PASSWORD=${instance.cloudPassword}
MARKETPLACE_ID=${instance.marketplaceId}
MARKETPLACE_SECRET=${instance.marketplacePassword}
NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}
STORAGE_DIALECT=${instance.storageDialect}
STORAGE_SERVER=${instance.storageServer}.database.windows.net
STORAGE_NAME=${instance.storageName}
STORAGE_USERNAME=${instance.storageUsername}
STORAGE_PASSWORD=${instance.storagePassword}
STORAGE_SYNC=true`;
fs.writeFileSync('.env', env);
}
public async ensureProxy(port): Promise<string> {
let proxyAddress: string;
const ngrok = require('ngrok');
return await ngrok.connect({ port: port });
}
public async saveInstance(fullInstance: any) {
const options = { where: {} };
options.where = { botId: fullInstance.botId };
let instance = await GuaribasInstance.findOne(options);
// tslint:disable-next-line:prefer-object-spread
instance = Object.assign(instance, fullInstance);
return await instance.save();
}
/**
* Loads all bot instances from object storage, if it's formatted.
*
* @param core
* @param azureDeployer
* @param proxyAddress
*/
public async loadAllInstances(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
logger.info(`Loading instances from storage...`);
let instances: GuaribasInstance[];
try {
instances = await core.loadInstances();
const instance = instances[0];
if (process.env.NODE_ENV === 'development') {
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
await azureDeployer.updateBotProxy(
instance.botId,
instance.botId,
`${proxyAddress}/api/messages/${instance.botId}`
);
}
} catch (error) {
// Check if storage is empty and needs formatting.
const isInvalidObject = error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE.
if (isInvalidObject) {
if (GBConfigService.get('STORAGE_SYNC') != 'true') {
throw new Error(
`Operating storage is out of sync or there is a storage connection error.
Try setting STORAGE_SYNC to true in .env file. Error: ${error.message}.`
);
} else {
logger.info(`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`);
}
} else {
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
}
}
return instances;
}
/**
* If instances is undefined here it's because storage has been formatted.
* Load all instances from .gbot found on deploy package directory.
* @param instances
* @param bootInstance
* @param core
*/
public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) {
if (!instances) {
const saveInstance = new GuaribasInstance(bootInstance);
await saveInstance.save();
instances = await core.loadInstances();
}
return instances;
}
public loadSysPackages(core: GBCoreService) {
// NOTE: if there is any code before this line a semicolon
// will be necessary before this line.
// Loads all system packages.
[
GBAdminPackage,
GBAnalyticsPackage,
GBCorePackage,
GBSecurityPackage,
GBKBPackage,
GBCustomerSatisfactionPackage,
GBWhatsappPackage
].forEach(e => {
logger.info(`Loading sys package: ${e.name}...`);
const p = Object.create(e.prototype) as IGBPackage;
p.loadPackage(core, core.sequelize);
});
}
public ensureAdminIsSecured() {
const password = GBConfigService.get('ADMIN_PASS');
if (!GBAdminService.StrongRegex.test(password)) {
throw new Error(
'Please, define a really strong password in ADMIN_PASS environment variable before running the server.'
);
}
}
public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
let instance: IGBInstance;
try {
await core.initStorage();
} catch (error) {
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
try {
instance = await azureDeployer.deployFarm(proxyAddress);
} catch (error) {
logger.warn(
'In case of error, please cleanup any infrastructure objects ' +
'created during this procedure and .env before running again.'
);
throw error;
}
core.writeEnv(instance);
logger.info(`File .env written, starting General Bots...`);
GBConfigService.init();
await core.initStorage();
return instance;
}
}
public openBrowserInDevelopment() {
if (process.env.NODE_ENV === 'development') {
opn('http://localhost:4242');
}
}
/**
* SQL:
*
* // let sql: string = '' +
* // 'IF OBJECT_ID(\'[UserGroup]\', \'U\') IS NULL\n' +
* // 'CREATE TABLE [UserGroup] (\n' +
* // ' [id] INTEGER NOT NULL IDENTITY(1,1),\n' +
* // ' [userId] INTEGER NULL,\n' +
* // ' [groupId] INTEGER NULL,\n' +
* // ' [instanceId] INTEGER NULL,\n' +
* // ' PRIMARY KEY ([id1], [id2]),\n' +
* // ' FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,\n' +
* // ' FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId1]) ON DELETE NO ACTION,\n' +
* // 'IF OBJECT_ID(\'[UserGroup]\', \'U\') IS NULL' +
* // 'CREATE TABLE [UserGroup] (' +
* // ' [id] INTEGER NOT NULL IDENTITY(1,1),' +
* // ' [userId] INTEGER NULL,' +
* // ' [groupId] INTEGER NULL,' +
* // ' [instanceId] INTEGER NULL,' +
* // ' PRIMARY KEY ([id1], [id2]),' +
* // ' FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,' +
* // ' FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId1]) ON DELETE NO ACTION,' +
* // ' FOREIGN KEY ([instanceId]) REFERENCES [Instance] ([instanceId]) ON DELETE NO ACTION)'
*/
private createTableQueryOverride(tableName, attributes, options): string {
let sql: string = this.createTableQuery.apply(this.queryGenerator, [
tableName,
attributes,
options,
]);
let sql: string = this.createTableQuery.apply(this.queryGenerator, [tableName, attributes, options]);
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
if (matches) {
@ -277,7 +396,7 @@ export class GBCoreService implements IGBCoreService {
re2,
(match: string, ...args: any[]): string => {
return 'CONSTRAINT [' + table + '_pk] ' + match;
},
}
);
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re4 = /\[([^\]]*)\]/g;
@ -292,7 +411,7 @@ export class GBCoreService implements IGBCoreService {
matches = re4.exec(fkcols);
}
return 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')';
},
}
);
}
return sql;
@ -301,16 +420,13 @@ export class GBCoreService implements IGBCoreService {
/**
* SQL:
* let sql = '' +
* 'ALTER TABLE [UserGroup]\n' +
* ' ADD CONSTRAINT [invalid1] FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,\n' +
* ' CONSTRAINT [invalid2] FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId2]) ON DELETE NO ACTION, \n' +
* ' CONSTRAINT [invalid3] FOREIGN KEY ([instanceId1]) REFERENCES [Instance] ([instanceId1]) ON DELETE NO ACTION\n'
* 'ALTER TABLE [UserGroup]' +
* ' ADD CONSTRAINT [invalid1] FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,' +
* ' CONSTRAINT [invalid2] FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId2]) ON DELETE NO ACTION, ' +
* ' CONSTRAINT [invalid3] FOREIGN KEY ([instanceId1]) REFERENCES [Instance] ([instanceId1]) ON DELETE NO ACTION'
*/
private changeColumnQueryOverride(tableName, attributes): string {
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [
tableName,
attributes,
]);
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [tableName, attributes]);
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
if (matches) {
@ -327,159 +443,21 @@ export class GBCoreService implements IGBCoreService {
fkname += '_' + matches[1];
matches = re3.exec(fkcols);
}
return (
(args[0] ? args[0] : '') +
'CONSTRAINT [' +
fkname +
'_fk] FOREIGN KEY (' +
fkcols +
')'
);
},
return (args[0] ? args[0] : '') + 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')';
}
);
}
return sql;
}
/**
* Loads all bot instances from object storage, if it's formatted.
* Opens storage firewall.
*
* @param core
* @param azureDeployer
* @param proxyAddress
* @param azureDeployer Infrastructure Deployer instance.
*/
public async loadAllInstances(
core: GBCoreService,
azureDeployer: AzureDeployerService,
proxyAddress: string,
) {
logger.info(`Loading instances from storage...`);
let instances: GuaribasInstance[];
try {
instances = await core.loadInstances();
const instance = instances[0];
if (process.env.NODE_ENV === 'development') {
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
await azureDeployer.updateBotProxy(
instance.botId,
instance.botId,
`${proxyAddress}/api/messages/${instance.botId}`,
);
}
} catch (error) {
if (error.parent.code === 'ELOGIN') {
const group = GBConfigService.get('CLOUD_GROUP');
const serverName = GBConfigService.get('STORAGE_SERVER').split(
'.database.windows.net',
)[0];
await azureDeployer.openStorageFirewall(group, serverName);
} else {
// Check if storage is empty and needs formatting.
const isInvalidObject =
error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE.
if (isInvalidObject) {
if (GBConfigService.get('STORAGE_SYNC') != 'true') {
throw new Error(
`Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${
error.message
}.`,
);
} else {
logger.info(
`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`,
);
}
} else {
throw new Error(
`Cannot connect to operating storage: ${error.message}.`,
);
}
}
}
return instances;
private async openStorageFrontier(deployer: AzureDeployerService) {
const group = GBConfigService.get('CLOUD_GROUP');
const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0];
await deployer.openStorageFirewall(group, serverName);
}
/**
* If instances is undefined here it's because storage has been formatted.
* Load all instances from .gbot found on deploy package directory.
* @param instances
* @param bootInstance
* @param core
*/
public async ensureInstances(
instances: GuaribasInstance[],
bootInstance: any,
core: GBCoreService,
) {
if (!instances) {
const saveInstance = new GuaribasInstance(bootInstance);
await saveInstance.save();
instances = await core.loadInstances();
}
return instances;
}
public loadSysPackages(core: GBCoreService) {
// NOTE: if there is any code before this line a semicolon
// will be necessary before this line.
// Loads all system packages.
[
GBAdminPackage,
GBAnalyticsPackage,
GBCorePackage,
GBSecurityPackage,
GBKBPackage,
GBCustomerSatisfactionPackage,
GBWhatsappPackage,
].forEach(e => {
logger.info(`Loading sys package: ${e.name}...`);
const p = Object.create(e.prototype) as IGBPackage;
p.loadPackage(core, core.sequelize);
});
}
public ensureAdminIsSecured() {
const password = GBConfigService.get('ADMIN_PASS');
if (!GBAdminService.StrongRegex.test(password)) {
throw new Error(
'Please, define a really strong password in ADMIN_PASS environment variable before running the server.',
);
}
}
public async createBootInstance(
core: GBCoreService,
azureDeployer: AzureDeployerService,
proxyAddress: string,
) {
let bootInstance: IGBInstance;
try {
await core.initDatabase();
} catch (error) {
logger.info(
`Deploying cognitive infrastructure (on the cloud / on premises)...`,
);
try {
bootInstance = await azureDeployer.deployFarm(proxyAddress);
} catch (error) {
logger.warn(
'In case of error, please cleanup any infrastructure objects created during this procedure and .env before running again.',
);
throw error;
}
core.writeEnv(bootInstance);
logger.info(`File .env written, starting General Bots...`);
GBConfigService.init();
await core.initDatabase();
}
return bootInstance;
}
public openBrowserInDevelopment() {
if (process.env.NODE_ENV === 'development') {
opn('http://localhost:4242');
}
}
}

View file

@ -84,7 +84,7 @@ export class GBDeployer {
public deployPackages(
core: IGBCoreService,
server: any,
appPackages: IGBPackage[],
appPackages: IGBPackage[]
) {
const _this = this;
return new Promise(
@ -93,7 +93,6 @@ export class GBDeployer {
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
let paths = [GBDeployer.deployFolder];
if (additionalPath) {
paths = paths.concat(additionalPath.toLowerCase().split(';'));
}
const botPackages = new Array<string>();
@ -124,7 +123,7 @@ export class GBDeployer {
}
logger.info(
`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`,
`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`
);
paths.forEach(e => {
logger.info(`Looking in: ${e}...`);
@ -199,20 +198,22 @@ export class GBDeployer {
server.use('/themes/' + filenameOnly, express.static(filename));
logger.info(
`Theme (.gbtheme) assets accessible at: ${'/themes/' +
filenameOnly}.`,
filenameOnly}.`
);
/** Knowledge base for bots. */
} else if (Path.extname(filename) === '.gbkb') {
server.use(
'/kb/' + filenameOnly + '/subjects',
express.static(UrlJoin(filename, 'subjects')),
express.static(UrlJoin(filename, 'subjects'))
);
logger.info(
`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`,
`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`
);
} else if (Path.extname(filename) === '.gbui') {
// Already Handled
} else if (Path.extname(filename) === '.gbdialog') {
// Already Handled
} else {
/** Unknown package format. */
const err = new Error(`Package type not handled: ${filename}.`);
@ -231,7 +232,7 @@ export class GBDeployer {
.done(function(result) {
if (botPackages.length === 0) {
logger.info(
'No external packages to load, please use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder.',
'No external packages to load, please use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder.'
);
} else {
logger.info(`Package deployment done.`);
@ -239,7 +240,7 @@ export class GBDeployer {
resolve();
});
});
},
}
);
}
@ -252,24 +253,22 @@ export class GBDeployer {
const packageName = Path.basename(localPath);
const instance = await this.importer.importIfNotExistsBotPackage(
packageName,
localPath,
localPath
);
return instance;
}
public async deployPackageToStorage(
instanceId: number,
packageName: string,
packageName: string
): Promise<GuaribasPackage> {
return GuaribasPackage.create({
packageName: packageName,
instanceId: instanceId,
instanceId: instanceId
});
}
public deployScriptToStorage(instanceId: number, localPath: string) {
}
public deployScriptToStorage(instanceId: number, localPath: string) {}
public deployTheme(localPath: string) {
// DISABLED: Until completed, "/ui/public".
@ -283,7 +282,7 @@ export class GBDeployer {
// })
}
public async deployPackageFromLocalPath(localPath: string) {
public async deployPackageFromLocalPath(min: IGBInstance, localPath: string) {
const packageType = Path.extname(localPath);
switch (packageType) {
@ -303,7 +302,7 @@ export class GBDeployer {
case '.gbdialog':
const vm = new GBVMService();
return service.deployKb(this.core, this, localPath);
return vm.loadJS(localPath, min, this.core, this, localPath);
default:
const err = GBError.create(
@ -316,7 +315,7 @@ export class GBDeployer {
public async undeployPackageFromLocalPath(
instance: IGBInstance,
localPath: string,
localPath: string
) {
const packageType = Path.extname(localPath);
const packageName = Path.basename(localPath);
@ -339,9 +338,12 @@ export class GBDeployer {
case '.gbui':
break;
case '.gbdialog':
break;
default:
const err = GBError.create(
`GuaribasBusinessError: Unknown package type: ${packageType}.`,
`GuaribasBusinessError: Unknown package type: ${packageType}.`
);
Promise.reject(err);
break;
@ -353,11 +355,11 @@ export class GBDeployer {
instance.searchKey,
instance.searchHost,
instance.searchIndex,
instance.searchIndexer,
instance.searchIndexer
);
const connectionString = GBDeployer.getConnectionStringFromInstance(
instance,
instance
);
const dsName = 'gb';
@ -375,7 +377,7 @@ export class GBDeployer {
dsName,
'GuaribasQuestion',
'azuresql',
connectionString,
connectionString
);
try {
@ -388,35 +390,17 @@ export class GBDeployer {
}
await search.createIndex(
AzureDeployerService.getKBSearchSchema(instance.searchIndex),
dsName,
dsName
);
}
public async getPackageByName(
instanceId: number,
packageName: string,
packageName: string
): Promise<GuaribasPackage> {
const where = { packageName: packageName, instanceId: instanceId };
return GuaribasPackage.findOne({
where: where,
where: where
});
}
/**
*
* Hot deploy processing.
*
*/
public async scanBootPackage() {
const deployFolder = 'packages';
const bootPackage = GBConfigService.get('BOOT_PACKAGE');
if (bootPackage === 'none') {
return Promise.resolve(true);
} else {
return this.deployPackageFromLocalPath(
UrlJoin(deployFolder, bootPackage),
);
}
}
}

View file

@ -57,14 +57,12 @@ export class GBImporter {
const packageJson = JSON.parse(
fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8')
);
const botId = packageJson.botId;
const instance = await this.core.loadInstance(botId);
if (instance) {
return Promise.resolve(instance);
return instance;
} else {
return this.createInstanceInternal(packageName, localPath, packageJson);
return await this.createInstanceInternal(packageName, localPath, packageJson);
}
}
@ -83,7 +81,6 @@ export class GBImporter {
packageJson = {...packageJson, ...settings, ...servicesJson};
GuaribasInstance.create(packageJson).then((instance: IGBInstance) => {
const service = new SecService();
// TODO: service.importSecurityFile(localPath, instance)

View file

@ -88,7 +88,7 @@ export class GBMinService {
core: IGBCoreService,
conversationalService: IGBConversationalService,
adminService: IGBAdminService,
deployer: GBDeployer,
deployer: GBDeployer
) {
this.core = core;
this.conversationalService = conversationalService;
@ -110,14 +110,14 @@ export class GBMinService {
public async buildMin(
server: any,
appPackages: IGBPackage[],
instances: GuaribasInstance[],
instances: GuaribasInstance[]
): Promise<GBMinInstance> {
// Serves default UI on root address '/'.
const uiPackage = 'default.gbui';
server.use(
'/',
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')),
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build'))
);
Promise.all(
@ -152,8 +152,8 @@ export class GBMinService {
speechToken: speechToken,
conversationId: webchatToken.conversationId,
authenticatorTenant: instance.authenticatorTenant,
authenticatorClientId: instance.authenticatorClientId,
}),
authenticatorClientId: instance.authenticatorClientId
})
);
} else {
const error = `Instance not found: ${botId}.`;
@ -166,7 +166,7 @@ export class GBMinService {
// Build bot adapter.
const { min, adapter, conversationState } = await this.buildBotAdapter(
instance,
instance
);
// Call the loadBot context.activity for all packages.
@ -184,11 +184,11 @@ export class GBMinService {
conversationState,
min,
instance,
appPackages,
appPackages
);
});
logger.info(
`GeneralBots(${instance.engineName}) listening on: ${url}.`,
`GeneralBots(${instance.engineName}) listening on: ${url}.`
);
// Serves individual URL for each bot user interface.
@ -196,12 +196,12 @@ export class GBMinService {
const uiUrl = `/${instance.botId}`;
server.use(
uiUrl,
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')),
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build'))
);
logger.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
const state = `${instance.instanceId}${Math.floor(
Math.random() * 1000000000,
Math.random() * 1000000000
)}`;
// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
@ -211,7 +211,7 @@ export class GBMinService {
let authorizationUrl = UrlJoin(
min.instance.authenticatorAuthorityHostUrl,
min.instance.authenticatorTenant,
'/oauth2/authorize',
'/oauth2/authorize'
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.authenticatorClientId
@ -229,7 +229,7 @@ export class GBMinService {
server.get(`/${min.instance.botId}/token`, async (req, res) => {
const state = await min.adminService.getValue(
min.instance.instanceId,
'AntiCSRFAttackState',
'AntiCSRFAttackState'
);
if (req.query.state !== state) {
@ -242,8 +242,8 @@ export class GBMinService {
const authenticationContext = new AuthenticationContext(
UrlJoin(
min.instance.authenticatorAuthorityHostUrl,
min.instance.authenticatorTenant,
),
min.instance.authenticatorTenant
)
);
const resource = 'https://graph.microsoft.com';
@ -263,27 +263,27 @@ export class GBMinService {
await this.adminService.setValue(
instance.instanceId,
'refreshToken',
token.refreshToken,
token.refreshToken
);
await this.adminService.setValue(
instance.instanceId,
'accessToken',
token.accessToken,
token.accessToken
);
await this.adminService.setValue(
instance.instanceId,
'expiresOn',
token.expiresOn.toString(),
token.expiresOn.toString()
);
await this.adminService.setValue(
instance.instanceId,
'AntiCSRFAttackState',
null,
null
);
res.redirect(min.instance.botEndpoint);
}
},
}
);
});
@ -301,7 +301,7 @@ export class GBMinService {
// }
// )
// next()
}),
})
);
}
@ -316,8 +316,8 @@ export class GBMinService {
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
method: 'POST',
headers: {
Authorization: `Bearer ${instance.webchatKey}`,
},
Authorization: `Bearer ${instance.webchatKey}`
}
};
try {
@ -344,8 +344,8 @@ export class GBMinService {
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': instance.speechKey,
},
'Ocp-Apim-Subscription-Key': instance.speechKey
}
};
try {
@ -359,7 +359,7 @@ export class GBMinService {
private async buildBotAdapter(instance: any) {
const adapter = new BotFrameworkAdapter({
appId: instance.marketplaceId,
appPassword: instance.marketplacePassword,
appPassword: instance.marketplacePassword
});
const storage = new MemoryStorage();
@ -395,7 +395,7 @@ export class GBMinService {
GBKBPackage,
GBAnalyticsPackage,
GBCustomerSatisfactionPackage,
GBWhatsappPackage,
GBWhatsappPackage
].forEach(sysPackage => {
const p = Object.create(sysPackage.prototype) as IGBPackage;
p.loadBot(min);
@ -406,12 +406,12 @@ export class GBMinService {
p.channel.received(req, res);
});
}
}, this);
}, this);
appPackages.forEach(e => {
e.sysPackages = sysPackages;
e.loadBot(min);
}, this);
}, this);
}
/**
@ -424,7 +424,7 @@ export class GBMinService {
conversationState: ConversationState,
min: any,
instance: any,
appPackages: any[],
appPackages: any[]
) {
return adapter.processActivity(req, res, async context => {
const state = conversationState.get(context);
@ -439,7 +439,7 @@ export class GBMinService {
instanceId: instance.instanceId,
botId: instance.botId,
theme: instance.theme ? instance.theme : 'default.gbtheme',
secret: instance.webchatKey,
secret: instance.webchatKey
});
user.loaded = true;
user.subjects = [];
@ -449,7 +449,7 @@ export class GBMinService {
logger.info(
`User>: ${context.activity.text} (${context.activity.type}, ${
context.activity.name
}, ${context.activity.channelId}, {context.activity.value})`,
}, ${context.activity.channelId}, {context.activity.value})`
);
if (
context.activity.type === 'conversationUpdate' &&
@ -478,7 +478,7 @@ export class GBMinService {
// Checks for /menu JSON signature.
} else if (context.activity.text.startsWith('{"title"')) {
await step.beginDialog('/menu', {
data: JSON.parse(context.activity.text),
data: JSON.parse(context.activity.text)
});
// Otherwise, continue to the active dialog in the stack.
@ -487,7 +487,7 @@ export class GBMinService {
await step.continueDialog();
} else {
await step.beginDialog('/answer', {
query: context.activity.text,
query: context.activity.text
});
}
}
@ -504,18 +504,18 @@ export class GBMinService {
await step.beginDialog('/menu');
} else if (context.activity.name === 'giveFeedback') {
await step.beginDialog('/feedback', {
fromMenu: true,
fromMenu: true
});
} else if (context.activity.name === 'showFAQ') {
await step.beginDialog('/faq');
} else if (context.activity.name === 'answerEvent') {
await step.beginDialog('/answerEvent', {
questionId: (context.activity as any).data,
fromFaq: true,
fromFaq: true
});
} else if (context.activity.name === 'quality') {
await step.beginDialog('/quality', {
score: (context.activity as any).data,
score: (context.activity as any).data
});
} else if (context.activity.name === 'updateToken') {
const token = (context.activity as any).data;
@ -529,7 +529,7 @@ export class GBMinService {
logger.error(msg);
await step.context.sendActivity(
Messages[step.context.activity.locale].very_sorry_about_error,
Messages[step.context.activity.locale].very_sorry_about_error
);
await step.beginDialog('/ask', { isReturning: true });
}

View file

@ -53,7 +53,6 @@ const UrlJoin = require('url-join');
*/
export class GBVMService implements IGBCoreService {
private script = new vm.Script();
public async loadJS(
@ -63,18 +62,15 @@ export class GBVMService implements IGBCoreService {
deployer: GBDeployer,
localPath: string
): Promise<void> {
const code = fs.readFileSync(UrlJoin(localPath, filename), 'utf8');
const sandbox = new DialogClass(min);
localPath = UrlJoin(localPath, 'chat.dialog.js');
const code: string = fs.readFileSync(UrlJoin(localPath, filename), 'utf8');
const sandbox: DialogClass = new DialogClass(min);
const context = vm.createContext(sandbox);
this.script.runInContext(context);
this.script.runInContext(code, context);
console.log(util.inspect(sandbox));
await deployer.deployScriptToStorage(
min.instanceId,
filename
);
await deployer.deployScriptToStorage(min.instanceId, filename);
logger.info(`[GBVMService] Finished loading of ${filename}`);
}
}

View file

@ -36,7 +36,7 @@ function ICanSendEmails()
bot.say ("Please, what's your e-mail address?")
email = bot.expectEmail()
bot.sendMail (email, "Olá", "I'm sending a General Bots VBA e-mail.")
bot.sendMail (email, "Hello", "I'm sending a General Bots VBA e-mail.")
end function

View file

@ -59,10 +59,10 @@ export class AskDialog extends IGBDialog {
min.dialogs.add(
new WaterfallDialog('/answerEvent', [
async step => {
if (step.options && step.options['questionId']) {
if (step.options && step.options.questionId) {
const question = await service.getQuestionById(
min.instance.instanceId,
step.options['questionId']
step.options.questionId
);
const answer = await service.getAnswerById(
min.instance.instanceId,
@ -84,7 +84,7 @@ export class AskDialog extends IGBDialog {
new WaterfallDialog('/answer', [
async step => {
const user = await min.userProfile.get(step.context, {});
let text = step.options['query'];
let text = step.options.query;
if (!text) {
throw new Error(`/answer being called with no args query text.`);
}
@ -97,9 +97,9 @@ export class AskDialog extends IGBDialog {
// Handle extra text from FAQ.
if (step.options && step.options['query']) {
text = step.options['query'];
} else if (step.options && step.options['fromFaq']) {
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);
}
@ -212,9 +212,9 @@ export class AskDialog extends IGBDialog {
// Three forms of asking.
if (step.options && step.options['firstTime'] ) {
if (step.options && step.options.firstTime) {
text = Messages[locale].ask_first_time;
} else if (step.options && step.options['isReturning'] ) {
} else if (step.options && step.options.isReturning) {
text = Messages[locale].anything_else;
} else if (user.subjects.length > 0) {
text = Messages[locale].which_question;

View file

@ -63,7 +63,7 @@ export class MenuDialog extends IGBDialog {
const locale = step.context.activity.locale;
let rootSubjectId = null;
if (step.options && step.options['data']) {
if (step.options && step.options.data) {
const subject = step.result.data;
// If there is a shortcut specified as subject destination, go there.

View file

@ -30,11 +30,15 @@
| |
\*****************************************************************************/
/**
* @fileoverview Knowledge base services and logic.
*/
const logger = require('../../../src/logger');
const Path = require('path');
const Fs = require('fs');
const promise = require('bluebird');
const parse = promise.promisify(require('csv-parse'));
const parse = require('bluebird').promisify(require('csv-parse'));
const UrlJoin = require('url-join');
const marked = require('marked');
const path = require('path');
@ -69,6 +73,7 @@ export class KBService {
subjects.forEach(subject => {
out.push(subject.title);
});
return out.join(', ');
}
@ -77,6 +82,7 @@ export class KBService {
subjects.forEach(subject => {
out.push(subject.internalId);
});
return out.join(' ');
}
@ -125,8 +131,10 @@ export class KBService {
answerId: question.answerId
}
});
return Promise.resolve({ question: question, answer: answer });
}
return Promise.resolve(null);
}
@ -163,9 +171,9 @@ export class KBService {
query = `${query} ${text}`;
}
}
// TODO: Filter by instance. what = `${what}&$filter=instanceId eq ${instanceId}`
query = `${query}&$filter=instanceId eq ${instance.instanceId}`;
try {
if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') == 'mssql') {
if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
const service = new AzureSearch(
instance.searchKey,
instance.searchHost,
@ -203,6 +211,7 @@ export class KBService {
parentId: number
): Promise<GuaribasSubject[]> {
const where = { parentSubjectId: parentId, instanceId: instanceId };
return GuaribasSubject.findAll({
where: where
});
@ -210,7 +219,7 @@ export class KBService {
public async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
const where = {
from: from, subject1: null, subject2: null, subject3: null, subject4:null
from: from, subject1: null, subject2: null, subject3: null, subject4: null
};
if (subjects) {
@ -230,6 +239,7 @@ export class KBService {
where.subject4 = subjects[3].internalId;
}
}
return await GuaribasQuestion.findAll({
where: where
});
@ -250,6 +260,7 @@ export class KBService {
let lastAnswer: GuaribasAnswer;
const data = await parse(file, opts);
return asyncPromise.eachSeries(data, async line => {
// Extracts values from columns in the current line.
@ -262,7 +273,7 @@ export class KBService {
// Skips the first line.
if (!(subjectsText === 'subjects' && from == 'from')) {
if (!(subjectsText === 'subjects' && from === 'from')) {
let format = '.txt';
// Extracts answer from external media if any.
@ -281,8 +292,10 @@ export class KBService {
// Processes subjects hierarchy splitting by dots.
const subjectArray = subjectsText.split('.');
let subject1: string, subject2: string, subject3: string,
subject4: string;
let subject1: string;
let subject2: string;
let subject3: string;
let subject4: string;
let indexer = 0;
subjectArray.forEach(element => {

View file

@ -75,8 +75,8 @@ export class GBServer {
server.use(
bodyParser.urlencoded({
// to support URL-encoded bodies
extended: true,
}),
extended: true
})
);
let bootInstance: IGBInstance;
@ -93,40 +93,33 @@ export class GBServer {
// Ensures cloud / on-premises infrastructure is setup.
logger.info(`Establishing a development local proxy (ngrok)...`);
const proxyAddress = await core.ensureProxy(port);
const proxyAddress: string = await core.ensureProxy(port);
logger.info(`Deploying packages...`);
const deployer = new GBDeployer(core, new GBImporter(core));
const azureDeployer = new AzureDeployerService(deployer);
const adminService = new GBAdminService(core);
const conversationalService = new GBConversationalService(core);
bootInstance = await core.createBootInstance(
core,
azureDeployer,
proxyAddress,
);
const importer: GBImporter = new GBImporter(core);
const deployer: GBDeployer = new GBDeployer(core, importer);
const azureDeployer: AzureDeployerService = new AzureDeployerService(deployer);
const adminService: GBAdminService = new GBAdminService(core);
const conversationalService: GBConversationalService = new GBConversationalService(core);
core.ensureAdminIsSecured();
core.loadSysPackages(core);
const bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress);
await core.checkStorage(azureDeployer);
await core.loadSysPackages(core);
await deployer.deployPackages(core, server, appPackages);
logger.info(`Publishing instances...`);
let instances: GuaribasInstance[] = await core.loadAllInstances(
core,
azureDeployer,
proxyAddress,
);
instances = await core.ensureInstances(
instances,
bootInstance,
core
);
const packageInstance = await importer.importIfNotExistsBotPackage('boot.gbot', 'packages/boot.gbot');
const fullInstance = Object.assign(packageInstance, bootInstance);
await core.saveInstance(fullInstance);
let instances: GuaribasInstance[] = await core.loadAllInstances(core, azureDeployer, proxyAddress);
instances = await core.ensureInstances(instances, bootInstance, core);
const minService = new GBMinService(
core,
conversationalService,
adminService,
deployer,
);
// Install default VBA module.
deployer.deployPackageFromLocalPath(instances[0], 'packages/default.gbdialog');
const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer);
await minService.buildMin(server, appPackages, instances);
logger.info(`The Bot Server is in RUNNING mode...`);
@ -141,7 +134,6 @@ export class GBServer {
});
}
}
// First line to run.

View file

@ -30,6 +30,10 @@
| |
\*****************************************************************************/
/**
* @fileoverview Logging support.
*/
const { createLogger, format, transports } = require('winston');
const config = {

View file

@ -15,6 +15,8 @@
],
"jsRules": {},
"rules": {
"no-var-requires":false,
"typedef":false,
"variable-name": false,
"no-parameter-properties": false,
"no-reserved-keywords": false,
@ -34,6 +36,6 @@
"export-name":false,
"no-relative-imports": false,
"no-backbone-get-set-outside-model": false,
"max-line-length": [true,{"limit":80,"ignore-pattern":"^\\s+\\*"}]
"max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}]
}
}