Adding tslint.json and working on Cognitive Services NPM package upgrade.

This commit is contained in:
Rodrigo Rodriguez 2018-09-11 19:33:58 -03:00
parent bd03cfbc6c
commit d07b6350a0
11 changed files with 658 additions and 606 deletions

View file

@ -52,7 +52,6 @@ import {
CreatedAt, CreatedAt,
UpdatedAt, UpdatedAt,
DataType, DataType,
IsUUID,
PrimaryKey, PrimaryKey,
AutoIncrement AutoIncrement
} from "sequelize-typescript"; } from "sequelize-typescript";
@ -90,6 +89,8 @@ export class GuaribasInstance extends Model<GuaribasInstance> implements IGBInst
@Column textAnalyticsKey: string; @Column textAnalyticsKey: string;
@Column textAnalyticsServerUrl: string;
@Column marketplacePassword: string; @Column marketplacePassword: string;
@Column webchatKey: string; @Column webchatKey: string;

View file

@ -30,73 +30,72 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict"; "use strict";
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
import { GBCoreService } from "./GBCoreService"; import { GBCoreService } from "./GBCoreService";
import { IGBConversationalService } from "botlib"; import { IGBConversationalService } from "botlib";
import { GBError } 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 interface LanguagePickerSettings {
defaultLocale?: string;
supportedLocales?: string[];
}
export class GBConversationalService implements IGBConversationalService { export class GBConversationalService implements IGBConversationalService {
coreService: GBCoreService; coreService: GBCoreService;
constructor(coreService: GBCoreService) { constructor(coreService: GBCoreService) {
this.coreService = coreService; this.coreService = coreService;
} }
getCurrentLanguage(dc: any) {
return dc.context.activity.locale;
}
async sendEvent(dc: any, name: string, value: any): Promise<any> { async sendEvent(dc: any, name: string, value: any): Promise<any> {
const msg = MessageFactory.text(''); const msg = MessageFactory.text("");
msg.value = value; msg.value = value;
msg.type = "event"; msg.type = "event";
msg.name = name; msg.name = name;
return dc.context.sendActivity(msg); return dc.context.sendActivity(msg);
} }
async runNLP( async runNLP(dc: any, min: GBMinInstance, text: string): Promise<any> {
dc: any, // Invokes LUIS.
min: GBMinInstance,
text: string
): Promise<any> {
const model = new LuisRecognizer({ const model = new LuisRecognizer({
appId: min.instance.nlpAppId, appId: min.instance.nlpAppId,
subscriptionKey: min.instance.nlpSubscriptionKey, subscriptionKey: min.instance.nlpSubscriptionKey,
serviceEndpoint: min.instance.nlpServerUrl serviceEndpoint: min.instance.nlpServerUrl
}); });
let res = await model.recognize(dc.context); let res = await model.recognize(dc.context);
// Resolve intents returned from LUIS // Resolves intents returned from LUIS.
let topIntent = LuisRecognizer.topIntent(res);
let topIntent = LuisRecognizer.topIntent(res);
if (topIntent) { if (topIntent) {
var intent = topIntent; var intent = topIntent;
var entity = var entity =
res.entities && res.entities.length > 0 res.entities && res.entities.length > 0
? res.entities[0].entity.toUpperCase() ? res.entities[0].entity.toUpperCase()
: null; : null;
logger.info( logger.info("luis: intent: [" + intent + "] entity: [" + entity + "]");
"luis: intent: [" + intent + "] entity: [" + entity + "]"
);
try { try {
await dc.replace("/" + intent); await dc.replace("/" + intent);
} catch (error) { } catch (error) {
logger.info("error: intent: [" + intent + "] error: [" + error + "]"); logger.info("error: intent: [" + intent + "] error: [" + error + "]");
await dc.context.sendActivity("Desculpe-me, não encontrei nada a respeito..."); await dc.context.sendActivity(
"Desculpe-me, não encontrei nada a respeito..."
);
await dc.replace("/ask", { isReturning: true }); await dc.replace("/ask", { isReturning: true });
} }
return Promise.resolve({ intent, entities: res.entities }); return Promise.resolve({ intent, entities: res.entities });
} else { } else {
await dc.context.sendActivity("Lamento, não achei nada a respeito..."); await dc.context.sendActivity("Lamento, não achei nada a respeito...");
await dc.replace("/ask", { isReturning: true }); await dc.replace("/ask", { isReturning: true });

View file

@ -33,16 +33,15 @@
"use strict"; "use strict";
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from "sequelize-typescript";
import { GBConfigService } from "./GBConfigService"; import { GBConfigService } from "./GBConfigService";
import { IGBInstance, IGBCoreService } from 'botlib'; import { IGBInstance, IGBCoreService } from "botlib";
import { GuaribasInstance } from "../models/GBModel"; import { GuaribasInstance } from "../models/GBModel";
/** /**
* Core service layer. * Core service layer.
*/ */
export class GBCoreService implements IGBCoreService { export class GBCoreService implements IGBCoreService {
/** /**
* Data access layer instance. * Data access layer instance.
*/ */
@ -79,11 +78,8 @@ export class GBCoreService implements IGBCoreService {
* Gets database config and connect to storage. * Gets database config and connect to storage.
*/ */
async initDatabase() { async initDatabase() {
return new Promise( return new Promise((resolve, reject) => {
(resolve, reject) => {
try { try {
let host: string | undefined; let host: string | undefined;
let database: string | undefined; let database: string | undefined;
let username: string | undefined; let username: string | undefined;
@ -99,11 +95,14 @@ export class GBCoreService implements IGBCoreService {
storage = GBConfigService.get("DATABASE_STORAGE"); storage = GBConfigService.get("DATABASE_STORAGE");
} }
let logging = (GBConfigService.get("DATABASE_LOGGING") === "true") let logging =
? (str: string) => { logger.info(str); } GBConfigService.get("DATABASE_LOGGING") === "true"
? (str: string) => {
logger.info(str);
}
: false; : false;
let encrypt = (GBConfigService.get("DATABASE_ENCRYPT") === "true"); let encrypt = GBConfigService.get("DATABASE_ENCRYPT") === "true";
this.sequelize = new Sequelize({ this.sequelize = new Sequelize({
host: host, host: host,
@ -123,14 +122,17 @@ export class GBCoreService implements IGBCoreService {
idle: 40000, idle: 40000,
evict: 40000, evict: 40000,
acquire: 40000 acquire: 40000
}, }
}); });
if (this.dialect === "mssql") { if (this.dialect === "mssql") {
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator; this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
this.createTableQuery = this.queryGenerator.createTableQuery; this.createTableQuery = this.queryGenerator.createTableQuery;
this.queryGenerator.createTableQuery = (tableName, attributes, options) => this.queryGenerator.createTableQuery = (
this.createTableQueryOverride(tableName, attributes, options); tableName,
attributes,
options
) => this.createTableQueryOverride(tableName, attributes, options);
this.changeColumnQuery = this.queryGenerator.changeColumnQuery; this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
this.queryGenerator.changeColumnQuery = (tableName, attributes) => this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
this.changeColumnQueryOverride(tableName, attributes); this.changeColumnQueryOverride(tableName, attributes);
@ -142,86 +144,119 @@ export class GBCoreService implements IGBCoreService {
}); });
} }
/**
* 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' +
* // ' FOREIGN KEY ([instanceId]) REFERENCES [Instance] ([instanceId]) ON DELETE NO ACTION);';
*/
private createTableQueryOverride(tableName, attributes, options): string { private createTableQueryOverride(tableName, attributes, options): string {
let sql: string = this.createTableQuery.apply(this.queryGenerator, let sql: string = this.createTableQuery.apply(this.queryGenerator, [
[tableName, attributes, options]); tableName,
// let sql: string = '' + attributes,
// 'IF OBJECT_ID(\'[UserGroup]\', \'U\') IS NULL\n' + options
// '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' +
// ' FOREIGN KEY ([instanceId]) REFERENCES [Instance] ([instanceId]) ON DELETE NO ACTION);';
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/; const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql); const matches = re1.exec(sql);
if (matches) { if (matches) {
const table = matches[1]; const table = matches[1];
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/; const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
sql = sql.replace(re2, (match: string, ...args: any[]): string => { sql = sql.replace(
return 'CONSTRAINT [' + table + '_pk] ' + match; re2,
}); (match: string, ...args: any[]): string => {
return "CONSTRAINT [" + table + "_pk] " + match;
}
);
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g; const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re4 = /\[([^\]]*)\]/g; const re4 = /\[([^\]]*)\]/g;
sql = sql.replace(re3, (match: string, ...args: any[]): string => { sql = sql.replace(
re3,
(match: string, ...args: any[]): string => {
const fkcols = args[0]; const fkcols = args[0];
let fkname = table; let fkname = table;
let matches = re4.exec(fkcols); let matches = re4.exec(fkcols);
while (matches != null) { while (matches != null) {
fkname += '_' + matches[1]; fkname += "_" + matches[1];
matches = re4.exec(fkcols); matches = re4.exec(fkcols);
} }
return 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')'; return "CONSTRAINT [" + fkname + "_fk] FOREIGN KEY (" + fkcols + ")";
}); }
);
} }
return sql; return sql;
} }
/**
* 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';
*/
private changeColumnQueryOverride(tableName, attributes): string { private changeColumnQueryOverride(tableName, attributes): string {
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [
[tableName, attributes]); tableName,
// let sql = '' + attributes
// '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';
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/; const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql); const matches = re1.exec(sql);
if (matches) { if (matches) {
const table = matches[1]; const table = matches[1];
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g; const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re3 = /\[([^\]]*)\]/g; const re3 = /\[([^\]]*)\]/g;
sql = sql.replace(re2, (match: string, ...args: any[]): string => { sql = sql.replace(
re2,
(match: string, ...args: any[]): string => {
const fkcols = args[2]; const fkcols = args[2];
let fkname = table; let fkname = table;
let matches = re3.exec(fkcols); let matches = re3.exec(fkcols);
while (matches != null) { while (matches != null) {
fkname += '_' + matches[1]; fkname += "_" + matches[1];
matches = re3.exec(fkcols); 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; return sql;
} }
async syncDatabaseStructure() { async syncDatabaseStructure() {
return new Promise( return new Promise((resolve, reject) => {
(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";
logger.info("Syncing database..."); logger.info("Syncing database...");
this.sequelize.sync({ this.sequelize
.sync({
alter: alter, alter: alter,
force: force force: force
}).then(value => { })
.then(
value => {
logger.info("Database synced."); logger.info("Database synced.");
resolve(value); resolve(value);
}, err => reject(err)); },
err => reject(err)
);
} else { } else {
logger.info("Database synchronization is disabled."); logger.info("Database synchronization is disabled.");
resolve(); resolve();
@ -233,8 +268,7 @@ export class GBCoreService implements IGBCoreService {
* Loads all items to start several listeners. * Loads all items to start several listeners.
*/ */
async loadInstances(): Promise<IGBInstance> { async loadInstances(): Promise<IGBInstance> {
return new Promise( return new Promise((resolve, reject) => {
(resolve, reject) => {
GuaribasInstance.findAll({}) GuaribasInstance.findAll({})
.then((items: IGBInstance[]) => { .then((items: IGBInstance[]) => {
if (!items) items = []; if (!items) items = [];
@ -260,9 +294,7 @@ export class GBCoreService implements IGBCoreService {
* Loads just one Bot instance. * Loads just one Bot instance.
*/ */
async loadInstance(botId: string): Promise<IGBInstance> { async loadInstance(botId: string): Promise<IGBInstance> {
return new Promise<IGBInstance>( return new Promise<IGBInstance>((resolve, reject) => {
(resolve, reject) => {
let options = { where: {} }; let options = { where: {} };
if (botId != "[default]") { if (botId != "[default]") {

View file

@ -1,4 +1,4 @@
import { IGBPackage } from 'botlib'; import { IGBPackage } from "botlib";
/*****************************************************************************\ /*****************************************************************************\
| ( )_ _ | | ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ | | _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
@ -40,12 +40,12 @@ const Fs = require("fs");
const WaitUntil = require("wait-until"); const WaitUntil = require("wait-until");
const express = require("express"); const express = require("express");
import { KBService } from './../../kb.gbapp/services/KBService'; import { KBService } from "./../../kb.gbapp/services/KBService";
import { GBImporter } from "./GBImporter"; import { GBImporter } from "./GBImporter";
import { IGBCoreService, IGBInstance } from "botlib"; import { IGBCoreService, IGBInstance } from "botlib";
import { GBConfigService } from "./GBConfigService"; import { GBConfigService } from "./GBConfigService";
import { GBError } from "botlib"; import { GBError } from "botlib";
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. */
export class GBDeployer { export class GBDeployer {
@ -62,13 +62,16 @@ export class GBDeployer {
this.importer = importer; this.importer = importer;
} }
/** /**
* *
* Performs package deployment in all .gbai or default. * Performs package deployment in all .gbai or default.
* *
* */ * */
public deployPackages(core: IGBCoreService, server: any, appPackages: Array<IGBPackage>) { public deployPackages(
core: IGBCoreService,
server: any,
appPackages: Array<IGBPackage>
) {
let _this = this; let _this = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
@ -83,31 +86,31 @@ export class GBDeployer {
let generalPackages = new Array<string>(); let generalPackages = new Array<string>();
function doIt(path) { function doIt(path) {
const isDirectory = source => Fs.lstatSync(source).isDirectory() const isDirectory = source => Fs.lstatSync(source).isDirectory();
const getDirectories = source => const getDirectories = source =>
Fs.readdirSync(source).map(name => Path.join(source, name)).filter(isDirectory) Fs.readdirSync(source)
.map(name => Path.join(source, name))
.filter(isDirectory);
let dirs = getDirectories(path); let dirs = getDirectories(path);
dirs.forEach(element => { dirs.forEach(element => {
if (element.startsWith('.')) { if (element.startsWith(".")) {
logger.info(`Ignoring ${element}...`); logger.info(`Ignoring ${element}...`);
} } else {
else { if (element.endsWith(".gbot")) {
if (element.endsWith('.gbot')) {
botPackages.push(element); botPackages.push(element);
} } else if (element.endsWith(".gbapp")) {
else if (element.endsWith('.gbapp')) {
gbappPackages.push(element); gbappPackages.push(element);
} } else {
else {
generalPackages.push(element); generalPackages.push(element);
} }
} }
}); });
} }
logger.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`); 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);
@ -121,14 +124,16 @@ export class GBDeployer {
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 => {
let p = new m.Package(); let p = new m.Package();
p.loadPackage(core, core.sequelize); p.loadPackage(core, core.sequelize);
appPackages.push(p); appPackages.push(p);
logger.info(`App (.gbapp) deployed: ${e}.`); logger.info(`App (.gbapp) deployed: ${e}.`);
appPackagesProcessed++; appPackagesProcessed++;
}).catch(err => { })
.catch(err => {
logger.info(`Error deploying App (.gbapp): ${e}: ${err}`); logger.info(`Error deploying App (.gbapp): ${e}: ${err}`);
appPackagesProcessed++; appPackagesProcessed++;
}); });
@ -137,7 +142,6 @@ export class GBDeployer {
} }
}); });
WaitUntil() WaitUntil()
.interval(1000) .interval(1000)
.times(10) .times(10)
@ -161,39 +165,39 @@ export class GBDeployer {
/** Then all remaining generalPackages are loaded. */ /** Then all remaining generalPackages are loaded. */
generalPackages.forEach(filename => { generalPackages.forEach(filename => {
let filenameOnly = Path.basename(filename); let filenameOnly = Path.basename(filename);
logger.info(`Deploying package: ${filename}...`); logger.info(`Deploying package: ${filename}...`);
/** Handles apps for general bots - .gbapp must stay out of deploy folder. */ /** Handles apps for general bots - .gbapp must stay out of deploy folder. */
if (Path.extname(filename) === ".gbapp" || Path.extname(filename) === ".gblib") { if (
Path.extname(filename) === ".gbapp" ||
Path.extname(filename) === ".gblib"
) {
/** Themes for bots. */ /** Themes for bots. */
} else if (Path.extname(filename) === ".gbtheme") { } else if (Path.extname(filename) === ".gbtheme") {
server.use("/themes/" + filenameOnly, express.static(filename)); server.use("/themes/" + filenameOnly, express.static(filename));
logger.info(`Theme (.gbtheme) assets accessible at: ${"/themes/" + filenameOnly}.`); logger.info(
`Theme (.gbtheme) assets accessible at: ${"/themes/" +
filenameOnly}.`
);
/** Knowledge base for bots. */ /** Knowledge base for bots. */
} else if (Path.extname(filename) === ".gbkb") { } else if (Path.extname(filename) === ".gbkb") {
server.use( server.use(
"/kb/" + filenameOnly + "/subjects", "/kb/" + filenameOnly + "/subjects",
express.static(UrlJoin(filename, "subjects")) express.static(UrlJoin(filename, "subjects"))
); );
logger.info(`KB (.gbkb) assets accessible at: ${"/kb/" + filenameOnly}.`); logger.info(
} `KB (.gbkb) assets accessible at: ${"/kb/" + filenameOnly}.`
);
else if (Path.extname(filename) === ".gbui" || filename.endsWith(".git")) { } else if (
Path.extname(filename) === ".gbui" ||
filename.endsWith(".git")
) {
// Already Handled // Already Handled
} } else {
/** Unknown package format. */ /** Unknown package format. */
else {
let err = new Error(`Package type not handled: ${filename}.`); let err = new Error(`Package type not handled: ${filename}.`);
reject(err); reject(err);
} }
@ -205,22 +209,22 @@ export class GBDeployer {
.times(5) .times(5)
.condition(function(cb) { .condition(function(cb) {
logger.info(`Waiting for package deployment...`); logger.info(`Waiting for package deployment...`);
cb(totalPackages == (generalPackages.length)); cb(totalPackages == generalPackages.length);
}) })
.done(function(result) { .done(function(result) {
if (botPackages.length === 0) { if (botPackages.length === 0) {
logger.info(`The bot server is running empty: No bot instances have been found, at least one .gbot file must be deployed.`); logger.info(
} "The server is running with no bot instances, at least one .gbot file must be deployed."
else { );
} else {
logger.info(`Package deployment done.`); logger.info(`Package deployment done.`);
} }
resolve(); resolve();
}); });
}); });
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
reject(err) reject(err);
} }
}); });
} }
@ -241,19 +245,18 @@ export class GBDeployer {
async deployPackageToStorage( async deployPackageToStorage(
instanceId: number, instanceId: number,
packageName: string): Promise<GuaribasPackage> { packageName: string
): Promise<GuaribasPackage> {
return GuaribasPackage.create({ return GuaribasPackage.create({
packageName: packageName, packageName: packageName,
instanceId: instanceId instanceId: instanceId
}); });
} }
deployTheme(localPath: string) { 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(() => {
// }) // })
// .catch(err => { // .catch(err => {
// var gberr = GBError.create( // var gberr = GBError.create(
@ -263,7 +266,6 @@ export class GBDeployer {
} }
async deployPackageFromLocalPath(localPath: string) { async deployPackageFromLocalPath(localPath: string) {
let packageType = Path.extname(localPath); let packageType = Path.extname(localPath);
switch (packageType) { switch (packageType) {
@ -279,7 +281,6 @@ export class GBDeployer {
return service.deployKb(this.core, this, localPath); return service.deployKb(this.core, this, localPath);
case ".gbui": case ".gbui":
break; break;
default: default:
@ -291,11 +292,7 @@ export class GBDeployer {
} }
} }
async undeployPackageFromLocalPath( async undeployPackageFromLocalPath(instance: IGBInstance, localPath: string) {
instance: IGBInstance,
localPath: string
) {
let packageType = Path.extname(localPath); let packageType = Path.extname(localPath);
let packageName = Path.basename(localPath); let packageName = Path.basename(localPath);
@ -315,7 +312,6 @@ export class GBDeployer {
return service.undeployKbFromStorage(instance, p.packageId); return service.undeployKbFromStorage(instance, p.packageId);
case ".gbui": case ".gbui":
break; break;
default: default:
@ -327,8 +323,10 @@ export class GBDeployer {
} }
} }
async getPackageByName(instanceId: number, packageName: string): async getPackageByName(
Promise<GuaribasPackage> { instanceId: number,
packageName: string
): Promise<GuaribasPackage> {
var where = { packageName: packageName, instanceId: instanceId }; var where = { packageName: packageName, instanceId: instanceId };
return GuaribasPackage.findOne({ return GuaribasPackage.findOne({
where: where where: where
@ -341,7 +339,6 @@ export class GBDeployer {
* *
*/ */
async scanBootPackage() { async scanBootPackage() {
const deployFolder = "deploy"; const deployFolder = "deploy";
let bootPackage = GBConfigService.get("BOOT_PACKAGE"); let bootPackage = GBConfigService.get("BOOT_PACKAGE");

View file

@ -32,42 +32,44 @@
"use strict"; "use strict";
const { TextPrompt } = require("botbuilder-dialogs"); const { TextPrompt } = require("botbuilder-dialogs");
const UrlJoin = require("url-join"); const UrlJoin = require("url-join");
const express = require("express"); const express = require("express");
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
import { BotFrameworkAdapter, BotStateSet, ConversationState, MemoryStorage, UserState } from "botbuilder"; import {
import { LanguageTranslator, LocaleConverter } from "botbuilder-ai"; BotFrameworkAdapter,
BotStateSet,
ConversationState,
MemoryStorage,
UserState
} from "botbuilder";
import { GBCoreService } from "./GBCoreService"; import { GBCoreService } from "./GBCoreService";
import { GBConversationalService } from "./GBConversationalService"; import { GBConversationalService } from "./GBConversationalService";
import { GBConfigService } from "./GBConfigService";
import * as request from "request-promise-native"; import * as request from "request-promise-native";
import { GBMinInstance, IGBCoreService, IGBInstance, IGBPackage, GBError } from "botlib"; import {
import { GBServiceCallback } from "botlib"; GBMinInstance,
IGBPackage,
} from "botlib";
import { GBAnalyticsPackage } from "../../analytics.gblib"; import { GBAnalyticsPackage } from "../../analytics.gblib";
import { GBCorePackage } from "../../core.gbapp"; import { GBCorePackage } from "../../core.gbapp";
import { GBKBPackage } from '../../kb.gbapp'; import { GBKBPackage } from "../../kb.gbapp";
import { GBDeployer } from './GBDeployer'; import { GBDeployer } from "./GBDeployer";
import { GBSecurityPackage } from '../../security.gblib'; import { GBSecurityPackage } from "../../security.gblib";
import { GBAdminPackage } from './../../admin.gbapp/index'; import { GBAdminPackage } from "./../../admin.gbapp/index";
import { GBCustomerSatisfactionPackage } from "../../customer-satisfaction.gbapp"; import { GBCustomerSatisfactionPackage } from "../../customer-satisfaction.gbapp";
import { GBWhatsappPackage } from "../../whatsapp.gblib"; import { GBWhatsappPackage } from "../../whatsapp.gblib";
/** Minimal service layer for a bot. */ /** Minimal service layer for a bot. */
export class GBMinService { export class GBMinService {
core: GBCoreService; core: GBCoreService;
conversationalService: GBConversationalService; conversationalService: GBConversationalService;
deployer: GBDeployer; deployer: GBDeployer;
corePackage = "core.gbai"; corePackage = "core.gbai";
/** /**
* Static initialization of minimal instance. * Static initialization of minimal instance.
* *
@ -94,8 +96,10 @@ export class GBMinService {
* *
* */ * */
async buildMin(server: any, appPackages: Array<IGBPackage>): Promise<GBMinInstance> { async buildMin(
server: any,
appPackages: Array<IGBPackage>
): Promise<GBMinInstance> {
// Serves default UI on root address '/'. // Serves default UI on root address '/'.
let uiPackage = "default.gbui"; let uiPackage = "default.gbui";
@ -107,8 +111,8 @@ export class GBMinService {
// Loads all bot instances from storage and starting loading them. // Loads all bot instances from storage and starting loading them.
let instances = await this.core.loadInstances(); let instances = await this.core.loadInstances();
Promise.all(instances.map(async instance => { Promise.all(
instances.map(async instance => {
// Gets the authorization key for each instance from Bot Service. // Gets the authorization key for each instance from Bot Service.
let webchatToken = await this.getWebchatToken(instance); let webchatToken = await this.getWebchatToken(instance);
@ -118,13 +122,11 @@ export class GBMinService {
server.get("/instances/:botId", (req, res) => { server.get("/instances/:botId", (req, res) => {
(async () => { (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;
let instance = await this.core.loadInstance(botId); let instance = await this.core.loadInstance(botId);
if (instance) { if (instance) {
let speechToken = await this.getSTSToken(instance); let speechToken = await this.getSTSToken(instance);
res.send( res.send(
@ -142,12 +144,14 @@ export class GBMinService {
res.sendStatus(error); res.sendStatus(error);
logger.error(error); logger.error(error);
} }
})() })();
}); });
// Build bot adapter. // Build bot adapter.
var { min, adapter, conversationState } = await this.buildBotAdapter(instance); var { min, adapter, conversationState } = await this.buildBotAdapter(
instance
);
// Call the loadBot context.activity for all packages. // Call the loadBot context.activity for all packages.
@ -157,10 +161,19 @@ export class GBMinService {
let url = `/api/messages/${instance.botId}`; let url = `/api/messages/${instance.botId}`;
server.post(url, async (req, res) => { server.post(url, async (req, res) => {
return this.receiver(adapter, req, res, conversationState, min, return this.receiver(
instance, appPackages); adapter,
req,
res,
conversationState,
min,
instance,
appPackages
);
}); });
logger.info(`GeneralBots(${instance.engineName}) listening on: ${url}.` ); logger.info(
`GeneralBots(${instance.engineName}) listening on: ${url}.`
);
// Serves individual URL for each bot user interface. // Serves individual URL for each bot user interface.
@ -185,13 +198,11 @@ export class GBMinService {
// } // }
// ); // );
// next(); // next();
})
})); );
} }
private async buildBotAdapter(instance: any) { private async buildBotAdapter(instance: any) {
let adapter = new BotFrameworkAdapter({ let adapter = new BotFrameworkAdapter({
appId: instance.marketplaceId, appId: instance.marketplaceId,
appPassword: instance.marketplacePassword appPassword: instance.marketplacePassword
@ -211,7 +222,7 @@ export class GBMinService {
min.core = this.core; min.core = this.core;
min.conversationalService = this.conversationalService; min.conversationalService = this.conversationalService;
min.instance = await this.core.loadInstance(min.botId); min.instance = await this.core.loadInstance(min.botId);
min.dialogs.add('textPrompt', new TextPrompt()); min.dialogs.add("textPrompt", new TextPrompt());
return { min, adapter, conversationState }; return { min, adapter, conversationState };
} }
@ -219,8 +230,15 @@ export class GBMinService {
private invokeLoadBot(appPackages: any[], min: any, server: any) { private invokeLoadBot(appPackages: any[], min: any, server: any) {
appPackages.forEach(e => { appPackages.forEach(e => {
e.sysPackages = new Array<IGBPackage>(); e.sysPackages = new Array<IGBPackage>();
[GBAdminPackage, GBAnalyticsPackage, GBCorePackage, GBSecurityPackage, [
GBKBPackage, GBCustomerSatisfactionPackage, GBWhatsappPackage].forEach(sysPackage => { GBAdminPackage,
GBAnalyticsPackage,
GBCorePackage,
GBSecurityPackage,
GBKBPackage,
GBCustomerSatisfactionPackage,
GBWhatsappPackage
].forEach(sysPackage => {
logger.info(`Loading sys package: ${sysPackage.name}...`); logger.info(`Loading sys package: ${sysPackage.name}...`);
let p = Object.create(sysPackage.prototype) as IGBPackage; let p = Object.create(sysPackage.prototype) as IGBPackage;
p.loadBot(min); p.loadBot(min);
@ -239,11 +257,16 @@ export class GBMinService {
/** /**
* Bot Service hook method. * Bot Service hook method.
*/ */
private receiver(adapter: BotFrameworkAdapter, req: any, res: any, conversationState private receiver(
: ConversationState, min: any, instance: any, appPackages: any[]) { adapter: BotFrameworkAdapter,
req: any,
return adapter.processActivity(req, res, async (context) => { res: any,
conversationState: ConversationState,
min: any,
instance: any,
appPackages: any[]
) {
return adapter.processActivity(req, res, async context => {
const state = conversationState.get(context); const state = conversationState.get(context);
const dc = min.dialogs.createContext(context, state); const dc = min.dialogs.createContext(context, state);
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context);
@ -252,75 +275,67 @@ export class GBMinService {
instanceId: instance.instanceId, instanceId: instance.instanceId,
botId: instance.botId, botId: instance.botId,
theme: instance.theme, theme: instance.theme,
secret: instance.webchatKey, secret: instance.webchatKey
}); });
user.loaded = true; user.loaded = true;
user.subjects = []; user.subjects = [];
} }
logger.info(`[RCV]: ${context.activity.type}, ChannelID: ${context.activity.channelId}, logger.info(`[RCV]: ${context.activity.type}, ChannelID: ${
context.activity.channelId
},
ConversationID: ${context.activity.conversation.id}, ConversationID: ${context.activity.conversation.id},
Name: ${context.activity.name}, Text: ${context.activity.text}.`); Name: ${context.activity.name}, Text: ${
if (context.activity.type === "conversationUpdate" && context.activity.text
context.activity.membersAdded.length > 0) { }.`);
if (
context.activity.type === "conversationUpdate" &&
context.activity.membersAdded.length > 0
) {
let member = context.activity.membersAdded[0]; let member = context.activity.membersAdded[0];
if (member.name === "GeneralBots") { if (member.name === "GeneralBots") {
logger.info(`Bot added to conversation, starting chat...`); logger.info(`Bot added to conversation, starting chat...`);
appPackages.forEach(e => { appPackages.forEach(e => {
e.onNewSession(min, dc); e.onNewSession(min, dc);
}); });
await dc.begin('/'); await dc.begin("/");
} } else {
else {
logger.info(`Member added to conversation: ${member.name}`); logger.info(`Member added to conversation: ${member.name}`);
} }
} } else if (context.activity.type === "message") {
else if (context.activity.type === 'message') {
// Check to see if anyone replied. If not then start echo dialog // Check to see if anyone replied. If not then start echo dialog
if (context.activity.text === "admin") { if (context.activity.text === "admin") {
await dc.begin("/admin"); await dc.begin("/admin");
} } else {
else {
await dc.continue(); await dc.continue();
} }
} else if (context.activity.type === "event") {
}
else if (context.activity.type === 'event') {
if (context.activity.name === "whoAmI") { if (context.activity.name === "whoAmI") {
await dc.begin("/whoAmI"); await dc.begin("/whoAmI");
} } else if (context.activity.name === "showSubjects") {
else if (context.activity.name === "showSubjects") {
await dc.begin("/menu"); await dc.begin("/menu");
} } else if (context.activity.name === "giveFeedback") {
else if (context.activity.name === "giveFeedback") {
await dc.begin("/feedback", { await dc.begin("/feedback", {
fromMenu: true fromMenu: true
}); });
} } else if (context.activity.name === "showFAQ") {
else if (context.activity.name === "showFAQ") {
await dc.begin("/faq"); await dc.begin("/faq");
} } else if (context.activity.name === "ask") {
else if (context.activity.name === "ask") {
await dc.begin("/answer", { await dc.begin("/answer", {
query: (context.activity as any).data, query: (context.activity as any).data,
fromFaq: true fromFaq: true
}); });
} } else if (context.activity.name === "quality") {
else if (context.activity.name === "quality") {
await dc.begin("/quality", { await dc.begin("/quality", {
// TODO: score: context.activity.data // TODO: score: context.activity.data
}); });
} } else {
else {
await dc.continue(); await dc.continue();
} }
} }
}); });
} }
/** /**
* Get Webchat key from Bot Service. * Get Webchat key from Bot Service.
* *
@ -328,10 +343,8 @@ export class GBMinService {
* *
*/ */
async getWebchatToken(instance: any) { async getWebchatToken(instance: any) {
let options = { let options = {
url: url: "https://directline.botframework.com/v3/directline/tokens/generate",
"https://directline.botframework.com/v3/directline/tokens/generate",
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${instance.webchatKey}` Authorization: `Bearer ${instance.webchatKey}`
@ -355,12 +368,10 @@ export class GBMinService {
* *
*/ */
async getSTSToken(instance: any) { async getSTSToken(instance: any) {
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0 // TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
let options = { let options = {
url: url: "https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken",
"https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken",
method: "POST", method: "POST",
headers: { headers: {
"Ocp-Apim-Subscription-Key": instance.speechKey "Ocp-Apim-Subscription-Key": instance.speechKey

View file

@ -1,45 +0,0 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of Pragmatismo.io. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
"use strict";
const assert = require('assert');
describe('Array', () => {
describe('#indexOf()', () => {
it('should return -1 when the value is not present',()=> {
assert.equal([1,2,3].indexOf(4), -1);
});
});
});

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,14 +30,13 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict"; "use strict"
import { CSService } from '../services/CSService'
import { CSService } from '../services/CSService'; import { AzureText } from "pragmatismo-io-framework"
import { AzureText } from "pragmatismo-io-framework"; import { GBMinInstance } from "botlib"
import { GBMinInstance } from "botlib"; import { IGBDialog } from "botlib"
import { IGBDialog } from "botlib"; import { BotAdapter } from 'botbuilder'
import { BotAdapter } from 'botbuilder';
export class FeedbackDialog extends IGBDialog { export class FeedbackDialog extends IGBDialog {
@ -49,7 +48,7 @@ export class FeedbackDialog extends IGBDialog {
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService(); const service = new CSService()
min.dialogs.add("/feedbackNumber", [ min.dialogs.add("/feedbackNumber", [
async (dc) => { async (dc) => {
@ -57,17 +56,17 @@ export class FeedbackDialog extends IGBDialog {
"O que achou do meu atendimento, de 1 a 5?", "O que achou do meu atendimento, de 1 a 5?",
"Qual a nota do meu atendimento?", "Qual a nota do meu atendimento?",
"Como define meu atendimento numa escala de 1 a 5?" "Como define meu atendimento numa escala de 1 a 5?"
]; ]
await dc.prompt('choicePrompt', messages[0], ['1', '2', '3', '4', ' 5']); await dc.prompt('choicePrompt', messages[0], ['1', '2', '3', '4', ' 5'])
}, },
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)
await service.updateConversationRate(user.conversation, rate); await service.updateConversationRate(user.conversation, rate)
let messages = ["Obrigado!", "Obrigado por responder."]; let messages = ["Obrigado!", "Obrigado por responder."]
await dc.context.sendActivity(messages[0]); // TODO: Handle rnd. await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
} }
]); ])
min.dialogs.add("/feedback", [ min.dialogs.add("/feedback", [
async (dc, args) => { async (dc, args) => {
@ -75,27 +74,30 @@ export class FeedbackDialog extends IGBDialog {
let messages = [ let messages = [
"Sugestões melhoram muito minha qualidade...", "Sugestões melhoram muito minha qualidade...",
"Obrigado pela sua iniciativa de sugestão." "Obrigado pela sua iniciativa de sugestão."
]; ]
await dc.context.sendActivity(messages[0]); // TODO: Handle rnd. await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
} }
let messages = [ let messages = [
"O que achou do meu atendimento?", "O que achou do meu atendimento?",
"Como foi meu atendimento?", "Como foi meu atendimento?",
"Gostaria de dizer algo sobre meu atendimento?" "Gostaria de dizer algo sobre meu atendimento?"
]; ]
await dc.prompt('textPrompt', messages[0]); await dc.prompt('textPrompt', messages[0])
}, },
async (dc, value) => { async (dc, value) => {
let rate = await AzureText.getSentiment(min.instance.textAnalyticsKey, "pt-br", value); let rate = await AzureText.getSentiment(min.instance.textAnalyticsKey,
min.instance.textAnalyticsServerUrl,
min.conversationalService.getCurrentLanguage(dc), value)
if (rate > 0) { if (rate > 0) {
await dc.context.sendActivity("Bom saber que você gostou. Conte comigo."); await dc.context.sendActivity("Bom saber que você gostou. Conte comigo.")
} else { } else {
await dc.context.sendActivity( await dc.context.sendActivity(
"Vamos registrar sua questão, obrigado pela sinceridade." "Vamos registrar sua questão, obrigado pela sinceridade."
); )
} }
await dc.replace('/ask', { isReturning: true }); await dc.replace('/ask', { isReturning: true })
}]); }])
} }
} }

View file

@ -35,47 +35,33 @@ import { GuaribasConversation } from '../../analytics.gblib/models';
export class CSService { export class CSService {
resolveQuestionAlternate( async resolveQuestionAlternate(
instanceId: number, instanceId: number,
questionTyped: string): Promise<GuaribasQuestionAlternate> { questionTyped: string): Promise<GuaribasQuestionAlternate> {
return new Promise<GuaribasQuestionAlternate>(
(resolve, reject) => { return GuaribasQuestionAlternate.findOne({
GuaribasQuestionAlternate.findOne({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
questionTyped: questionTyped questionTyped: questionTyped
} }
}).then((value: GuaribasQuestionAlternate) => { })
resolve(value);
}).error(reason => reject(reason));
});
} }
insertQuestionAlternate( async insertQuestionAlternate(
instanceId: number, instanceId: number,
questionTyped: string, questionTyped: string,
questionText: string): Promise<GuaribasQuestionAlternate> { questionText: string): Promise<GuaribasQuestionAlternate> {
return new Promise<GuaribasQuestionAlternate>( return GuaribasQuestionAlternate.create({
(resolve, reject) => {
GuaribasQuestionAlternate.create({
questionTyped: questionTyped, questionTyped: questionTyped,
questionText: questionText questionText: questionText
}).then(item => { })
resolve(item);
}).error(reason => reject(reason));
});
} }
updateConversationRate( async updateConversationRate(
conversation: GuaribasConversation, conversation: GuaribasConversation,
rate: number rate: number
): Promise<GuaribasConversation> { ): Promise<GuaribasConversation> {
return new Promise<GuaribasConversation>(
(resolve, reject) => {
conversation.rate = rate; conversation.rate = rate;
conversation.save().then((value: GuaribasConversation) => { return conversation.save()
resolve(conversation);
}).error(reason => reject(reason));
});
} }
} }

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,16 +30,16 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict"; "use strict"
import { IGBDialog } from "botlib"; import { IGBDialog } from "botlib"
import { AzureText } from "pragmatismo-io-framework"; import { AzureText } from "pragmatismo-io-framework"
import { GBMinInstance } from "botlib"; import { GBMinInstance } from "botlib"
import { KBService } from './../services/KBService'; import { KBService } from './../services/KBService'
import { BotAdapter } from "botbuilder"; import { BotAdapter } from "botbuilder"
import { LuisRecognizer } from "botbuilder-ai"; 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 {
/** /**
@ -50,42 +50,67 @@ export class AskDialog extends IGBDialog {
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(min.core.sequelize); const service = new KBService(min.core.sequelize)
const model = new LuisRecognizer({ const model = new LuisRecognizer({
appId: min.instance.nlpAppId, appId: min.instance.nlpAppId,
subscriptionKey: min.instance.nlpSubscriptionKey, subscriptionKey: min.instance.nlpSubscriptionKey,
serviceEndpoint: min.instance.nlpServerUrl serviceEndpoint: min.instance.nlpServerUrl
}); })
min.dialogs.add("/answer", [ min.dialogs.add("/answer", [
async (dc, args) => { async (dc, args) => {
// Initialize values. // Initialize values.
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context)
let text = args.query; let text = args.query
if (!text) { if (!text) {
throw new Error(`/answer being called with no args.query text.`) throw new Error(`/answer being called with no args.query text.`)
} }
let locale = await AzureText.getLocale(min.instance.textAnalyticsKey,
min.instance.textAnalyticsServerUrl, text)
if (locale != dc.context.activity.locale.split("-")[0])
{
switch(locale)
{
case "pt":
await dc.context.sendActivity("OK, mundando de idioma para o Português...");
dc.context.activity.locale = "pt-BR";
break;
case "en":
await dc.context.sendActivity("OK, changing language to English...");
dc.context.activity.locale = "en-US";
break;
default:
await dc.context.sendActivity(`Unknown language: ${locale}`);
break;
}
}
// Stops any content on projector. // Stops any content on projector.
await min.conversationalService.sendEvent(dc, "stop", null); await min.conversationalService.sendEvent(dc, "stop", null)
// Handle extra text from FAQ. // Handle extra text from FAQ.
if (args && args.query) { if (args && args.query) {
text = args.query; text = args.query
} else if (args && args.fromFaq) { } else if (args && args.fromFaq) {
let messages = [ let messages = [
`Ótima escolha, procurando resposta para sua questão...`, `Ótima escolha, procurando resposta para sua questão...`,
`Pesquisando sobre o termo...`, `Pesquisando sobre o termo...`,
`Aguarde, por favor, enquanto acho sua resposta...` `Aguarde, por favor, enquanto acho sua resposta...`
]; ]
await dc.context.sendActivity(messages[0]); // TODO: Handle rnd. await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
} }
// Spells check the input text before sending Search or NLP. // Spells check the input text before sending Search or NLP.
@ -93,23 +118,22 @@ export class AskDialog extends IGBDialog {
if (min.instance.spellcheckerKey) { if (min.instance.spellcheckerKey) {
let data = await AzureText.getSpelledText( let data = await AzureText.getSpelledText(
min.instance.spellcheckerKey, min.instance.spellcheckerKey,
text); text)
if (data != text) { if (data != text) {
logger.info(`Spelling corrected: ${data}`); logger.info(`Spelling corrected: ${data}`)
text = data; text = data
} }
} }
// Searches KB for the first time. // Searches KB for the first time.
user.lastQuestion = text; user.lastQuestion = text
let resultsA = await service.ask( let resultsA = await service.ask(
min.instance, min.instance,
text, text,
min.instance.searchScore, min.instance.searchScore,
user.subjects); user.subjects)
// If there is some result, answer immediately. // If there is some result, answer immediately.
@ -117,23 +141,23 @@ export class AskDialog extends IGBDialog {
// Saves some context info. // Saves some context info.
user.isAsking = false; user.isAsking = false
user.lastQuestionId = resultsA.questionId; user.lastQuestionId = resultsA.questionId
// Sends the answer to all outputs, including projector. // Sends the answer to all outputs, including projector.
await service.sendAnswer(min.conversationalService, dc, resultsA.answer); await service.sendAnswer(min.conversationalService, dc, resultsA.answer)
// Goes to ask loop, again. // Goes to ask loop, again.
await dc.replace("/ask", { isReturning: true }); await dc.replace("/ask", { isReturning: true })
} else { } else {
// Second time running Search, now with no filter. // Second time running Search, now with no filter.
let resultsB = await service.ask(min.instance, text, let resultsB = await service.ask(min.instance, text,
min.instance.searchScore, null); min.instance.searchScore, null)
// If there is some result, answer immediately. // If there is some result, answer immediately.
@ -141,9 +165,9 @@ export class AskDialog extends IGBDialog {
// Saves some context info. // Saves some context info.
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context)
user.isAsking = false; user.isAsking = false
user.lastQuestionId = resultsB.questionId; user.lastQuestionId = resultsB.questionId
// Informs user that a broader search will be used. // Informs user that a broader search will be used.
@ -151,53 +175,53 @@ export class AskDialog extends IGBDialog {
let subjectText = let subjectText =
`${KBService.getSubjectItemsSeparatedBySpaces( `${KBService.getSubjectItemsSeparatedBySpaces(
user.subjects user.subjects
)}`; )}`
let messages = [ let messages = [
`Respondendo nao apenas sobre ${subjectText}... `, `Respondendo nao apenas sobre ${subjectText}... `,
`Respondendo de modo mais abrangente...`, `Respondendo de modo mais abrangente...`,
`Vou te responder de modo mais abrangente... `Vou te responder de modo mais abrangente...
Não apenas sobre ${subjectText}` Não apenas sobre ${subjectText}`
]; ]
await dc.context.sendActivity(messages[0]); // TODO: Handle rnd. await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
} }
// Sends the answer to all outputs, including projector. // Sends the answer to all outputs, including projector.
await service.sendAnswer(min.conversationalService, dc, resultsB.answer); await service.sendAnswer(min.conversationalService, dc, resultsB.answer)
await dc.replace("/ask", { isReturning: true }); await dc.replace("/ask", { isReturning: true })
} else { } else {
let data = await min.conversationalService.runNLP(dc, min, text); let data = await min.conversationalService.runNLP(dc, min, text)
if (!data) { if (!data) {
let messages = [ let messages = [
"Desculpe-me, não encontrei nada a respeito.", "Desculpe-me, não encontrei nada a respeito.",
"Lamento... Não encontrei nada sobre isso. Vamos tentar novamente?", "Lamento... Não encontrei nada sobre isso. Vamos tentar novamente?",
"Desculpe-me, não achei nada parecido. Poderia tentar escrever de outra forma?" "Desculpe-me, não achei nada parecido. Poderia tentar escrever de outra forma?"
]; ]
await dc.context.sendActivity(messages[0]); // TODO: Handle rnd. await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
await dc.replace("/ask", { isReturning: true }); await dc.replace("/ask", { isReturning: true })
} }
} }
} }
} }
]); ])
min.dialogs.add("/ask", [ min.dialogs.add("/ask", [
async (dc, args) => { async (dc, args) => {
const user = min.userState.get(dc.context); const user = min.userState.get(dc.context)
user.isAsking = true; user.isAsking = true
if (!user.subjects) { if (!user.subjects) {
user.subjects = []; user.subjects = []
} }
let text = []; let text = []
if (user.subjects.length > 0) { if (user.subjects.length > 0) {
text = [ text = [
`Faça sua pergunta...`, `Faça sua pergunta...`,
`Pode perguntar sobre o assunto em questão... `, `Pode perguntar sobre o assunto em questão... `,
`Qual a pergunta?` `Qual a pergunta?`
]; ]
} }
if (args && args.isReturning) { if (args && args.isReturning) {
@ -205,16 +229,16 @@ export class AskDialog extends IGBDialog {
"Sobre o que mais posso ajudar?", "Sobre o que mais posso ajudar?",
"Então, posso ajudar em algo a mais?", "Então, posso ajudar em algo a mais?",
"Deseja fazer outra pergunta?" "Deseja fazer outra pergunta?"
]; ]
} }
if (text.length > 0) { if (text.length > 0) {
await dc.prompt('textPrompt', text[0]); await dc.prompt('textPrompt', text[0])
} }
}, },
async (dc, value) => { async (dc, value) => {
await dc.endAll(); await dc.endAll()
await dc.begin("/answer", { query: value }); await dc.begin("/answer", { query: value })
} }
]); ])
} }
} }

View file

@ -155,13 +155,21 @@ export class KBService {
let value = await this.getAnswerById( let value = await this.getAnswerById(
instance.instanceId, instance.instanceId,
results[0].answerId) results[0].answerId)
if (value) {
return Promise.resolve({ answer: value, questionId: results[0].questionId }) return Promise.resolve({ answer: value, questionId: results[0].questionId })
} }
else {
return Promise.resolve({ answer: null, questionId: 0 })
}
}
} else { } else {
let data = await this.getAnswerByText(instance.instanceId, query) let data = await this.getAnswerByText(instance.instanceId, query)
if (data) {
return Promise.resolve( return Promise.resolve(
{ answer: data.answer, questionId: data.question.questionId } { answer: data.answer, questionId: data.question.questionId })
) } else {
return Promise.resolve({ answer: null, questionId: 0 })
}
} }
} }
catch (reason) { catch (reason) {

37
tslint.json Normal file
View file

@ -0,0 +1,37 @@
{
"defaultSeverity": "warning",
"extends": [
"tslint:recommended",
"tslint-microsoft-contrib"
],
"linterOptions": {
"exclude":[
"libraries/botframework-connector/src/generated/**/*",
"libraries/botframework-schema/**/*"
]
},
"rulesDirectory": [
"node_modules/tslint-microsoft-contrib"
],
"jsRules": {},
"rules": {
"variable-name": false,
"no-parameter-properties": false,
"no-reserved-keywords": false,
"no-unnecessary-class":false,
"function-name": false,
"no-redundant-jsdoc": false,
"no-return-await": false,
"prefer-type-cast": false,
"no-object-literal-type-assertion":false,
"no-increment-decrement":false,
"no-any":false,
"interface-name":false,
"no-this-assignment":false,
"switch-final-break":false,
"no-parameter-reassignment":false,
"export-name":false,
"no-relative-imports": false,
"max-line-length": [true,{"limit":140,"ignore-pattern":"^\\s+\\*"}]
}
}