New promises and compiling.

This commit is contained in:
Rodrigo Rodriguez 2018-09-09 14:39:37 -03:00
parent 96f78956b6
commit 0ce8d48f09
21 changed files with 1136 additions and 1191 deletions

View file

@ -19,7 +19,7 @@
| in the LICENSE file you have received along with this program. | | in the LICENSE file you have received along with this program. |
| | | |
| This program is distributed in the hope that it will be useful, | | This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | | but WITHOUT ANY WARRANTY without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. | | GNU Affero General Public License for more details. |
| | | |
@ -30,157 +30,140 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict"; "use strict"
const UrlJoin = require("url-join"); const UrlJoin = require("url-join")
import { AzureSearch } from "pragmatismo-io-framework"
import { AzureSearch } from "pragmatismo-io-framework"; import { GBMinInstance } from "botlib"
const { DialogSet, TextPrompt, NumberPrompt } = require('botbuilder-dialogs'); import { IGBDialog } from "botlib"
const { createTextPrompt, createNumberPrompt } = require('botbuilder-prompts'); import { GBDeployer } from '../../core.gbapp/services/GBDeployer'
import { GBMinInstance } from "botlib"; import { GBImporter } from '../../core.gbapp/services/GBImporter'
import { IGBDialog } from "botlib"; import { GBConfigService } from '../../core.gbapp/services/GBConfigService'
import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { KBService } from './../../kb.gbapp/services/KBService'
import { GBImporter } from '../../core.gbapp/services/GBImporter'; import { BotAdapter } from "botbuilder"
import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; import { reject } from "async"
import { KBService } from './../../kb.gbapp/services/KBService';
import { BotAdapter } from "botbuilder";
/**
* Dialogs for administration tasks.
*/
export class AdminDialog extends IGBDialog { export class AdminDialog extends IGBDialog {
static setup(bot: BotAdapter, min: GBMinInstance) {
let importer = new GBImporter(min.core); static async undeployPackageCommand(text: any, min: GBMinInstance, dc) {
let deployer = new GBDeployer(min.core, importer); let packageName = text.split(" ")[1]
let importer = new GBImporter(min.core)
min.dialogs.add("/admin", [ let deployer = new GBDeployer(min.core, importer)
async (dc, args) => { dc.context.sendActivity(`Undeploying package ${packageName}...`)
const prompt = "Please, authenticate:"; let data = await deployer.undeployPackageFromLocalPath(
await dc.prompt('textPrompt', prompt);
},
async (dc, value) => {
let text = value;
const user = min.userState.get(dc.context);
if (
!user.authenticated ||
text === GBConfigService.get("ADMIN_PASS")
) {
user.authenticated = true;
dc.context.sendActivity(
"Welcome to Pragmatismo.io GeneralBots Administration."
);
await dc.prompt('textPrompt', "Which task do you wanna run now?");
} else {
dc.endAll();
}
},
async (dc, value) => {
var text = value;
const user = min.userState.get(dc.context);
if (text === "quit") {
user.authenticated = false;
dc.replace("/");
} else if (text === "sync") {
min.core.syncDatabaseStructure(() => { });
dc.context.sendActivity("Sync started...");
dc.replace("/admin", {
firstRun: false
});
} else if (text.split(" ")[0] === "rebuildIndex") {
AdminDialog.rebuildIndexCommand(min, dc, () =>
dc.replace("/admin", {
firstRun: false
})
);
} else if (text.split(" ")[0] === "deployPackage") {
AdminDialog.deployPackageCommand(text, dc, deployer, min, () =>
dc.replace("/admin", {
firstRun: false
})
);
} else if (text.split(" ")[0] === "redeployPackage") {
AdminDialog.undeployPackageCommand(text, min, dc, () => {
AdminDialog.deployPackageCommand(text, dc, deployer, min, () => {
dc.context.sendActivity("Redeploy done.");
dc.replace("/admin", {
firstRun: false
});
});
});
} else if (text.split(" ")[0] === "undeployPackage") {
AdminDialog.undeployPackageCommand(text, min, dc, () =>
dc.replace("/admin", {
firstRun: false
})
);
} else if (text.split(" ")[0] === "applyPackage") {
dc.context.sendActivity("Applying in progress...");
min.core.loadInstance(text.split(" ")[1], (item, err) => {
dc.context.sendActivity("Applying done...");
dc.replace("/");
});
dc.replace("/admin", {
firstRun: false
});
} else if (text.split(" ")[0] === "rat") {
min.conversationalService.sendEvent(dc, "play", { playerType: "login", data: null });
dc.context.sendActivity("Realize login clicando no botão de login, por favor...");
}
}
])
}
static undeployPackageCommand(text: any, min: GBMinInstance, dc, cb) {
let packageName = text.split(" ")[1];
let importer = new GBImporter(min.core);
let deployer = new GBDeployer(min.core, importer);
dc.context.sendActivity(`Undeploying package ${packageName}...`);
deployer.undeployPackageFromLocalPath(
min.instance, min.instance,
UrlJoin("deploy", packageName), UrlJoin("deploy", packageName))
(data, err) => { dc.context.sendActivity(`Package ${packageName} undeployed...`)
dc.context.sendActivity(`Package ${packageName} undeployed...`);
cb();
}
);
} }
static deployPackageCommand( static async deployPackageCommand(
text: string, text: string,
dc, dc,
deployer: GBDeployer, deployer: GBDeployer,
min: GBMinInstance, min: GBMinInstance,
cb
) { ) {
let packageName = text.split(" ")[1]; let packageName = text.split(" ")[1]
dc.context.sendActivity(`Deploying package ${packageName}... (It may take a few seconds)`); dc.context.sendActivity(`Deploying package ${packageName}... (It may take a few seconds)`)
// TODO: Find packages in all possible locations. // TODO: Find packages in all possible locations.
let additionalPath = GBConfigService.get("ADDITIONAL_DEPLOY_PATH"); let additionalPath = GBConfigService.get("ADDITIONAL_DEPLOY_PATH")
deployer.deployPackageFromLocalPath( let data = deployer.deployPackageFromLocalPath(
UrlJoin(additionalPath, packageName), UrlJoin(additionalPath, packageName))
(data, err) => { dc.context.sendActivity(`Package ${packageName} deployed... Please run rebuildIndex command.`)
dc.context.sendActivity(`Package ${packageName} deployed... Please run rebuildIndex command.`);
}
);
} }
static rebuildIndexCommand(min: GBMinInstance, dc, cb) { static async rebuildIndexCommand(min: GBMinInstance, dc) {
let search = new AzureSearch( let search = new AzureSearch(
min.instance.searchKey, min.instance.searchKey,
min.instance.searchHost, min.instance.searchHost,
min.instance.searchIndex, min.instance.searchIndex,
min.instance.searchIndexer min.instance.searchIndexer
); )
dc.context.sendActivity("Rebuilding index..."); dc.context.sendActivity("Rebuilding index...")
search.deleteIndex((data, err) => { await search.deleteIndex()
let kbService = new KBService(); let kbService = new KBService()
search.createIndex(kbService.getSearchSchema(min.instance.searchIndex), "gb", (data, err) => { await search.createIndex(kbService.getSearchSchema(min.instance.searchIndex), "gb")
dc.context.sendActivity("Index rebuilt."); await dc.context.sendActivity("Index rebuilt.")
}); }
});
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
// Setup services.
let importer = new GBImporter(min.core)
let deployer = new GBDeployer(min.core, importer)
min.dialogs.add("/admin", [
async (dc, args) => {
const prompt = "Please, authenticate:"
await dc.prompt('textPrompt', prompt)
},
async (dc, value) => {
let text = value
const user = min.userState.get(dc.context)
if (
!user.authenticated ||
text === GBConfigService.get("ADMIN_PASS")
) {
user.authenticated = true
await dc.context.sendActivity(
"Welcome to Pragmatismo.io GeneralBots Administration."
)
await dc.prompt('textPrompt', "Which task do you wanna run now?")
} else {
await dc.endAll()
}
},
async (dc, value) => {
var text = value
const user = min.userState.get(dc.context)
if (text === "quit") {
user.authenticated = false
await dc.replace("/")
} else if (text === "sync") {
await min.core.syncDatabaseStructure();
await dc.context.sendActivity("Sync started...")
await dc.replace("/admin", { firstRun: false })
} else if (text.split(" ")[0] === "rebuildIndex") {
await AdminDialog.rebuildIndexCommand(min, dc)
await dc.replace("/admin", { firstRun: false })
} else if (text.split(" ")[0] === "deployPackage") {
await AdminDialog.deployPackageCommand(text, dc, deployer, min)
await dc.replace("/admin", { firstRun: false })
} else if (text.split(" ")[0] === "redeployPackage") {
await AdminDialog.undeployPackageCommand(text, min, dc)
await AdminDialog.deployPackageCommand(text, dc, deployer, min)
await dc.context.sendActivity("Redeploy done.")
await dc.replace("/admin", { firstRun: false })
} else if (text.split(" ")[0] === "undeployPackage") {
await AdminDialog.undeployPackageCommand(text, min, dc)
await dc.replace("/admin", { firstRun: false })
} else if (text.split(" ")[0] === "applyPackage") {
await dc.context.sendActivity("Applying in progress...")
await min.core.loadInstance(text.split(" ")[1]);
await dc.context.sendActivity("Applying done...")
await dc.replace("/admin", { firstRun: false })
} else if (text.split(" ")[0] === "rat") {
await min.conversationalService.sendEvent(dc, "play", { playerType: "login", data: null })
await dc.context.sendActivity("Realize login clicando no botão de login, por favor...")
}
}
])
} }
} }

View file

@ -30,38 +30,38 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
import { GBServiceCallback } from "botlib";
import { GuaribasUser } from "../../security.gblib/models"; import { GuaribasUser } from "../../security.gblib/models";
import { GuaribasConversation, GuaribasConversationMessage } from "../models"; import { GuaribasConversation, GuaribasConversationMessage } from "../models";
export class AnalyticsService { export class AnalyticsService {
async createConversation(
user: GuaribasUser
createConversation( ): Promise<GuaribasConversation> {
user: GuaribasUser, return new Promise<GuaribasConversation>(
cb: GBServiceCallback<GuaribasConversation> (resolve, reject) => {
) {
let conversation = new GuaribasConversation(); let conversation = new GuaribasConversation();
conversation.startedBy = user; conversation.startedBy = user;
conversation.startedByUserId = user.userId; conversation.startedByUserId = user.userId;
conversation.save().then((value: GuaribasConversation) => { conversation.save().then((value: GuaribasConversation) => {
cb(conversation, null); resolve(value);
});
}); });
} }
createMessage( createMessage(
conversation: GuaribasConversation, conversation: GuaribasConversation,
user: GuaribasUser, user: GuaribasUser,
content: string, content: string
cb: GBServiceCallback<GuaribasConversationMessage> ): Promise<GuaribasConversationMessage> {
) { return new Promise<GuaribasConversationMessage>(
(resolve, reject) => {
let message = GuaribasConversationMessage.build(); let message = GuaribasConversationMessage.build();
message.conversation = conversation; message.conversation = conversation;
message.user = user; message.user = user;
message.content = content; message.content = content;
message.save().then((value: GuaribasConversationMessage) => { message.save().then((value: GuaribasConversationMessage) => {
cb(value, null); resolve(value);
});
}); });
} }
} }

View file

@ -40,9 +40,7 @@ const Walk = require("fs-walk");
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
const Swagger = require('swagger-client'); const Swagger = require('swagger-client');
const rp = require('request-promise'); const rp = require('request-promise');
import * as request from "request-promise-native"; import { GBService } from "botlib";
import { GBServiceCallback, GBService, IGBInstance } from "botlib";
export class ConsoleDirectLine extends GBService { export class ConsoleDirectLine extends GBService {
@ -193,7 +191,4 @@ export class ConsoleDirectLine extends GBService {
console.log('*' + contentLine(attachment.content.text) + '*'); console.log('*' + contentLine(attachment.content.text) + '*');
console.log('*'.repeat(width + 1) + '/'); console.log('*'.repeat(width + 1) + '/');
} }
} }

View file

@ -40,6 +40,12 @@ import { GBMinInstance } from "botlib";
import { BotAdapter } from "botbuilder"; import { BotAdapter } from "botbuilder";
export class WelcomeDialog extends IGBDialog { export class WelcomeDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add("/", [ min.dialogs.add("/", [

View file

@ -40,7 +40,15 @@ import { BotAdapter } from "botbuilder";
export class WhoAmIDialog extends IGBDialog { export class WhoAmIDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add("/whoAmI", [ min.dialogs.add("/whoAmI", [
async (dc, args) => { async (dc, args) => {
dc.context.sendActivity(`${min.instance.description}`); dc.context.sendActivity(`${min.instance.description}`);

View file

@ -32,18 +32,16 @@
"use strict"; "use strict";
const UrlJoin = require("url-join");
const gBuilder = require("botbuilder");
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
import { GBConfigService } from "./GBConfigService";
import { GBCoreService } from "./GBCoreService"; import { GBCoreService } from "./GBCoreService";
import { GBService, GBServiceCallback, IGBConversationalService } from "botlib"; import { IGBConversationalService } from "botlib";
import { GBError } from "botlib"; import { GBError } from "botlib";
import { GBERROR_TYPE } from "botlib"; import { GBERROR_TYPE } from "botlib";
import { GBMinInstance } from "botlib"; import { GBMinInstance } from "botlib";
import { LuisRecognizer } from "botbuilder-ai"; import { LuisRecognizer } from "botbuilder-ai";
import {MessageFactory} from "botbuilder"; import { MessageFactory } from "botbuilder";
import { resolve } from "bluebird";
export class GBConversationalService implements IGBConversationalService { export class GBConversationalService implements IGBConversationalService {
@ -64,9 +62,8 @@ export class GBConversationalService implements IGBConversationalService {
async runNLP( async runNLP(
dc: any, dc: any,
min: GBMinInstance, min: GBMinInstance,
text: string, text: string
cb: GBServiceCallback<any> ): Promise<any> {
) {
const model = new LuisRecognizer({ const model = new LuisRecognizer({
appId: min.instance.nlpAppId, appId: min.instance.nlpAppId,
@ -74,7 +71,7 @@ export class GBConversationalService implements IGBConversationalService {
serviceEndpoint: min.instance.nlpServerUrl serviceEndpoint: min.instance.nlpServerUrl
}); });
await model.recognize(dc.context).then(res => { return await model.recognize(dc.context).then(res => {
// Resolve intents returned from LUIS // Resolve intents returned from LUIS
let topIntent = LuisRecognizer.topIntent(res); let topIntent = LuisRecognizer.topIntent(res);
@ -98,16 +95,16 @@ export class GBConversationalService implements IGBConversationalService {
dc.replace("/ask", { isReturning: true }); dc.replace("/ask", { isReturning: true });
} }
cb({ intent, entities: res.entities }, null); return Promise.resolve({ intent, entities: res.entities });
} else { } else {
dc.context.sendActivity("Lamento, não achei nada a respeito..."); dc.context.sendActivity("Lamento, não achei nada a respeito...");
dc.replace("/ask", { isReturning: true }); dc.replace("/ask", { isReturning: true });
cb(null, null); resolve(null);
} }
} }
).catch(err => { ).catch(err => {
cb(null, new GBError(err, GBERROR_TYPE.nlpGeneralError)); return Promise.reject(new GBError(err, GBERROR_TYPE.nlpGeneralError));
}); });
} }
} }

View file

@ -32,21 +32,10 @@
"use strict"; "use strict";
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 Walk = require("fs-walk");
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { Promise } from "bluebird";
import { GBConfigService } from "./GBConfigService"; import { GBConfigService } from "./GBConfigService";
import { DataTypeUUIDv1 } from "sequelize"; import { IGBInstance, IGBCoreService } from 'botlib';
import { GBServiceCallback, IGBInstance, IGBCoreService } from 'botlib';
import { GuaribasInstance } from "../models/GBModel"; import { GuaribasInstance } from "../models/GBModel";
/** /**
@ -54,22 +43,46 @@ import { GuaribasInstance } from "../models/GBModel";
*/ */
export class GBCoreService implements IGBCoreService { export class GBCoreService implements IGBCoreService {
/**
* Data access layer instance.
*/
public sequelize: Sequelize; public sequelize: Sequelize;
/**
* Allows filtering on SQL generated before send to the database.
*/
private queryGenerator: any; private queryGenerator: any;
/**
* Custom create table query.
*/
private createTableQuery: (tableName, attributes, options) => string; private createTableQuery: (tableName, attributes, options) => string;
/**
* Custom change column query.
*/
private changeColumnQuery: (tableName, attributes) => string; private changeColumnQuery: (tableName, attributes) => string;
/** Dialect used. Tested: mssql and sqlite. */ /**
* Dialect used. Tested: mssql and sqlite.
*/
private dialect: string; private dialect: string;
/**
* Constructor retrieves default values.
*/
constructor() { constructor() {
this.dialect = GBConfigService.get("DATABASE_DIALECT"); this.dialect = GBConfigService.get("DATABASE_DIALECT");
} }
/** Get config and connect to storage. */ /**
initDatabase(cb) { * Gets database config and connect to storage.
*/
async initDatabase() {
return new Promise(
(resolve, reject) => {
try {
let host: string | undefined; let host: string | undefined;
let database: string | undefined; let database: string | undefined;
@ -122,8 +135,11 @@ export class GBCoreService implements IGBCoreService {
this.queryGenerator.changeColumnQuery = (tableName, attributes) => this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
this.changeColumnQueryOverride(tableName, attributes); this.changeColumnQueryOverride(tableName, attributes);
} }
resolve();
setImmediate(cb); } catch (error) {
reject(error);
}
});
} }
private createTableQueryOverride(tableName, attributes, options): string { private createTableQueryOverride(tableName, attributes, options): string {
@ -192,7 +208,9 @@ export class GBCoreService implements IGBCoreService {
return sql; return sql;
} }
syncDatabaseStructure(cb) { async syncDatabaseStructure() {
return new Promise(
(resolve, reject) => {
if (GBConfigService.get("DATABASE_SYNC") === "true") { if (GBConfigService.get("DATABASE_SYNC") === "true") {
const alter = (GBConfigService.get("DATABASE_SYNC_ALTER") === "true"); const alter = (GBConfigService.get("DATABASE_SYNC_ALTER") === "true");
const force = (GBConfigService.get("DATABASE_SYNC_FORCE") === "true"); const force = (GBConfigService.get("DATABASE_SYNC_FORCE") === "true");
@ -202,43 +220,49 @@ export class GBCoreService implements IGBCoreService {
force: force force: force
}).then(value => { }).then(value => {
logger.info("Database synced."); logger.info("Database synced.");
cb(); resolve(value);
}, err => logger.error(err)); }, err => reject(err));
} else { } else {
logger.info("Database synchronization is disabled."); logger.info("Database synchronization is disabled.");
cb(); resolve();
} }
});
} }
/** /**
* Loads all items to start several listeners. * Loads all items to start several listeners.
* @param cb Instances loaded or error info.
*/ */
loadInstances(cb: GBServiceCallback<IGBInstance[]>) { async loadInstances(): Promise<IGBInstance> {
return new Promise(
(resolve, reject) => {
GuaribasInstance.findAll({}) GuaribasInstance.findAll({})
.then((items: IGBInstance[]) => { .then((items: IGBInstance[]) => {
if (!items) items = []; if (!items) items = [];
if (items.length == 0) { if (items.length == 0) {
cb([], null); resolve([]);
} else { } else {
cb(items, null); resolve(items);
} }
}) })
.catch(reason => { .catch(reason => {
if (reason.message.indexOf("no such table: GuaribasInstance") != -1) { if (reason.message.indexOf("no such table: GuaribasInstance") != -1) {
cb([], null); resolve([]);
} else { } else {
logger.info(`GuaribasServiceError: ${reason}`); logger.info(`GuaribasServiceError: ${reason}`);
cb(null, reason); reject(reason);
} }
}); });
});
} }
/** /**
* Loads just one Bot instance. * Loads just one Bot instance.
*/ */
loadInstance(botId: string, cb: GBServiceCallback<IGBInstance>) { async loadInstance(botId: string): Promise<IGBInstance> {
return new Promise<IGBInstance>(
(resolve, reject) => {
let options = { where: {} }; let options = { where: {} };
if (botId != "[default]") { if (botId != "[default]") {
@ -248,14 +272,15 @@ export class GBCoreService implements IGBCoreService {
GuaribasInstance.findOne(options) GuaribasInstance.findOne(options)
.then((instance: IGBInstance) => { .then((instance: IGBInstance) => {
if (instance) { if (instance) {
cb(instance, null); resolve(instance);
} else { } else {
cb(null, null); resolve(null);
} }
}) })
.catch(err => { .catch(err => {
cb(null, err);
logger.info(`GuaribasServiceError: ${err}`); logger.info(`GuaribasServiceError: ${err}`);
reject(err);
});
}); });
} }
} }

View file

@ -34,25 +34,14 @@
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
const Path = require("path"); const Path = require("path");
const Fs = require("fs");
const FsExtra = require("fs-extra");
const _ = require("lodash"); const _ = require("lodash");
const Async = require("async");
const UrlJoin = require("url-join"); const UrlJoin = require("url-join");
const Walk = require("fs-walk");
const WaitUntil = require("wait-until");
import { KBService } from './../../kb.gbapp/services/KBService'; import { KBService } from './../../kb.gbapp/services/KBService';
import { GBImporter } from "./GBImporter"; import { GBImporter } from "./GBImporter";
import { GBCoreService } from "./GBCoreService";
import { GBServiceCallback, IGBCoreService, IGBInstance } from "botlib"; import { GBServiceCallback, IGBCoreService, IGBInstance } from "botlib";
import { Sequelize } from 'sequelize-typescript';
import { Promise } from "bluebird";
import { GBConfigService } from "./GBConfigService"; import { GBConfigService } from "./GBConfigService";
import { DataTypeUUIDv1 } from "sequelize"; import { GBError } from "botlib";
import { GBError, GBERROR_TYPE } from "botlib";
import { GBConversationalService } from "./GBConversationalService";
import { GuaribasPackage } from '../models/GBModel'; import { GuaribasPackage } from '../models/GBModel';
/** Deployer service for bots, themes, ai and more. */ /** Deployer service for bots, themes, ai and more. */
@ -69,156 +58,131 @@ export class GBDeployer {
this.importer = importer; this.importer = importer;
} }
/** Deploys a bot to the storage. */ /**
deployBot(localPath: string, cb: GBServiceCallback<any>) { * Deploys a bot to the storage.
*/
async deployBot(localPath: string): Promise<IGBInstance> {
let packageType = Path.extname(localPath); let packageType = Path.extname(localPath);
let packageName = Path.basename(localPath); let packageName = Path.basename(localPath);
let instance = await this.importer.importIfNotExistsBotPackage(
this.importer.importIfNotExistsBotPackage(
packageName, packageName,
localPath, localPath
(data, err) => {
if (err) {
logger.info(err);
} else {
cb(data, null);
}
}
); );
return instance;
} }
deployPackageToStorage( async deployPackageToStorage(
instanceId: number, instanceId: number,
packageName: string, packageName: string): Promise<GuaribasPackage> {
cb: GBServiceCallback<GuaribasPackage> return GuaribasPackage.create({
) {
GuaribasPackage.create({
packageName: packageName, packageName: packageName,
instanceId: instanceId instanceId: instanceId
}).then((item: GuaribasPackage) => {
cb(item, null);
}); });
} }
deployTheme(localPath: string, cb: GBServiceCallback<any>) { deployTheme(localPath: string) {
// DISABLED: Until completed, "/ui/public". // DISABLED: Until completed, "/ui/public".
// FsExtra.copy(localPath, this.workDir + packageName) // FsExtra.copy(localPath, this.workDir + packageName)
// .then(() => { // .then(() => {
// cb(null, null);
// }) // })
// .catch(err => { // .catch(err => {
// var gberr = GBError.create( // var gberr = GBError.create(
// `GuaribasBusinessError: Error copying package: ${localPath}.` // `GuaribasBusinessError: Error copying package: ${localPath}.`
// ); // );
// cb(null, gberr);
// }); // });
} }
deployPackageFromLocalPath(localPath: string, cb: GBServiceCallback<any>) { async deployPackageFromLocalPath(localPath: string) {
let packageType = Path.extname(localPath); let packageType = Path.extname(localPath);
switch (packageType) { switch (packageType) {
case ".gbot": case ".gbot":
this.deployBot(localPath, cb); return this.deployBot(localPath);
break;
case ".gbtheme": case ".gbtheme":
this.deployTheme(localPath, cb); return this.deployTheme(localPath);
break;
// PACKAGE: Put in package logic. // PACKAGE: Put in package logic.
case ".gbkb": case ".gbkb":
let service = new KBService(); let service = new KBService();
service.deployKb(this.core, this, localPath, cb); return service.deployKb(this.core, this, localPath);
break;
case ".gbui": case ".gbui":
break; break;
default: default:
var err = GBError.create( var err = GBError.create(
`GuaribasBusinessError: Unknow package type: ${packageType}.` `GuaribasBusinessError: Unknow package type: ${packageType}.`
); );
cb(null, err); Promise.reject(err);
break; break;
} }
} }
undeployPackageFromLocalPath( async undeployPackageFromLocalPath(
instance: IGBInstance, instance: IGBInstance,
localPath: string, localPath: string
cb: GBServiceCallback<any>
) { ) {
let packageType = Path.extname(localPath); let packageType = Path.extname(localPath);
let packageName = Path.basename(localPath); let packageName = Path.basename(localPath);
this.getPackageByName(instance.instanceId, packageName, (p, err) => { let p = await this.getPackageByName(instance.instanceId, packageName);
switch (packageType) { switch (packageType) {
case ".gbot": case ".gbot":
// TODO: this.undeployBot(packageName, localPath, cb); // TODO: this.undeployBot(packageName, localPath);
break; break;
case ".gbtheme": case ".gbtheme":
// TODO: this.undeployTheme(packageName, localPath, cb); // TODO: this.undeployTheme(packageName, localPath);
break; break;
case ".gbkb": case ".gbkb":
let service = new KBService(); let service = new KBService();
service.undeployKbFromStorage(instance, p.packageId, cb); return service.undeployKbFromStorage(instance, p.packageId);
break;
case ".gbui": case ".gbui":
break; break;
default: default:
var err = GBError.create( var err = GBError.create(
`GuaribasBusinessError: Unknow package type: ${packageType}.` `GuaribasBusinessError: Unknown package type: ${packageType}.`
); );
cb(null, err); Promise.reject(err);
break; break;
} }
});
} }
getPackageByName( async getPackageByName(instanceId: number, packageName: string):
instanceId: number, Promise<GuaribasPackage> {
packageName: string,
cb: GBServiceCallback<GuaribasPackage>
) {
var where = { packageName: packageName, instanceId: instanceId }; var where = { packageName: packageName, instanceId: instanceId };
return GuaribasPackage.findOne({
GuaribasPackage.findOne({
where: where where: where
})
.then((value: GuaribasPackage) => {
cb(value, null);
})
.error(reason => {
cb(null, reason);
}); });
} }
/** /**
* *
* Hot deploy processing. * Hot deploy processing.
* *
*/ */
scanBootPackage(cb: GBServiceCallback<boolean>) { async scanBootPackage() {
const deployFolder = "deploy"; const deployFolder = "deploy";
let bootPackage = GBConfigService.get("BOOT_PACKAGE"); let bootPackage = GBConfigService.get("BOOT_PACKAGE");
if (bootPackage === "none") { if (bootPackage === "none") {
cb(true, null); return Promise.resolve(true);
} else { } else {
this.deployPackageFromLocalPath( return this.deployPackageFromLocalPath(
UrlJoin(deployFolder, bootPackage), UrlJoin(deployFolder, bootPackage)
(data, err) => {
logger.info(`Boot package deployed: ${bootPackage}`);
if (err) logger.info(err);
}
); );
} }
} }

View file

@ -33,22 +33,10 @@
"use strict"; "use strict";
const _ = require("lodash");
const Parse = require("csv-parse");
const Async = require("async");
const UrlJoin = require("url-join"); const UrlJoin = require("url-join");
const Walk = require("fs-walk");
const logger = require("../../../src/logger");
import { KBService } from './../../kb.gbapp/services/KBService';
import { Sequelize } from 'sequelize-typescript';
import { Promise } from "bluebird";
import Fs = require("fs"); import Fs = require("fs");
import Path = require("path"); import Path = require("path");
import { DataTypeUUIDv1 } from "sequelize"; import { IGBCoreService, IGBInstance } from "botlib";
import { GBConfigService } from "./GBConfigService";
import { GBCoreService } from "./GBCoreService";
import { GBServiceCallback, IGBCoreService, IGBInstance } from "botlib";
import { SecService } from "../../security.gblib/services/SecService"; import { SecService } from "../../security.gblib/services/SecService";
import { GuaribasInstance } from "../models/GBModel"; import { GuaribasInstance } from "../models/GBModel";
@ -58,12 +46,10 @@ export class GBImporter {
constructor(core: IGBCoreService) { constructor(core: IGBCoreService) {
this.core = core; this.core = core;
} }
importIfNotExistsBotPackage(
async importIfNotExistsBotPackage(
packageName: string, packageName: string,
localPath: string, localPath: string) {
cb: GBServiceCallback<IGBInstance>
) {
let _this_ = this;
let packageJson = JSON.parse( let packageJson = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "package.json"), "utf8") Fs.readFileSync(UrlJoin(localPath, "package.json"), "utf8")
@ -71,20 +57,18 @@ export class GBImporter {
let botId = packageJson.botId; let botId = packageJson.botId;
this.core.loadInstance(botId, (instance, err) => { let instance = await this.core.loadInstance(botId);
if (instance) { if (instance) {
cb(instance, null); return Promise.resolve(instance);
} else { } else {
this.createInstanceInternal(packageName, localPath, packageJson, cb); return this.createInstanceInternal(packageName, localPath, packageJson);
} }
});
} }
private createInstanceInternal( private async createInstanceInternal(
packageName: string, packageName: string,
localPath: string, localPath: string,
packageJson: any, packageJson: any
cb: GBServiceCallback<IGBInstance>
) { ) {
const settings = JSON.parse( const settings = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "settings.json"), "utf8") Fs.readFileSync(UrlJoin(localPath, "settings.json"), "utf8")
@ -97,11 +81,10 @@ export class GBImporter {
GuaribasInstance.create(packageJson).then((instance: IGBInstance) => { GuaribasInstance.create(packageJson).then((instance: IGBInstance) => {
// PACKAGE: security.json loading
let service = new SecService(); let service = new SecService();
// TODO: service.importSecurityFile(localPath, instance); // TODO: service.importSecurityFile(localPath, instance);
cb(instance, null); Promise.resolve(instance);
}); });
} }
} }

View file

@ -77,7 +77,6 @@ export class GBMinService {
* Static initialization of minimal instance. * Static initialization of minimal instance.
* *
* @param core Basic database services to identify instance, for example. * @param core Basic database services to identify instance, for example.
* @param cb Returns the loaded instance.
*/ */
constructor( constructor(
core: GBCoreService, core: GBCoreService,
@ -91,9 +90,7 @@ export class GBMinService {
/** Constructs a new minimal instance for each bot. */ /** Constructs a new minimal instance for each bot. */
buildMin(cb: GBServiceCallback<GBMinInstance>, server: any, appPackages: Array<IGBPackage>) { async buildMin(server: any, appPackages: Array<IGBPackage>): Promise<GBMinInstance> {
var _this_ = this;
// Serves default UI on root address '/'. // Serves default UI on root address '/'.
@ -105,11 +102,12 @@ export class GBMinService {
// Loads all bot instances from storage. // Loads all bot instances from storage.
_this_.core.loadInstances((instances: IGBInstance[], err) => { let instances = await this.core.loadInstances();
// Gets the authorization key for each instance from Bot Service. // Gets the authorization key for each instance from Bot Service.
instances.forEach(instance => { Promise.all(instances).then(async (instance: IGBInstance) => {
let options = { let options = {
url: url:
"https://directline.botframework.com/v3/directline/tokens/generate", "https://directline.botframework.com/v3/directline/tokens/generate",
@ -118,21 +116,20 @@ export class GBMinService {
Authorization: `Bearer ${instance.webchatKey}` Authorization: `Bearer ${instance.webchatKey}`
} }
}; };
request(options).then((response:
string) => { let response = await request(options);
// Serves the bot information object via http so clients can get // Serves the bot information object via http so clients can get
// instance information stored on server. // instance information stored on server.
let responseObject = JSON.parse(response); let responseObject = JSON.parse(response);
server.get("/instances/:botId", (req, res) => { server.get("/instances/:botId", (req, res) => {
(async () => {
// Returns the instance object to clients requesting bot info. // Returns the instance object to clients requesting bot info.
let botId = req.params.botId; let botId = req.params.botId;
_this_.core.loadInstance( let instance = await this.core.loadInstance(botId);
botId,
(instance: IGBInstance, err) => {
if (instance) { if (instance) {
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0 // TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
@ -145,8 +142,8 @@ export class GBMinService {
"Ocp-Apim-Subscription-Key": instance.speechKey "Ocp-Apim-Subscription-Key": instance.speechKey
} }
}; };
request(options).then((response:
string) => { response = await request(options);
res.send( res.send(
JSON.stringify({ JSON.stringify({
@ -158,18 +155,11 @@ export class GBMinService {
conversationId: responseObject.conversationId conversationId: responseObject.conversationId
}) })
); );
}).catch((reason) => {
let error = `Error loading Speech Service: ${reason}.`;
res.send(error);
logger.error(error);
});
} else { } else {
let error = `Instance not found: ${botId}.`; let error = `Instance not found: ${botId}.`;
res.send(error); res.send(error);
logger.error(error); logger.error(error);
} }
}
);
}); });
}); });
@ -190,12 +180,10 @@ export class GBMinService {
min.botId = instance.botId; min.botId = instance.botId;
min.bot = adapter; min.bot = adapter;
min.userState = userState; min.userState = userState;
min.core = _this_.core; min.core = this.core;
min.conversationalService = _this_.conversationalService; min.conversationalService = this.conversationalService;
_this_.core.loadInstance(min.botId, (data, err) => { min.instance = await this.core.loadInstance(min.botId);
min.instance = data;
// Call the loadBot context.activity for all packages. // Call the loadBot context.activity for all packages.
@ -220,8 +208,6 @@ export class GBMinService {
e.loadBot(min); e.loadBot(min);
}); });
});
// Serves individual URL for each bot conversational interface... // Serves individual URL for each bot conversational interface...
@ -230,7 +216,6 @@ export class GBMinService {
`GeneralBots(${instance.engineName}) listening on: ${url}.` `GeneralBots(${instance.engineName}) listening on: ${url}.`
); );
min.dialogs.add('textPrompt', new TextPrompt()); min.dialogs.add('textPrompt', new TextPrompt());
server.post(`/api/messages/${instance.botId}`, (req, res) => { server.post(`/api/messages/${instance.botId}`, (req, res) => {
@ -341,10 +326,8 @@ export class GBMinService {
// next(); // next();
// Specialized load for each min instance. // Specialized load for each min instance.
});
cb(min, null);
});
});
} }
/** Performs package deployment in all .gbai or default. */ /** Performs package deployment in all .gbai or default. */
@ -352,7 +335,6 @@ export class GBMinService {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
var _this_ = this;
let totalPackages = 0; let totalPackages = 0;
let additionalPath = GBConfigService.get("ADDITIONAL_DEPLOY_PATH"); let additionalPath = GBConfigService.get("ADDITIONAL_DEPLOY_PATH");
let paths = [this.deployFolder]; let paths = [this.deployFolder];
@ -388,19 +370,19 @@ export class GBMinService {
} }
logger.info(`Starting looking for generalPackages...`); logger.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`);
paths.forEach(e => { paths.forEach(e => {
logger.info(`Looking in: ${e}...`); logger.info(`Looking in: ${e}...`);
doIt(e); doIt(e);
}); });
/** Deploys all .gbapp files first. */ /** Deploys all .gbapp files first. */
let appPackagesProcessed = 0; let appPackagesProcessed = 0;
gbappPackages.forEach(e => { gbappPackages.forEach(e => {
logger.info(`Deploying app: ${e}...`); logger.info(`Deploying app: ${e}...`);
// Skips .gbapp inside deploy folder. // Skips .gbapp inside deploy folder.
if (!e.startsWith('deploy')) { if (!e.startsWith('deploy')) {
import(e).then(m => { import(e).then(m => {
@ -435,13 +417,11 @@ export class GBMinService {
botPackages.forEach(e => { botPackages.forEach(e => {
logger.info(`Deploying bot: ${e}...`); logger.info(`Deploying bot: ${e}...`);
_this_.deployer.deployBot(e, (data, err) => { this.deployer.deployBot(e, (data, err) => {
logger.info(`Bot: ${e} deployed...`); logger.info(`Bot: ${e} deployed...`);
}); });
}); });
// TODO: Wait here.
/** Then all remaining generalPackages are loaded. */ /** Then all remaining generalPackages are loaded. */
generalPackages.forEach(filename => { generalPackages.forEach(filename => {

View file

@ -41,6 +41,12 @@ import { BotAdapter } from 'botbuilder';
export class FeedbackDialog extends IGBDialog { export class FeedbackDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService(); const service = new CSService();
@ -58,10 +64,9 @@ export class FeedbackDialog extends IGBDialog {
async (dc, value) => { async (dc, value) => {
let rate = value.entity; let rate = value.entity;
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context);
service.updateConversationRate(user.conversation, rate, item => { await service.updateConversationRate(user.conversation, rate);
let messages = ["Obrigado!", "Obrigado por responder."]; let messages = ["Obrigado!", "Obrigado por responder."];
dc.context.sendActivity(messages[0]); // TODO: Handle rnd. await dc.context.sendActivity(messages[0]); // TODO: Handle rnd.
});
} }
]); ]);

View file

@ -41,6 +41,12 @@ const logger = require("../../../src/logger");
export class QualityDialog extends IGBDialog { export class QualityDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService(); const service = new CSService();
@ -61,25 +67,22 @@ export class QualityDialog extends IGBDialog {
"Lamento... Vamos tentar novamente!", "Lamento... Vamos tentar novamente!",
"Desculpe-me. Por favor, tente escrever de outra forma?" "Desculpe-me. Por favor, tente escrever de outra forma?"
]; ];
dc.context.sendActivity(msg[0]); await dc.context.sendActivity(msg[0]);
} else { } else {
let msg = [ let msg = [
"Ótimo, obrigado por contribuir com sua resposta.", "Ótimo, obrigado por contribuir com sua resposta.",
"Certo, obrigado pela informação.", "Certo, obrigado pela informação.",
"Obrigado pela contribuição." "Obrigado pela contribuição."
]; ];
dc.context.sendActivity(msg[0]); await dc.context.sendActivity(msg[0]);
service.insertQuestionAlternate( await service.insertQuestionAlternate(
min.instance.instanceId, min.instance.instanceId,
user.lastQuestion, user.lastQuestion,
user.lastQuestionId, user.lastQuestionId
(data, err) => {
logger.info("QuestionAlternate inserted.");
}
); );
dc.replace('/ask', {isReturning: true}); await dc.replace('/ask', {isReturning: true});
} }
} }
]); ]);

View file

@ -30,19 +30,6 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
const logger = require("../../../src/logger");
const Path = require("path");
const Fs = require("fs");
const FsExtra = require("fs-extra");
const _ = require("lodash");
const Parse = require("csv-parse");
const Async = require("async");
const UrlJoin = require("url-join");
const Walk = require("fs-walk");
const WaitUntil = require("wait-until");
import { GBServiceCallback } from "botlib";
import { GBDeployer } from "../../core.gbapp/services/GBDeployer";
import { GuaribasQuestionAlternate } from '../models'; import { GuaribasQuestionAlternate } from '../models';
import { GuaribasConversation } from '../../analytics.gblib/models'; import { GuaribasConversation } from '../../analytics.gblib/models';
@ -50,44 +37,45 @@ export class CSService {
resolveQuestionAlternate( resolveQuestionAlternate(
instanceId: number, instanceId: number,
questionTyped: string, questionTyped: string): Promise<GuaribasQuestionAlternate> {
cb: GBServiceCallback<GuaribasQuestionAlternate> return new Promise<GuaribasQuestionAlternate>(
) { (resolve, reject) => {
GuaribasQuestionAlternate.findOne({ GuaribasQuestionAlternate.findOne({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
questionTyped: questionTyped questionTyped: questionTyped
} }
}).then((value: GuaribasQuestionAlternate) => { }).then((value: GuaribasQuestionAlternate) => {
cb(value, null); resolve(value);
}).error(reason => reject(reason));
}); });
} }
insertQuestionAlternate( insertQuestionAlternate(
instanceId: number, instanceId: number,
questionTyped: string, questionTyped: string,
questionText: string, questionText: string): Promise<GuaribasQuestionAlternate> {
cb: GBServiceCallback<GuaribasQuestionAlternate> return new Promise<GuaribasQuestionAlternate>(
) { (resolve, reject) => {
GuaribasQuestionAlternate.create({ GuaribasQuestionAlternate.create({
questionTyped: questionTyped, questionTyped: questionTyped,
questionText: questionText questionText: questionText
}).then(item => { }).then(item => {
if (cb) { resolve(item);
cb(item, null); }).error(reason => reject(reason));
}
}); });
} }
updateConversationRate( updateConversationRate(
conversation: GuaribasConversation, conversation: GuaribasConversation,
rate: number, rate: number
cb: GBServiceCallback<GuaribasConversation> ): Promise<GuaribasConversation> {
) { return new Promise<GuaribasConversation>(
(resolve, reject) => {
conversation.rate = rate; conversation.rate = rate;
conversation.save().then((value: GuaribasConversation) => { conversation.save().then((value: GuaribasConversation) => {
cb(conversation, null); resolve(conversation);
}).error(reason => reject(reason));
}); });
} }
} }

View file

@ -42,6 +42,12 @@ import { LuisRecognizer } from "botbuilder-ai";
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
export class AskDialog extends IGBDialog { export class AskDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(); const service = new KBService();

View file

@ -38,15 +38,21 @@ import { BotAdapter } from "botbuilder";
import { GBMinInstance } from "botlib"; import { GBMinInstance } from "botlib";
export class FaqDialog extends IGBDialog { export class FaqDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(); const service = new KBService();
min.dialogs.add("/faq", [ min.dialogs.add("/faq", [
async (dc, args) => { async (dc, args) => {
service.getFaqBySubjectArray("faq", null, (data, err) => { let data = await service.getFaqBySubjectArray("faq", null);
if (data) { if (data) {
min.conversationalService.sendEvent(dc, "play", { await min.conversationalService.sendEvent(dc, "play", {
playerType: "bullet", playerType: "bullet",
data: data.slice(0, 10) data: data.slice(0, 10)
}); });
@ -57,12 +63,10 @@ export class FaqDialog extends IGBDialog {
"Veja a lista que eu preparei logo aí na tela..." "Veja a lista que eu preparei logo aí na tela..."
]; ];
dc.context.sendActivity(messages[0]); // TODO: RND messages. await dc.context.sendActivity(messages[0]); // TODO: RND messages.
dc.endAll(); await dc.endAll();
} }
});
} }
]); ]);
} }
} }

View file

@ -52,6 +52,12 @@ const WaitUntil = require("wait-until");
export class MenuDialog extends IGBDialog { export class MenuDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
var service = new KBService(); var service = new KBService();
@ -83,16 +89,12 @@ export class MenuDialog extends IGBDialog {
if (user.subjects.length > 0) { if (user.subjects.length > 0) {
service.getFaqBySubjectArray( let data = await service.getFaqBySubjectArray("menu", user.subjects);
"menu", await min.conversationalService.sendEvent(dc, "play", {
user.subjects,
(data, err) => {
min.conversationalService.sendEvent(dc, "play", {
playerType: "bullet", playerType: "bullet",
data: data.slice(0, 6) data: data.slice(0, 6)
}); });
}
);
} }
} else { } else {
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context);
@ -119,12 +121,12 @@ export class MenuDialog extends IGBDialog {
const msg = MessageFactory.text('Greetings from example message'); const msg = MessageFactory.text('Greetings from example message');
var attachments = []; var attachments = [];
service.getSubjectItems( let data = await service.getSubjectItems(
min.instance.instanceId, min.instance.instanceId,
rootSubjectId, rootSubjectId);
data => {
msg.attachmentLayout='carousel';
msg.attachmentLayout = 'carousel';
data.forEach(function (item: GuaribasSubject) { data.forEach(function (item: GuaribasSubject) {
@ -157,26 +159,25 @@ export class MenuDialog extends IGBDialog {
if (attachments.length == 0) { if (attachments.length == 0) {
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context);
if (user.subjects && user.subjects.length > 0) { if (user.subjects && user.subjects.length > 0) {
dc.context.sendActivity( await dc.context.sendActivity(
`Vamos pesquisar sobre ${KBService.getFormattedSubjectItems( `Vamos pesquisar sobre ${KBService.getFormattedSubjectItems(
user.subjects user.subjects
)}?` )}?`
); );
} }
dc.replace("/ask", {}); await dc.replace("/ask", {});
} else { } else {
msg.attachments = attachments; msg.attachments = attachments;
dc.context.sendActivity(msg); await dc.context.sendActivity(msg);
} }
}
);
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context);
user.isAsking = true; user.isAsking = true;
}, },
async (dc, value) => { async (dc, value) => {
var text = value; var text = value;
if (AzureText.isIntentNo(text)) { if (text==="no"||text==="n") { // TODO: Migrate to a common.
dc.replace("/feedback"); dc.replace("/feedback");
} else { } else {
dc.replace("/ask"); dc.replace("/ask");

View file

@ -33,14 +33,17 @@
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
const Path = require("path"); const Path = require("path");
const Fs = require("fs"); const Fs = require("fs");
const Parse = require("csv-parse"); const promise = require('bluebird');
const Async = require("async"); const parse = promise.promisify(require('csv-parse'));
const UrlJoin = require("url-join"); const UrlJoin = require("url-join");
const Walk = require("fs-walk");
const marked = require("marked"); const marked = require("marked");
const path = require("path");
const asyncPromise = require('async-promises');
const walkPromise = require('walk-promise');
import { GBConfigService } from './../../core.gbapp/services/GBConfigService'; import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
import { GuaribasQuestion, GuaribasAnswer, GuaribasSubject } from "../models"; import { GuaribasQuestion, GuaribasAnswer, GuaribasSubject } from "../models";
import { GBServiceCallback, IGBCoreService, IGBConversationalService, IGBInstance } from "botlib"; import { IGBCoreService, IGBConversationalService, IGBInstance } from "botlib";
import { AzureSearch } from "pragmatismo-io-framework"; import { AzureSearch } from "pragmatismo-io-framework";
import { GBDeployer } from "../../core.gbapp/services/GBDeployer"; import { GBDeployer } from "../../core.gbapp/services/GBDeployer";
import { GuaribasPackage } from "../../core.gbapp/models/GBModel"; import { GuaribasPackage } from "../../core.gbapp/models/GBModel";
@ -52,26 +55,32 @@ export class KBServiceSearchResults {
export class KBService { export class KBService {
getAnswerById( async getAnswerById(
instanceId: number, instanceId: number,
answerId: number, answerId: number
cb: GBServiceCallback<GuaribasAnswer> ): Promise<GuaribasAnswer> {
) { return new Promise<GuaribasAnswer>(
(resolve, reject) => {
GuaribasAnswer.findAll({ GuaribasAnswer.findAll({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
answerId: answerId answerId: answerId
} }
}).then((item: GuaribasAnswer[]) => { }).then((item: GuaribasAnswer[]) => {
cb(item[0], null); resolve(item[0]);
}).error((reason) => {
reject(reason);
});
}); });
} }
getAnswerByText( async getAnswerByText(
instanceId: number, instanceId: number,
text: string, text: string
cb: GBServiceCallback<any> ): Promise<any> {
) { return new Promise(
(resolve, reject) => {
GuaribasQuestion.findOne({ GuaribasQuestion.findOne({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
@ -85,21 +94,26 @@ export class KBService {
answerId: question.answerId answerId: question.answerId
} }
}).then((answer: GuaribasAnswer[]) => { }).then((answer: GuaribasAnswer[]) => {
cb({ question: question, answer: answer[0] }, null); resolve({ question: question, answer: answer[0] });
}); });
} }
else { else {
cb(null, null); resolve(null);
} }
}).error((reason) => {
reject(reason);
});
}); });
} }
async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
addAnswer(obj: GuaribasAnswer, cb: GBServiceCallback<GuaribasAnswer>) { return new Promise<GuaribasAnswer>(
(resolve, reject) => {
GuaribasAnswer.create(obj).then(item => { GuaribasAnswer.create(obj).then(item => {
if (cb) { resolve(item);
cb(item, null); }).error((reason) => {
} reject(reason);
});
}); });
} }
@ -110,8 +124,6 @@ export class KBService {
subjects: GuaribasSubject[] subjects: GuaribasSubject[]
): Promise<KBServiceSearchResults> { ): Promise<KBServiceSearchResults> {
return new Promise<KBServiceSearchResults>((resolve, reject) => {
// Builds search query. // Builds search query.
what = what.toLowerCase(); what = what.toLowerCase();
@ -129,13 +141,8 @@ export class KBService {
what = `${what} ${text}`; what = `${what} ${text}`;
} }
} }
// TODO: Filter by instance. what = `${what}&$filter=instanceId eq ${instanceId}`; // TODO: Filter by instance. what = `${what}&$filter=instanceId eq ${instanceId}`;
try {
// Performs search.
var _this_ = this;
if (instance.searchKey && GBConfigService.get("DATABASE_DIALECT") == "mssql") { if (instance.searchKey && GBConfigService.get("DATABASE_DIALECT") == "mssql") {
let service = new AzureSearch( let service = new AzureSearch(
instance.searchKey, instance.searchKey,
@ -143,41 +150,24 @@ export class KBService {
instance.searchIndex, instance.searchIndex,
instance.searchIndexer instance.searchIndexer
); );
let results = await service.search(what);
service.search(what, (err: any, results: any) => { if (results && results.length > 0 &&
if (results && results.length > 0) { results[0]["@search.score"] >= searchScore) {
// Ponders over configuration. let value = await this.getAnswerById(
if (results[0]["@search.score"] >= searchScore) {
_this_.getAnswerById(
instance.instanceId, instance.instanceId,
results[0].answerId, results[0].answerId);
(answer, err) => { return Promise.resolve({ answer: value, questionId: results[0].questionId });
if (err) { reject(err); } else {
resolve({ answer: answer, questionId: results[0].questionId });
}
} }
} else {
let data = await this.getAnswerByText(instance.instanceId, what);
return Promise.resolve(
{ answer: data.answer, questionId: data.question.questionId }
); );
} else {
resolve(null);
}
} else {
resolve(null);
}
});
} else {
this.getAnswerByText(instance.instanceId, what, (data, err) => {
if (data) {
resolve({ answer: data.answer, questionId: data.question.questionId });
}
else {
if (err) { reject(err); } else {
resolve(null);
} }
} }
}); catch (reason) {
return Promise.reject(reason);
} }
});
} }
getSearchSchema(indexName) { getSearchSchema(indexName) {
@ -298,24 +288,29 @@ export class KBService {
return out.join(" "); return out.join(" ");
} }
getSubjectItems( async getSubjectItems(
instanceId: number, instanceId: number,
parentId: number, parentId: number
cb: GBServiceCallback<GuaribasSubject[]> ): Promise<GuaribasSubject[]> {
) { return new Promise<GuaribasSubject[]>(
(resolve, reject) => {
var where = { parentSubjectId: parentId, instanceId: instanceId }; var where = { parentSubjectId: parentId, instanceId: instanceId };
GuaribasSubject.findAll({ GuaribasSubject.findAll({
where: where where: where
}) })
.then((values: GuaribasSubject[]) => { .then((values: GuaribasSubject[]) => {
cb(values, null); resolve(values);
}) })
.error(reason => { .error(reason => {
cb(null, reason); reject(reason);
});
}); });
} }
getFaqBySubjectArray(from: string, subjects: any, cb) { async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
return new Promise<GuaribasQuestion[]>(
(resolve, reject) => {
let where = { let where = {
from: from from: from
}; };
@ -343,53 +338,56 @@ export class KBService {
.then((items: GuaribasQuestion[]) => { .then((items: GuaribasQuestion[]) => {
if (!items) items = []; if (!items) items = [];
if (items.length == 0) { if (items.length == 0) {
cb([], null); resolve([]);
} else { } else {
cb(items, null); resolve(items);
} }
}) })
.catch(reason => { .catch(reason => {
if (reason.message.indexOf("no such table: IGBInstance") != -1) { if (reason.message.indexOf("no such table: IGBInstance") != -1) {
cb([], null); resolve([]);
} else { } else {
cb(null, reason); reject(reason);
logger.info(`GuaribasServiceError: ${reason}`); logger.info(`GuaribasServiceError: ${reason}`);
} }
}); });
});
} }
async importKbTabularFile(
importKbTabularFile( filePath: string,
basedir: string,
filename: string,
instanceId: number, instanceId: number,
packageId: number, packageId: number
cb ): Promise<GuaribasQuestion[]> {
) { return new Promise<GuaribasQuestion[]>(
var filePath = UrlJoin(basedir, filename); (resolve, reject) => {
var parser = Parse( let file = Fs.readFileSync(filePath, "UCS-2");
{ let opts = {
delimiter: "\t" delimiter: "\t"
}, };
function (err, data) {
Async.eachSeries(data, function (line, callback) { var parser = parse(file, opts).then((data) => {
callback(); asyncPromise.eachSeries(data, (line) => {
return new Promise((resolve, reject) => {
// Extracts values from columns in the current line.
let subjectsText = line[0]; let subjectsText = line[0];
var from = line[1]; var from = line[1];
var to = line[2]; var to = line[2];
var question = line[3]; var question = line[3];
var answer = line[4]; var answer = line[4];
// Skip the first line. // Skips the first line.
if (!(subjectsText === "subjects" && from == "from")) { if (!(subjectsText === "subjects" && from == "from")) {
let format = ".txt"; let format = ".txt";
// Extract answer from external media if any. // Extracts answer from external media if any.
if (answer.indexOf(".md") > -1) { if (answer.indexOf(".md") > -1) {
let mediaFilename = UrlJoin(basedir, "..", "articles", answer); let mediaFilename = UrlJoin(path.dirname(filePath), "..", "articles", answer);
if (Fs.existsSync(mediaFilename)) { if (Fs.existsSync(mediaFilename)) {
answer = Fs.readFileSync(mediaFilename, "utf8"); answer = Fs.readFileSync(mediaFilename, "utf8");
format = ".md"; format = ".md";
@ -400,13 +398,13 @@ export class KBService {
} }
} }
let subjectArray = subjectsText.split("."); // Processes subjects hierarchy splitting by dots.
let subject1: string,
subject2: string,
subject3: string,
subject4: string;
let subjectArray = subjectsText.split(".");
let subject1: string, subject2: string, subject3: string,
subject4: string;
var indexer = 0; var indexer = 0;
subjectArray.forEach(element => { subjectArray.forEach(element => {
if (indexer == 0) { if (indexer == 0) {
subject1 = subjectArray[indexer].substring(0, 63); subject1 = subjectArray[indexer].substring(0, 63);
@ -420,12 +418,14 @@ export class KBService {
indexer++; indexer++;
}); });
// Now with all the data ready, creates entities in the store.
GuaribasAnswer.create({ GuaribasAnswer.create({
instanceId: instanceId, instanceId: instanceId,
content: answer, content: answer,
format: format, format: format,
packageId: packageId packageId: packageId
}).then(function (answer: GuaribasAnswer) { }).then((answer: GuaribasAnswer) => {
GuaribasQuestion.create({ GuaribasQuestion.create({
from: from, from: from,
to: to, to: to,
@ -437,27 +437,30 @@ export class KBService {
instanceId: instanceId, instanceId: instanceId,
answerId: answer.answerId, answerId: answer.answerId,
packageId: packageId packageId: packageId
}); }).then((question: GuaribasQuestion) => {
}); resolve(question);
}).error(reason => reject(reason));
}).error(reason => reject(reason));;
} else { } else {
logger.warn("[GBImporter] Missing header in file: ", filename); logger.warn("[GBImporter] Missing header in file: ", filePath);
} }
}); });
} });
); }).error(reason => reject(reason));
Fs.createReadStream(filePath, { });
encoding: "UCS-2"
}).pipe(parser);
} }
sendAnswer(conversationalService: IGBConversationalService, dc: any, answer: GuaribasAnswer) { sendAnswer(conversationalService: IGBConversationalService,
dc: any, answer: GuaribasAnswer) {
if (answer.content.endsWith('.mp4')) { if (answer.content.endsWith('.mp4')) {
conversationalService.sendEvent(dc, "play", { conversationalService.sendEvent(dc, "play", {
playerType: "video", playerType: "video",
data: answer.content data: answer.content
}); });
} else if (answer.content.length > 140 && dc.message.source != "directline") { } else if (answer.content.length > 140 &&
dc.message.source != "directline") {
let messages = [ let messages = [
"Vou te responder na tela para melhor visualização...", "Vou te responder na tela para melhor visualização...",
"A resposta está na tela...", "A resposta está na tela...",
@ -486,67 +489,67 @@ export class KBService {
} }
} }
async importKbPackage(
importKbPackage(
localPath: string, localPath: string,
packageStorage: GuaribasPackage, packageStorage: GuaribasPackage,
instance: IGBInstance instance: IGBInstance
) { ): Promise<GuaribasQuestion[]> {
return new Promise<GuaribasQuestion[]>(
(resolve, reject) => {
// Imports subjects tree into database and return it.
this.importSubjectFile( this.importSubjectFile(
packageStorage.packageId, packageStorage.packageId,
UrlJoin(localPath, "subjects.json"), UrlJoin(localPath, "subjects.json"),
instance instance
); ).then((value: GuaribasQuestion[]) => {
let _this_ = this;
setTimeout(() => { // Import all .tsv files in the tabular directory.
_this_.importKbTabularDirectory(
this.importKbTabularDirectory(
localPath, localPath,
instance, instance,
packageStorage.packageId packageStorage.packageId
); );
}, 3000); });
});
} }
importKbTabularDirectory( importKbTabularDirectory(
localPath: string, localPath: string,
instance: IGBInstance, instance: IGBInstance,
packageId: number packageId: number
) { ): Promise<GuaribasQuestion[]> {
let _this_ = this; return new Promise(
Walk.files( (resolve, reject) => {
UrlJoin(localPath, "tabular"),
(basedir, filename, stat, next) => { walkPromise(UrlJoin(localPath, "tabular")).then((files) => {
if (filename.endsWith(".tsv")) { files.array.forEach(file => {
_this_.importKbTabularFile( if (file.endsWith(".tsv")) {
basedir, this.importKbTabularFile(
filename, file,
instance.instanceId, instance.instanceId,
packageId, packageId);
(data, err) => {
if (err) {
logger.info(err);
} else {
logger.info("Import KB done.");
} }
} });
); });
} });
},
function (err) {
if (err) logger.info(err);
}
);
} }
importSubjectFile( async importSubjectFile(
packageId: number, packageId: number,
filename: string, filename: string,
instance: IGBInstance instance: IGBInstance
) { ): Promise<GuaribasQuestion[]> {
return new Promise<GuaribasQuestion[]>(
(resolve, reject) => {
var subjects = JSON.parse(Fs.readFileSync(filename, "utf8")); var subjects = JSON.parse(Fs.readFileSync(filename, "utf8"));
function doIt(subjects: GuaribasSubject[], parentSubjectId: number) { const doIt = (subjects: GuaribasSubject[], parentSubjectId: number) =>
Async.eachSeries(subjects, (item, callback) => { new Promise((resolve, reject) => {
asyncPromise.eachSeries(subjects, (item, callback) => {
let mediaFilename = item.id + ".png"; let mediaFilename = item.id + ".png";
GuaribasSubject.create({ GuaribasSubject.create({
internalId: item.id, internalId: item.id,
@ -564,16 +567,20 @@ export class KBService {
}); });
callback(); callback();
}); });
} });
doIt(subjects.children, null);
}
doIt(subjects.children, null);
resolve()
});
}
undeployKbFromStorage( undeployKbFromStorage(
instance: IGBInstance, instance: IGBInstance,
packageId: number, packageId: number
cb: GBServiceCallback<any>
) { ) {
// TODO: call reject.
return new Promise(
(resolve, reject) => {
GuaribasQuestion.destroy({ GuaribasQuestion.destroy({
where: { instanceId: instance.instanceId, packageId: packageId } where: { instanceId: instance.instanceId, packageId: packageId }
}).then(value => { }).then(value => {
@ -586,7 +593,8 @@ export class KBService {
GuaribasPackage.destroy({ GuaribasPackage.destroy({
where: { instanceId: instance.instanceId, packageId: packageId } where: { instanceId: instance.instanceId, packageId: packageId }
}).then(value => { }).then(value => {
cb(null, null); resolve(null);
});
}); });
}); });
}); });
@ -597,9 +605,8 @@ export class KBService {
* Deploys a knowledge base to the storage using the .gbkb format. * Deploys a knowledge base to the storage using the .gbkb format.
* *
* @param localPath Path to the .gbkb folder. * @param localPath Path to the .gbkb folder.
* @param cb Package instance or error info.
*/ */
deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string, cb: GBServiceCallback<any>) { async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
let packageType = Path.extname(localPath); let packageType = Path.extname(localPath);
let packageName = Path.basename(localPath); let packageName = Path.basename(localPath);
logger.info("[GBDeployer] Opening package: ", packageName); logger.info("[GBDeployer] Opening package: ", packageName);
@ -607,18 +614,10 @@ export class KBService {
Fs.readFileSync(UrlJoin(localPath, "package.json"), "utf8") Fs.readFileSync(UrlJoin(localPath, "package.json"), "utf8")
); );
core.loadInstance(packageObject.botId, (instance, err) => { let instance = await core.loadInstance(packageObject.botId);
deployer.deployPackageToStorage( let p = await deployer.deployPackageToStorage(
instance.instanceId, instance.instanceId,
packageName, packageName);
(p, err) => { await this.importKbPackage(localPath, p, instance);
this.importKbPackage(localPath, p, instance);
setTimeout(() => {
cb(null, null);
}, 8000);
} }
);
});
}
} }

View file

@ -44,8 +44,8 @@ import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from "../models";
export class SecService extends GBService { export class SecService extends GBService {
importSecurityFile(localPath: string, instance: IGBInstance) { async importSecurityFile(localPath: string, instance: IGBInstance) {
var security = JSON.parse( let security = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "security.json"), "utf8") Fs.readFileSync(UrlJoin(localPath, "security.json"), "utf8")
); );
security.groups.forEach(group => { security.groups.forEach(group => {
@ -71,15 +71,17 @@ export class SecService extends GBService {
}); });
} }
ensureUser( async ensureUser(
instanceId: number, instanceId: number,
userSystemId: string, userSystemId: string,
userName: string, userName: string,
address: string, address: string,
channelName: string, channelName: string,
displayName: string, displayName: string
cb: GBServiceCallback<GuaribasUser> ): Promise<GuaribasUser> {
) { return new Promise<GuaribasUser>(
(resolve, reject) => {
GuaribasUser.findOne({ GuaribasUser.findOne({
attributes: ["instanceId", "internalAddress"], attributes: ["instanceId", "internalAddress"],
where: { where: {
@ -97,7 +99,8 @@ export class SecService extends GBService {
user.email = userName; user.email = userName;
user.defaultChannel = channelName; user.defaultChannel = channelName;
user.save(); user.save();
cb(user, null); resolve(user);
}).error(reason => reject(reason));
}); });
} }
} }

View file

@ -138,7 +138,6 @@ export class WhatsappDirectLine extends GBService {
.then((conversationId) => { .then((conversationId) => {
this.conversationIds[from] = conversationId; this.conversationIds[from] = conversationId;
this.inputMessage(client, conversationId, text, this.inputMessage(client, conversationId, text,
from, fromName); from, fromName);

View file

@ -56,6 +56,7 @@
"tedious": "^2.6.4", "tedious": "^2.6.4",
"url-join": "^4.0.0", "url-join": "^4.0.0",
"wait-until": "^0.0.2", "wait-until": "^0.0.2",
"walk-promise": "^0.2.0",
"winston": "^3.0.0" "winston": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -77,7 +77,9 @@ export class GBServer {
extended: true extended: true
})); }));
server.listen(port, () => { server.listen(port, async () => {
try {
logger.info(`Accepting connections on ${port}...`); logger.info(`Accepting connections on ${port}...`);
logger.info(`Starting instances...`); logger.info(`Starting instances...`);
@ -86,7 +88,7 @@ export class GBServer {
GBConfigService.init(); GBConfigService.init();
let core = new GBCoreService(); let core = new GBCoreService();
core.initDatabase(() => { await core.initDatabase();
// Boot a bot package if any. // Boot a bot package if any.
@ -104,24 +106,17 @@ export class GBServer {
p.loadPackage(core, core.sequelize); p.loadPackage(core, core.sequelize);
}); });
(async () => {
try {
await minService.deployPackages(core, server, appPackages); await minService.deployPackages(core, server, appPackages);
logger.info(`The Bot Server is in RUNNING mode...`); logger.info(`The Bot Server is in RUNNING mode...`);
minService.buildMin(instance => { let instance = await minService.buildMin(server, appPackages);
logger.info(`Instance loaded: ${instance.botId}...`); logger.info(`Instance loaded: ${instance.botId}...`);
}, server, appPackages); return core;
} catch (err) { } catch (err) {
logger.info(err); logger.info(err);
} }
})()
}); });
return core;
});
} }
} }