fix(config): TSLint parsed on all files.

new(config): Several CI integrations.
This commit is contained in:
Rodrigo Rodriguez (pragmatismo.io) 2018-11-12 12:20:44 -02:00
parent 6f868c5178
commit ea978f7d65
47 changed files with 11509 additions and 1839 deletions

View file

@ -6,4 +6,4 @@ tmp
.env
.coveralls.yml
coverage
{YOUR_LIB}-*.tgz
BotServer-*.tgz

View file

@ -7,7 +7,7 @@ notifications:
email: false
before_install:
- npm install --global nyc mocha typescript rimraf shx
- npm install --global nyc mocha typescript shx
install:
- npm install
@ -17,7 +17,6 @@ before_script:
script:
#- npm run build-docs
#- npm run tslint
#- npm run coveralls
branches:
@ -29,10 +28,17 @@ after_success:
- npm pack
deploy:
- provider: pages
skip_cleanup: true
local_dir: docs/reference
github_token: $GITHUB_TOKEN
on:
tags: false
- provider: releases
api_key: $GITHUB_TOKEN
file_glob: true
file: "{YOURLIB}-*.tgz"
file: "BotServer-*.tgz"
skip_cleanup: true
on:
tags: false

9654
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,15 +18,29 @@
"url": "https://github.com/pragmatismo-io/BotServer.git"
},
"scripts": {
"clean": "rimraf dist",
"tslint": "tslint ./src ./packages/**/*.ts -t verbose",
"clean": "shx rm -rf node_modules/ dist/ docs/reference",
"tslint": "tslint --fix ./src/*.ts ./packages/**/*.ts -t verbose",
"build": "tsc",
"build-docs": "typedoc --options typedoc.json src/",
"test": "nyc --reporter=html --reporter=text mocha -r ts-node/register packages/**/*.test.ts ",
"pretest": "npm run build",
"coveralls": "npm run test && nyc report --reporter=text-lcov | coveralls",
"start": "node ./dist/src/app.js",
"watch:build": "tsc --watch",
"watch:server": "nodemon './dist/index.js' --watch './dist'"
"watch:server": "nodemon './dist/index.js' --watch './dist'",
"posttypedoc": "shx cp .nojekyll docs/reference/.nojekyll",
"ban": "ban",
"issues": "git-issues",
"license": "license-checker --production --onlyunknown --csv",
"lint": "standard --verbose --fix 'src/*.js' 'cypress/**/*.js'",
"prelint": "npm run pretty",
"pretty": "prettier-standard 'src/*.ts' 'packages/**/*.ts'",
"secure": "nsp check",
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
"unused-deps": "dependency-check --unused --no-dev ./package.json",
"travis-deploy-once": "travis-deploy-once",
"semantic-release": "semantic-release",
"commit": "commit-wizard"
},
"engines": {
"node": "=10.13.0"
@ -81,7 +95,7 @@
"reflect-metadata": "0.1.12",
"request-promise-native": "1.0.5",
"scanf": "^1.0.2",
"sequelize": "4.41.1",
"sequelize": "4.41.2",
"sequelize-typescript": "0.6.6",
"simple-git": "^1.107.0",
"sqlite3": "4.0.4",
@ -99,14 +113,25 @@
"winston": "3.1.0"
},
"devDependencies": {
"@types/mocha": "^2.2.47",
"@types/mocha": "^5.2.5",
"chai": "^4.2.0",
"coveralls": "^3.0.2",
"mocha": "^5.2.0",
"shx": "^0.3.2",
"ts-loader": "^5.3.0",
"tslint": "^5.11.0",
"tslint-microsoft-contrib": "^5.2.1"
"tslint-microsoft-contrib": "^5.2.1",
"ban-sensitive-files": "1.9.2",
"dependency-check": "3.2.1",
"deps-ok": "1.4.1",
"git-issues": "1.3.1",
"license-checker": "24.0.1",
"nsp": "3.2.1",
"pre-git": "3.17.1",
"prettier-standard": "8.0.1",
"standard": "12.0.1",
"travis-deploy-once": "5.0.9",
"semantic-release": "15.10.8"
},
"eslintConfig": {
"env": {
@ -152,5 +177,18 @@
"warn"
]
}
},
"release": {
"analyzeCommits": "simple-commit-message"
},
"config": {
"pre-git": {
"commit-msg": "simple",
"pre-commit": [],
"pre-push": [],
"post-commit": [],
"post-checkout": [],
"post-merge": []
}
}
}

View file

@ -36,36 +36,36 @@
'use strict';
const UrlJoin = require("url-join");
import { GBMinInstance } from "botlib";
import { IGBDialog } from "botlib";
import { GBDeployer } from "../../core.gbapp/services/GBDeployer";
import { GBImporter } from "../../core.gbapp/services/GBImporter";
import { GBConfigService } from "../../core.gbapp/services/GBConfigService";
import { BotAdapter } from "botbuilder";
import { GBAdminService } from "../services/GBAdminService";
import { Messages } from "../strings";
import { WaterfallDialog } from "botbuilder-dialogs";
const UrlJoin = require('url-join');
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance } from 'botlib';
import { IGBDialog } from 'botlib';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
import { GBImporter } from '../../core.gbapp/services/GBImporter';
import { GBAdminService } from '../services/GBAdminService';
import { Messages } from '../strings';
/**
* Dialogs for administration tasks.
*/
export class AdminDialog extends IGBDialog {
static async createFarmCommand(text: any, min: GBMinInstance) {}
public static async createFarmCommand(text: any, min: GBMinInstance) {}
static async undeployPackageCommand(text: any, min: GBMinInstance) {
let packageName = text.split(" ")[1];
let importer = new GBImporter(min.core);
let deployer = new GBDeployer(min.core, importer);
public static async undeployPackageCommand(text: any, min: GBMinInstance) {
const packageName = text.split(' ')[1];
const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer);
await deployer.undeployPackageFromLocalPath(
min.instance,
UrlJoin("packages", packageName)
UrlJoin('packages', packageName)
);
}
static async deployPackageCommand(text: string, deployer: GBDeployer) {
let packageName = text.split(" ")[1];
let additionalPath = GBConfigService.get("ADDITIONAL_DEPLOY_PATH");
public static async deployPackageCommand(text: string, deployer: GBDeployer) {
const packageName = text.split(' ')[1];
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
await deployer.deployPackageFromLocalPath(
UrlJoin(additionalPath, packageName)
);
@ -76,59 +76,59 @@ export class AdminDialog extends IGBDialog {
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
public static setup(bot: BotAdapter, min: GBMinInstance) {
// Setup services.
let importer = new GBImporter(min.core);
let deployer = new GBDeployer(min.core, importer);
const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer);
min.dialogs.add(
new WaterfallDialog("/admin", [
new WaterfallDialog('/admin', [
async step => {
const locale = step.context.activity.locale;
const prompt = Messages[locale].authenticate;
await step.prompt("textPrompt", prompt);
await step.prompt('textPrompt', prompt);
return await step.next();
},
async step => {
const locale = step.context.activity.locale;
let password = step.result;
const password = step.result;
if (
password === GBConfigService.get("ADMIN_PASS") &&
password === GBConfigService.get('ADMIN_PASS') &&
GBAdminService.StrongRegex.test(password)
) {
await step.context.sendActivity(Messages[locale].welcome);
await step.prompt("textPrompt", Messages[locale].which_task);
await step.prompt('textPrompt', Messages[locale].which_task);
} else {
await step.prompt("textPrompt", Messages[locale].wrong_password);
await step.prompt('textPrompt', Messages[locale].wrong_password);
await step.endDialog();
}
return await step.next();
},
async step => {
const locale = step.context.activity.locale;
var text = step.result;
let cmdName = text.split(" ")[0];
const text = step.result;
const cmdName = text.split(' ')[0];
step.context.sendActivity(Messages[locale].working(cmdName));
let unknownCommand = false;
if (text === "quit") {
await step.replaceDialog("/");
} else if (cmdName === "createFarm") {
if (text === 'quit') {
await step.replaceDialog('/');
} else if (cmdName === 'createFarm') {
await AdminDialog.createFarmCommand(text, deployer);
await step.replaceDialog("/admin", { firstRun: false });
} else if (cmdName === "deployPackage") {
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'deployPackage') {
await AdminDialog.deployPackageCommand(text, deployer);
await step.replaceDialog("/admin", { firstRun: false });
} else if (cmdName === "redeployPackage") {
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'redeployPackage') {
await AdminDialog.undeployPackageCommand(text, min);
await AdminDialog.deployPackageCommand(text, deployer);
await step.replaceDialog("/admin", { firstRun: false });
} else if (cmdName === "undeployPackage") {
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'undeployPackage') {
await AdminDialog.undeployPackageCommand(text, min);
await step.replaceDialog("/admin", { firstRun: false });
} else if (cmdName === "setupSecurity") {
await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'setupSecurity') {
await AdminDialog.setupSecurity(min, step);
} else {
unknownCommand = true;
@ -142,7 +142,7 @@ export class AdminDialog extends IGBDialog {
);
}
await step.endDialog();
await step.replaceDialog("/answer", { query: text });
await step.replaceDialog('/answer', { query: text });
return await step.next();
}
])
@ -151,15 +151,15 @@ export class AdminDialog extends IGBDialog {
private static async setupSecurity(min: any, step: any) {
const locale = step.activity.locale;
let state = `${min.instance.instanceId}${Math.floor(
const state = `${min.instance.instanceId}${Math.floor(
Math.random() * 1000000000
)}`;
await min.adminService.setValue(
min.instance.instanceId,
"AntiCSRFAttackState",
'AntiCSRFAttackState',
state
);
let url = `https://login.microsoftonline.com/${
const url = `https://login.microsoftonline.com/${
min.instance.authenticatorTenant
}/oauth2/authorize?client_id=${
min.instance.authenticatorClientId

View file

@ -37,32 +37,30 @@
'use strict';
import {
Table,
Column,
Model,
CreatedAt,
UpdatedAt,
} from "sequelize-typescript";
Model,
Table,
UpdatedAt
} from 'sequelize-typescript';
@Table
export class GuaribasAdmin extends Model<GuaribasAdmin>
{
export class GuaribasAdmin extends Model<GuaribasAdmin> {
@Column
instanceId: number;
public instanceId: number;
@Column
key: string;
public key: string;
@Column
value: string;
public value: string;
@Column
@CreatedAt
createdAt: Date;
public createdAt: Date;
@Column
@UpdatedAt
updatedAt: Date;
public updatedAt: Date;
}

View file

@ -36,116 +36,41 @@
'use strict';
import { GuaribasAdmin } from "../models/AdminModel";
import { IGBCoreService } from "botlib";
import { AuthenticationContext, TokenResponse } from "adal-node";
const UrlJoin = require("url-join");
const msRestAzure = require("ms-rest-azure");
const PasswordGenerator = require("strict-password-generator").default;
import { AuthenticationContext, TokenResponse } from 'adal-node';
import { IGBCoreService } from 'botlib';
import { GuaribasAdmin } from '../models/AdminModel';
const UrlJoin = require('url-join');
const msRestAzure = require('ms-rest-azure');
const PasswordGenerator = require('strict-password-generator').default;
export class GBAdminService {
static GB_PROMPT: string = "GeneralBots: "
static generateUuid(): string {
return msRestAzure.generateUuid();
}
static masterBotInstanceId = 0;
public static GB_PROMPT: string = 'GeneralBots: ';
public static masterBotInstanceId = 0;
public static StrongRegex = new RegExp(
"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*+_-])(?=.{8,})"
'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*+_-])(?=.{8,})'
);
core: IGBCoreService;
public core: IGBCoreService;
constructor(core: IGBCoreService) {
this.core = core;
}
public async setValue(
instanceId: number,
key: string,
value: string
): Promise<GuaribasAdmin> {
let options = { where: {} };
options.where = { key: key };
let admin = await GuaribasAdmin.findOne(options);
if (admin == null) {
admin = new GuaribasAdmin();
admin.key = key;
}
admin.value = value;
admin.instanceId = instanceId;
return admin.save();
}
public async getValue(instanceId: number, key: string) {
let options = { where: {} };
options.where = { key: key, instanceId: instanceId };
let obj = await GuaribasAdmin.findOne(options);
return Promise.resolve(obj.value);
}
public async acquireElevatedToken(instanceId): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
let instance = await this.core.loadInstanceById(instanceId);
let expiresOn = new Date(await this.getValue(instanceId, "expiresOn"));
if (expiresOn.getTime() > new Date().getTime()) {
let accessToken = await this.getValue(instanceId, "accessToken");
resolve(accessToken);
} else {
let authorizationUrl = UrlJoin(
instance.authenticatorAuthorityHostUrl,
instance.authenticatorTenant,
"/oauth2/authorize"
);
let refreshToken = await this.getValue(instanceId, "refreshToken");
let resource = "https://graph.microsoft.com";
var authenticationContext = new AuthenticationContext(authorizationUrl);
authenticationContext.acquireTokenWithRefreshToken(
refreshToken,
instance.authenticatorClientId,
instance.authenticatorClientSecret,
resource,
async (err, res) => {
if (err) {
reject(err);
} else {
let token = res as TokenResponse;
await this.setValue(
instanceId,
"accessToken",
token.accessToken
);
await this.setValue(
instanceId,
"refreshToken",
token.refreshToken
);
await this.setValue(
instanceId,
"expiresOn",
token.expiresOn.toString()
);
resolve(token.accessToken);
}
}
);
}
});
public static generateUuid(): string {
return msRestAzure.generateUuid();
}
public static async getADALTokenFromUsername(
username: string,
password: string
) {
let credentials = await GBAdminService.getADALCredentialsFromUsername(
const credentials = await GBAdminService.getADALCredentialsFromUsername(
username,
password
);
let accessToken = credentials.tokenCache._entries[0].accessToken;
const accessToken = credentials.tokenCache._entries[0].accessToken;
return accessToken;
}
@ -153,7 +78,7 @@ export class GBAdminService {
username: string,
password: string
) {
let credentials = await msRestAzure.loginWithUsernamePassword(
const credentials = await msRestAzure.loginWithUsernamePassword(
username,
password
);
@ -171,7 +96,7 @@ export class GBAdminService {
maximumLength: 14
};
let password = passwordGenerator.generatePassword(options);
password = password.replace(/@[=:;\?]/g, "#");
password = password.replace(/@[=:;\?]/g, '#');
return password;
}
@ -185,8 +110,83 @@ export class GBAdminService {
minimumLength: 12,
maximumLength: 14
};
let name = passwordGenerator.generatePassword(options);
const name = passwordGenerator.generatePassword(options);
return name;
}
public async setValue(
instanceId: number,
key: string,
value: string
): Promise<GuaribasAdmin> {
const options = { where: {} };
options.where = { key: key };
let admin = await GuaribasAdmin.findOne(options);
if (admin == null) {
admin = new GuaribasAdmin();
admin.key = key;
}
admin.value = value;
admin.instanceId = instanceId;
return admin.save();
}
public async getValue(instanceId: number, key: string) {
const options = { where: {} };
options.where = { key: key, instanceId: instanceId };
const obj = await GuaribasAdmin.findOne(options);
return Promise.resolve(obj.value);
}
public async acquireElevatedToken(instanceId): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
const instance = await this.core.loadInstanceById(instanceId);
const expiresOn = new Date(await this.getValue(instanceId, 'expiresOn'));
if (expiresOn.getTime() > new Date().getTime()) {
const accessToken = await this.getValue(instanceId, 'accessToken');
resolve(accessToken);
} else {
const authorizationUrl = UrlJoin(
instance.authenticatorAuthorityHostUrl,
instance.authenticatorTenant,
'/oauth2/authorize'
);
const refreshToken = await this.getValue(instanceId, 'refreshToken');
const resource = 'https://graph.microsoft.com';
const authenticationContext = new AuthenticationContext(authorizationUrl);
authenticationContext.acquireTokenWithRefreshToken(
refreshToken,
instance.authenticatorClientId,
instance.authenticatorClientSecret,
resource,
async (err, res) => {
if (err) {
reject(err);
} else {
const token = res as TokenResponse;
await this.setValue(
instanceId,
'accessToken',
token.accessToken
);
await this.setValue(
instanceId,
'refreshToken',
token.refreshToken
);
await this.setValue(
instanceId,
'expiresOn',
token.expiresOn.toString()
);
resolve(token.accessToken);
}
}
);
}
});
}
}

View file

@ -1,10 +1,10 @@
export const Messages = {
"en-US": {
authenticate: "Please, authenticate:",
welcome: "Welcome to Pragmatismo.io GeneralBots Administration.",
which_task: "Which task do you wanna run now?",
working:(command)=> `I'm working on ${command}...`,
finshed_working:"Done.",
'en-US': {
authenticate: 'Please, authenticate:',
welcome: 'Welcome to Pragmatismo.io GeneralBots Administration.',
which_task: 'Which task do you wanna run now?',
working: (command) => `I'm working on ${command}...`,
finshed_working: 'Done.',
unknown_command: text =>
`Well, but ${text} is not a administrative General Bots command, I will try to search for it.`,
hi: text => `Hello, ${text}.`,
@ -12,11 +12,11 @@ export const Messages = {
deployPackage: text => `Deploying package ${text}...`,
redeployPackage: text => `Redeploying package ${text}...`,
packageUndeployed: text => `Package ${text} undeployed...`,
consent: (url)=>`Please, consent access to this app at: [Microsoft Online](${url}).`,
wrong_password: "Sorry, wrong password. Please, try again."
consent: (url) => `Please, consent access to this app at: [Microsoft Online](${url}).`,
wrong_password: 'Sorry, wrong password. Please, try again.'
},
"pt-BR": {
show_video: "Vou te mostrar um vídeo. Por favor, aguarde...",
'pt-BR': {
show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...',
hi: msg => `Oi, ${msg}.`
}
};

View file

@ -34,33 +34,31 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
const UrlJoin = require('url-join');
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { GBMinInstance, IGBPackage, IGBCoreService } from "botlib"
import { Sequelize } from "sequelize-typescript"
import { Sequelize } from 'sequelize-typescript';
export class GBAnalyticsPackage implements IGBPackage {
sysPackages: IGBPackage[] = null
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public sysPackages: IGBPackage[] = null;
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
}
unloadPackage(core: IGBCoreService): void {
public unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
public loadBot(min: GBMinInstance): void {
}
unloadBot(min: GBMinInstance): void {
public unloadBot(min: GBMinInstance): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -34,36 +34,36 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
import {
DataTypes,
DataTypeUUIDv4,
DataTypeDate,
DataTypeDecimal
} from "sequelize"
DataTypeDecimal,
DataTypes,
DataTypeUUIDv4
} from 'sequelize';
import {
Sequelize,
Table,
Column,
Model,
HasMany,
AutoIncrement,
BelongsTo,
BelongsToMany,
Length,
ForeignKey,
Column,
CreatedAt,
UpdatedAt,
DataType,
ForeignKey,
HasMany,
IsUUID,
Length,
Model,
PrimaryKey,
AutoIncrement
} from "sequelize-typescript"
Sequelize,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { GuaribasSubject } from "../../kb.gbapp/models"
import { GuaribasUser } from "../../security.gblib/models"
import { GuaribasChannel, GuaribasInstance } from "../../core.gbapp/models/GBModel"
import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBModel';
import { GuaribasSubject } from '../../kb.gbapp/models';
import { GuaribasUser } from '../../security.gblib/models';
@Table
export class GuaribasConversation extends Model<GuaribasConversation> {
@ -71,40 +71,40 @@ export class GuaribasConversation extends Model<GuaribasConversation> {
@PrimaryKey
@AutoIncrement
@Column
conversationId: number
public conversationId: number;
@ForeignKey(() => GuaribasSubject)
@Column
startSubjectId: number
public startSubjectId: number;
@BelongsTo(() => GuaribasSubject)
startSubject: GuaribasSubject
public startSubject: GuaribasSubject;
@ForeignKey(() => GuaribasChannel)
@Column
channelId: string
public channelId: string;
@Column rateDate: Date
@Column public rateDate: Date;
@Column(DataType.FLOAT)
@Column
rate: number
public rate: number;
@Column
@CreatedAt
createdAt: Date
public createdAt: Date;
@Column text: string
@Column public text: string;
@HasMany(() => GuaribasConversationMessage)
conversationMessage: GuaribasConversationMessage[]
public conversationMessage: GuaribasConversationMessage[];
@ForeignKey(() => GuaribasUser)
@Column
startedByUserId: number
public startedByUserId: number;
@BelongsTo(() => GuaribasUser)
startedBy: GuaribasUser
public startedBy: GuaribasUser;
}
@Table
@ -113,38 +113,38 @@ export class GuaribasConversationMessage extends Model<GuaribasConversationMessa
@PrimaryKey
@AutoIncrement
@Column
conversationMessageId: number
public conversationMessageId: number;
@ForeignKey(() => GuaribasSubject)
@Column
subjectId: number
public subjectId: number;
@Column(DataType.TEXT)
content: string
public content: string;
@Column
@CreatedAt
createdAt: Date
public createdAt: Date;
@Column
@UpdatedAt
updatedAt: Date
public updatedAt: Date;
@ForeignKey(() => GuaribasConversation)
@Column
conversationId: number
public conversationId: number;
@BelongsTo(() => GuaribasConversation)
conversation: GuaribasConversation
public conversation: GuaribasConversation;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number
public instanceId: number;
@ForeignKey(() => GuaribasUser)
@Column
userId: number
public userId: number;
@BelongsTo(() => GuaribasUser)
user: GuaribasUser
public user: GuaribasUser;
}

View file

@ -34,38 +34,38 @@
* @fileoverview General Bots server core.
*/
import { GuaribasUser } from "../../security.gblib/models"
import { GuaribasConversation, GuaribasConversationMessage } from "../models"
import { GuaribasUser } from '../../security.gblib/models';
import { GuaribasConversation, GuaribasConversationMessage } from '../models';
export class AnalyticsService {
async createConversation(
public async createConversation(
user: GuaribasUser
): Promise<GuaribasConversation> {
return new Promise<GuaribasConversation>(
(resolve, reject) => {
let conversation = new GuaribasConversation()
conversation.startedBy = user
conversation.startedByUserId = user.userId
const conversation = new GuaribasConversation();
conversation.startedBy = user;
conversation.startedByUserId = user.userId;
conversation.save().then((value: GuaribasConversation) => {
resolve(value)
})
})
resolve(value);
});
});
}
createMessage(
public createMessage(
conversation: GuaribasConversation,
user: GuaribasUser,
content: string
): Promise<GuaribasConversationMessage> {
return new Promise<GuaribasConversationMessage>(
(resolve, reject) => {
let message = GuaribasConversationMessage.build()
message.conversation = conversation
message.user = user
message.content = content
const message = GuaribasConversationMessage.build();
message.conversation = conversation;
message.user = user;
message.content = content;
message.save().then((value: GuaribasConversationMessage) => {
resolve(value)
})
})
resolve(value);
});
});
}
}

View file

@ -57,13 +57,13 @@ export class BotFarmDialog extends IGBDialog {
'2',
'3',
'4',
'5',
'5'
]);
},
async step => {
const locale = step.context.activity.locale;
await step.context.sendActivity(Messages[locale].thanks);
},
}
]);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
export const Messages = {
"en-US": {
about_suggestions: "Suggestions are welcomed and improve my quality...",
'en-US': {
about_suggestions: 'Suggestions are welcomed and improve my quality...'
},
"pt-BR": {
about_suggestions: "Sugestões melhoram muito minha qualidade...",
'pt-BR': {
about_suggestions: 'Sugestões melhoram muito minha qualidade...'
}
};

View file

@ -34,32 +34,32 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
const UrlJoin = require('url-join');
import { GBMinInstance, IGBPackage, IGBCoreService } from "botlib"
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'
import { ConsoleDirectLine } from "./services/ConsoleDirectLine"
import { Sequelize } from 'sequelize-typescript';
import { ConsoleDirectLine } from './services/ConsoleDirectLine';
export class GBConsolePackage implements IGBPackage {
sysPackages: IGBPackage[] = null
channel: ConsoleDirectLine
public sysPackages: IGBPackage[] = null;
public channel: ConsoleDirectLine;
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
}
unloadPackage(core: IGBCoreService): void {
public unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
this.channel = new ConsoleDirectLine(min.instance.webchatKey)
public loadBot(min: GBMinInstance): void {
this.channel = new ConsoleDirectLine(min.instance.webchatKey);
}
unloadBot(min: GBMinInstance): void {
public unloadBot(min: GBMinInstance): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -30,76 +30,75 @@
| |
\*****************************************************************************/
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 Swagger = require('swagger-client')
const rp = require('request-promise')
import { GBService } from "botlib"
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 Swagger = require('swagger-client');
const rp = require('request-promise');
import { GBService } from 'botlib';
export class ConsoleDirectLine extends GBService {
pollInterval = 1000
directLineSecret = ''
directLineClientName = 'DirectLineClient'
directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json'
public pollInterval = 1000;
public directLineSecret = '';
public directLineClientName = 'DirectLineClient';
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
constructor(directLineSecret) {
super()
this.directLineSecret = directLineSecret
super();
this.directLineSecret = directLineSecret;
// TODO: Migrate to Swagger 3.
let directLineClient = rp(this.directLineSpecUrl)
const directLineClient = rp(this.directLineSpecUrl)
.then(function (spec) {
return new Swagger({
spec: JSON.parse(spec.trim()),
usePromise: true
})
});
})
.then(function (client) {
client.clientAuthorizations.add('AuthorizationBotConnector',
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'))
return client
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'));
return client;
})
.catch(function (err) {
console.error('Error initializing DirectLine client', err)
})
console.error('Error initializing DirectLine client', err);
});
// TODO: Remove *this* issue.
let _this_ = this
directLineClient.then((client)=> {
const _this_ = this;
directLineClient.then((client) => {
client.Conversations.Conversations_StartConversation()
.then(function (response) {
return response.obj.conversationId
return response.obj.conversationId;
})
.then(function (conversationId) {
_this_.sendMessagesFromConsole(client, conversationId)
_this_.pollMessages(client, conversationId)
_this_.sendMessagesFromConsole(client, conversationId);
_this_.pollMessages(client, conversationId);
})
.catch(function (err) {
console.error('Error starting conversation', err)
})
})
console.error('Error starting conversation', err);
});
});
}
sendMessagesFromConsole(client, conversationId) {
let _this_ = this
process.stdin.resume()
var stdin = process.stdin
process.stdout.write('Command> ')
public sendMessagesFromConsole(client, conversationId) {
const _this_ = this;
process.stdin.resume();
const stdin = process.stdin;
process.stdout.write('Command> ');
stdin.addListener('data', function (e) {
var input = e.toString().trim()
const input = e.toString().trim();
if (input) {
// exit
if (input.toLowerCase() === 'exit') {
return process.exit()
return process.exit();
}
client.Conversations.Conversations_PostActivity(
@ -115,80 +114,80 @@ export class ConsoleDirectLine extends GBService {
}
}
}).catch(function (err) {
console.error('Error sending message:', err)
})
console.error('Error sending message:', err);
});
process.stdout.write('Command> ')
process.stdout.write('Command> ');
}
})
});
}
/** TBD: Poll Messages from conversation using DirectLine client */
pollMessages(client, conversationId) {
let _this_ = this
console.log('Starting polling message for conversationId: ' + conversationId)
var watermark = null
public pollMessages(client, conversationId) {
const _this_ = this;
console.log('Starting polling message for conversationId: ' + conversationId);
let watermark = null;
setInterval(function () {
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
.then(function (response) {
watermark = response.obj.watermark // use watermark so subsequent requests skip old messages
return response.obj.activities
watermark = response.obj.watermark; // use watermark so subsequent requests skip old messages
return response.obj.activities;
})
.then(_this_.printMessages, _this_.directLineClientName)
}, this.pollInterval)
.then(_this_.printMessages, _this_.directLineClientName);
}, this.pollInterval);
}
printMessages(activities, directLineClientName) {
public printMessages(activities, directLineClientName) {
if (activities && activities.length) {
// ignore own messages
activities = activities.filter(function (m) { return m.from.id !== directLineClientName })
activities = activities.filter(function (m) { return m.from.id !== directLineClientName; });
if (activities.length) {
// print other messages
activities.forEach(activity => {
console.log(activity.text)
}, this)
console.log(activity.text);
}, this);
process.stdout.write('Command> ')
process.stdout.write('Command> ');
}
}
}
printMessage(activity) {
public printMessage(activity) {
if (activity.text) {
console.log(activity.text)
console.log(activity.text);
}
if (activity.attachments) {
activity.attachments.forEach(function (attachment) {
switch (attachment.contentType) {
case "application/vnd.microsoft.card.hero":
this.renderHeroCard(attachment)
break
case 'application/vnd.microsoft.card.hero':
this.renderHeroCard(attachment);
break;
case "image/png":
console.log('Opening the requested image ' + attachment.contentUrl)
open(attachment.contentUrl)
break
case 'image/png':
console.log('Opening the requested image ' + attachment.contentUrl);
open(attachment.contentUrl);
break;
}
})
});
}
}
renderHeroCard(attachment) {
var width = 70
var contentLine = function (content) {
public renderHeroCard(attachment) {
const width = 70;
const contentLine = function (content) {
return ' '.repeat((width - content.length) / 2) +
content +
' '.repeat((width - content.length) / 2)
}
' '.repeat((width - content.length) / 2);
};
console.log('/' + '*'.repeat(width + 1))
console.log('*' + contentLine(attachment.content.title) + '*')
console.log('*' + ' '.repeat(width) + '*')
console.log('*' + contentLine(attachment.content.text) + '*')
console.log('*'.repeat(width + 1) + '/')
console.log('/' + '*'.repeat(width + 1));
console.log('*' + contentLine(attachment.content.title) + '*');
console.log('*' + ' '.repeat(width) + '*');
console.log('*' + contentLine(attachment.content.text) + '*');
console.log('*'.repeat(width + 1) + '/');
}
}
}

View file

@ -36,11 +36,11 @@
'use strict';
import { IGBDialog } from "botlib";
import { GBMinInstance } from "botlib";
import { BotAdapter } from "botbuilder";
import {WaterfallDialog } from "botbuilder-dialogs";
import { Messages } from "../strings";
import { BotAdapter } from 'botbuilder';
import {WaterfallDialog } from 'botbuilder-dialogs';
import { IGBDialog } from 'botlib';
import { GBMinInstance } from 'botlib';
import { Messages } from '../strings';
export class WelcomeDialog extends IGBDialog {
/**
@ -49,20 +49,20 @@ export class WelcomeDialog extends IGBDialog {
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
public static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(new WaterfallDialog('/', [
async step => {
min.dialogs.add(new WaterfallDialog("/", [
async step => {
const user = await min.userProfile.get(context, {});
const locale = step.context.activity.locale;
if (!user.once) {
user.once = true;
await min.userProfile.set(step.context, user);
var a = new Date();
const a = new Date();
const date = a.getHours();
var msg =
const msg =
date < 12
? Messages[locale].good_morning
: date < 18
@ -70,18 +70,18 @@ export class WelcomeDialog extends IGBDialog {
: Messages[locale].good_night;
await step.context.sendActivity(Messages[locale].hi(msg));
await step.replaceDialog("/ask", { firstTime: true });
await step.replaceDialog('/ask', { firstTime: true });
if (
step.context.activity &&
step.context.activity.type == "message" &&
step.context.activity.text != ""
step.context.activity.type == 'message' &&
step.context.activity.text != ''
) {
await step.replaceDialog("/answer", { query: step.context.activity.text });
await step.replaceDialog('/answer', { query: step.context.activity.text });
}
}
return await step.next();
}
]))
]));
}
}

View file

@ -36,11 +36,11 @@
'use strict';
import { IGBDialog } from "botlib";
import { GBMinInstance } from "botlib";
import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
import { WaterfallDialog } from "botbuilder-dialogs";
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { IGBDialog } from 'botlib';
import { GBMinInstance } from 'botlib';
import { Messages } from '../strings';
export class WhoAmIDialog extends IGBDialog {
/**
@ -49,21 +49,21 @@ export class WhoAmIDialog extends IGBDialog {
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(new WaterfallDialog("/whoAmI", [
public static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(new WaterfallDialog('/whoAmI', [
async step => {
let locale = step.context.activity.locale;
const locale = step.context.activity.locale;
await step.context.sendActivity(`${min.instance.description}`);
if (min.instance.whoAmIVideo) {
await step.context.sendActivity(Messages[locale].show_video);
await min.conversationalService.sendEvent(step, "play", {
playerType: "video",
await min.conversationalService.sendEvent(step, 'play', {
playerType: 'video',
data: min.instance.whoAmIVideo.trim()
});
}
await step.replaceDialog("/ask", { isReturning: true });
await step.replaceDialog('/ask', { isReturning: true });
return await step.next();
}
]));

View file

@ -34,44 +34,44 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
const UrlJoin = require('url-join');
import { GBMinInstance, IGBPackage } from "botlib"
import { GBMinInstance, IGBPackage } from 'botlib';
import { WelcomeDialog } from "./dialogs/WelcomeDialog"
import { WhoAmIDialog } from "./dialogs/WhoAmIDialog"
import { IGBCoreService} from "botlib"
import { Sequelize } from "sequelize-typescript"
import { GuaribasInstance, GuaribasException, GuaribasPackage, GuaribasChannel } from "./models/GBModel"
import { IGBCoreService} from 'botlib';
import { Sequelize } from 'sequelize-typescript';
import { WelcomeDialog } from './dialogs/WelcomeDialog';
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel';
export class GBCorePackage implements IGBPackage {
sysPackages: IGBPackage[] = null
public static CurrentEngineName = "guaribas-1.0.0";
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public static CurrentEngineName = 'guaribas-1.0.0';
public sysPackages: IGBPackage[] = null;
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
core.sequelize.addModels([
GuaribasInstance,
GuaribasPackage,
GuaribasChannel,
GuaribasException,
])
GuaribasException
]);
}
unloadPackage(core: IGBCoreService): void {
public unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
WelcomeDialog.setup(min.bot, min)
WhoAmIDialog.setup(min.bot, min)
public loadBot(min: GBMinInstance): void {
WelcomeDialog.setup(min.bot, min);
WhoAmIDialog.setup(min.bot, min);
}
unloadBot(min: GBMinInstance): void {
public unloadBot(min: GBMinInstance): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -36,21 +36,20 @@
'use strict';
import {
Table,
Column,
Model,
AutoIncrement,
BelongsTo,
ForeignKey,
Column,
CreatedAt,
UpdatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
AutoIncrement
} from "sequelize-typescript";
Table,
UpdatedAt
} from 'sequelize-typescript';
import { IGBInstance } from "botlib";
import { IGBInstance } from 'botlib';
@Table
export class GuaribasInstance extends Model<GuaribasInstance>
@ -58,185 +57,185 @@ export class GuaribasInstance extends Model<GuaribasInstance>
@PrimaryKey
@AutoIncrement
@Column
instanceId: number;
public instanceId: number;
@Column
botEndpoint: string;
public botEndpoint: string;
@Column
whoAmIVideo: string;
public whoAmIVideo: string;
@Column
botId: string;
public botId: string;
@Column
title: string;
public title: string;
@Column
description: string;
public description: string;
@Column
version: string;
public version: string;
@Column
enabledAdmin: boolean;
public enabledAdmin: boolean;
/* Services section on bot.json */
@Column
engineName: string;
public engineName: string;
@Column
marketplaceId: string;
public marketplaceId: string;
@Column
textAnalyticsKey: string;
public textAnalyticsKey: string;
@Column
textAnalyticsEndpoint: string;
public textAnalyticsEndpoint: string;
@Column
marketplacePassword: string;
public marketplacePassword: string;
@Column
webchatKey: string;
public webchatKey: string;
@Column
authenticatorTenant: string;
public authenticatorTenant: string;
@Column
authenticatorAuthorityHostUrl: string;
public authenticatorAuthorityHostUrl: string;
@Column
authenticatorClientId: string;
public authenticatorClientId: string;
@Column
authenticatorClientSecret: string;
public authenticatorClientSecret: string;
@Column
cloudSubscriptionId: string;
@Column
cloudUsername: string;
public cloudSubscriptionId: string;
@Column
cloudPassword: string;
public cloudUsername: string;
@Column
cloudLocation: string;
public cloudPassword: string;
@Column
whatsappBotKey: string;
public cloudLocation: string;
@Column
whatsappServiceKey: string;
public whatsappBotKey: string;
@Column
whatsappServiceNumber: string;
public whatsappServiceKey: string;
@Column
whatsappServiceUrl: string;
public whatsappServiceNumber: string;
@Column
whatsappServiceWebhookUrl: string;
public whatsappServiceUrl: string;
@Column
smsKey: string;
public whatsappServiceWebhookUrl: string;
@Column
smsSecret: string;
public smsKey: string;
@Column
smsServiceNumber: string;
public smsSecret: string;
@Column
speechKey: string;
public smsServiceNumber: string;
@Column
speechKeyEndpoint: string;
public speechKey: string;
@Column
spellcheckerKey: string;
public speechKeyEndpoint: string;
@Column
spellcheckerEndpoint: string;
public spellcheckerKey: string;
@Column
theme: string;
public spellcheckerEndpoint: string;
@Column
ui: string;
public theme: string;
@Column
kb: string;
public ui: string;
@Column
nlpAppId: string;
public kb: string;
@Column
nlpKey: string;
public nlpAppId: string;
@Column
public nlpKey: string;
@Column
@Column({ type: DataType.STRING(512) })
nlpEndpoint: string;
public nlpEndpoint: string;
@Column
nlpAuthoringKey: string;
@Column
deploymentPaths: string;
public nlpAuthoringKey: string;
@Column
searchHost: string;
public deploymentPaths: string;
@Column
searchKey: string;
public searchHost: string;
@Column
searchIndex: string;
public searchKey: string;
@Column
searchIndexer: string;
public searchIndex: string;
@Column
storageUsername: string;
public searchIndexer: string;
@Column
storagePassword: string;
public storageUsername: string;
@Column
storageName: string;
public storagePassword: string;
@Column
storageServer: string;
public storageName: string;
@Column
storageDialect: string;
public storageServer: string;
@Column
storagePath: string;
public storageDialect: string;
@Column
adminPass: string;
public storagePath: string;
@Column
public adminPass: string;
/* Settings section of bot.json */
@Column(DataType.FLOAT)
nlpVsSearch: number;
public nlpVsSearch: number;
@Column(DataType.FLOAT)
searchScore: number;
public searchScore: number;
@Column(DataType.FLOAT)
nlpScore: number;
public nlpScore: number;
@Column
@CreatedAt
createdAt: Date;
public createdAt: Date;
@Column
@UpdatedAt
updatedAt: Date;
public updatedAt: Date;
}
@Table
@ -244,25 +243,25 @@ export class GuaribasPackage extends Model<GuaribasPackage> {
@PrimaryKey
@AutoIncrement
@Column
packageId: number;
public packageId: number;
@Column
packageName: string;
public packageName: string;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number;
public instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance;
public instance: GuaribasInstance;
@Column
@CreatedAt
createdAt: Date;
public createdAt: Date;
@Column
@UpdatedAt
updatedAt: Date;
public updatedAt: Date;
}
@Table
@ -270,18 +269,18 @@ export class GuaribasChannel extends Model<GuaribasChannel> {
@PrimaryKey
@AutoIncrement
@Column
channelId: number;
public channelId: number;
@Column
title: string;
public title: string;
@Column
@CreatedAt
createdAt: Date;
public createdAt: Date;
@Column
@UpdatedAt
updatedAt: Date;
public updatedAt: Date;
}
@Table
@ -289,23 +288,23 @@ export class GuaribasException extends Model<GuaribasException> {
@PrimaryKey
@AutoIncrement
@Column
exceptionId: number;
public exceptionId: number;
@Column
message: string;
public message: string;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number;
public instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance;
public instance: GuaribasInstance;
@Column
@CreatedAt
createdAt: Date;
public createdAt: Date;
@Column
@UpdatedAt
updatedAt: Date;
public updatedAt: Date;
}

View file

@ -30,20 +30,20 @@
| |
\*****************************************************************************/
const logger = require("../../../src/logger");
import * as fs from "fs";
const logger = require('../../../src/logger');
import * as fs from 'fs';
/**
* @fileoverview General Bots server core.
*/
"use strict";
'use strict';
export class GBConfigService {
static init(): any {
public static init(): any {
try {
require("dotenv-extended").load({
path: ".env",
require('dotenv-extended').load({
path: '.env',
errorOnMissing: true,
errorOnExtra: false,
overrideProcessEnv: true
@ -54,52 +54,52 @@ export class GBConfigService {
}
}
static get(key: string): string | undefined {
public static get(key: string): string | undefined {
let value = GBConfigService.tryGet(key);
if (!value) {
switch (key) {
case "CLOUD_USERNAME":
case 'CLOUD_USERNAME':
value = undefined;
break;
case "BOT_ID":
case 'BOT_ID':
value = undefined;
break;
case "CLOUD_PASSWORD":
case 'CLOUD_PASSWORD':
value = undefined;
break;
case "CLOUD_SUBSCRIPTIONID":
case 'CLOUD_SUBSCRIPTIONID':
value = undefined;
break;
case "CLOUD_LOCATION":
case 'CLOUD_LOCATION':
value = undefined;
break;
case "NLP_AUTHORING_KEY":
case 'NLP_AUTHORING_KEY':
value = undefined;
break;
case "STORAGE_DIALECT":
case 'STORAGE_DIALECT':
value = undefined;
break;
case "STORAGE_STORAGE":
value = "./guaribas.sqlite";
case 'STORAGE_STORAGE':
value = './guaribas.sqlite';
break;
case "ADDITIONAL_DEPLOY_PATH":
case 'ADDITIONAL_DEPLOY_PATH':
value = undefined;
break;
case "STORAGE_SYNC":
value = "false";
case 'STORAGE_SYNC':
value = 'false';
break;
case "STORAGE_SYNC_ALTER":
value = "false";
case 'STORAGE_SYNC_ALTER':
value = 'false';
break;
case "STORAGE_SYNC_FORCE":
value = "false";
case 'STORAGE_SYNC_FORCE':
value = 'false';
break;
case "STORAGE_LOGGING":
value = "false";
case 'STORAGE_LOGGING':
value = 'false';
break;
case "STORAGE_ENCRYPT":
value = "true";
case 'STORAGE_ENCRYPT':
value = 'true';
break;
default:
logger.warn(`Invalid key on .env file: '${key}'`);
@ -110,7 +110,7 @@ export class GBConfigService {
}
public static tryGet(key: string) {
let value = process.env["container:" + key];
let value = process.env['container:' + key];
if (!value) {
value = process.env[key];
}

View file

@ -36,17 +36,17 @@
'use strict';
const logger = require("../../../src/logger");
const logger = require('../../../src/logger');
import { GBCoreService } from "./GBCoreService";
import { IGBConversationalService } from "botlib";
import { GBMinInstance } from "botlib";
import { LuisRecognizer } from "botbuilder-ai";
import { MessageFactory } from "botbuilder";
import { Messages } from "../strings";
import { AzureText } from "pragmatismo-io-framework";
import { any } from "bluebird";
const Nexmo = require("nexmo");
import { any } from 'bluebird';
import { MessageFactory } from 'botbuilder';
import { LuisRecognizer } from 'botbuilder-ai';
import { IGBConversationalService } from 'botlib';
import { GBMinInstance } from 'botlib';
import { AzureText } from 'pragmatismo-io-framework';
import { Messages } from '../strings';
import { GBCoreService } from './GBCoreService';
const Nexmo = require('nexmo');
export interface LanguagePickerSettings {
defaultLocale?: string;
@ -54,27 +54,27 @@ export interface LanguagePickerSettings {
}
export class GBConversationalService implements IGBConversationalService {
coreService: GBCoreService;
public coreService: GBCoreService;
constructor(coreService: GBCoreService) {
this.coreService = coreService;
}
getCurrentLanguage(step: any) {
public getCurrentLanguage(step: any) {
return step.context.activity.locale;
}
async sendEvent(step: any, name: string, value: any): Promise<any> {
if (step.context.activity.channelId === "webchat") {
const msg = MessageFactory.text("");
public async sendEvent(step: any, name: string, value: any): Promise<any> {
if (step.context.activity.channelId === 'webchat') {
const msg = MessageFactory.text('');
msg.value = value;
msg.type = "event";
msg.type = 'event';
msg.name = name;
return step.context.sendActivity(msg);
}
}
async sendSms(
public async sendSms(
min: GBMinInstance,
mobile: string,
text: string
@ -99,7 +99,7 @@ export class GBConversationalService implements IGBConversationalService {
});
}
async routeNLP(step: any, min: GBMinInstance, text: string): Promise<boolean> {
public async routeNLP(step: any, min: GBMinInstance, text: string): Promise<boolean> {
// Invokes LUIS.
const model = new LuisRecognizer({
@ -112,39 +112,38 @@ export class GBConversationalService implements IGBConversationalService {
try {
nlp = await model.recognize(step.context);
} catch (error) {
if (error.statusCode == 404){
logger.warn ('NLP application still not publish and there are no other options for answering.')
if (error.statusCode == 404) {
logger.warn ('NLP application still not publish and there are no other options for answering.');
return Promise.resolve(false);
}
else{
let msg = `Error calling NLP server, check if you have a published model and assigned keys on the service. Error: ${
error.statusCode ? error.statusCode : ""
} else {
const msg = `Error calling NLP server, check if you have a published model and assigned keys on the service. Error: ${
error.statusCode ? error.statusCode : ''
} ${error.message}`;
return Promise.reject(new Error(msg));}
return Promise.reject(new Error(msg)); }
}
// Resolves intents returned from LUIS.
let topIntent = LuisRecognizer.topIntent(nlp);
const topIntent = LuisRecognizer.topIntent(nlp);
if (topIntent) {
var intent = topIntent;
var entity =
const intent = topIntent;
const entity =
nlp.entities && nlp.entities.length > 0
? nlp.entities[0].entity.toUpperCase()
: null;
if (intent === "None") {
if (intent === 'None') {
return Promise.resolve(false);
}
logger.info("NLP called:" + intent + ", " + entity);
logger.info('NLP called:' + intent + ', ' + entity);
try {
await step.replace("/" + intent, nlp.entities);
await step.replace('/' + intent, nlp.entities);
return Promise.resolve(true);
} catch (error) {
let msg = `Error finding dialog associated to NLP event: ${intent}: ${
const msg = `Error finding dialog associated to NLP event: ${intent}: ${
error.message
}`;
return Promise.reject(new Error(msg));
@ -153,20 +152,20 @@ export class GBConversationalService implements IGBConversationalService {
return Promise.resolve(false);
}
async checkLanguage(step, min, text) {
let locale = await AzureText.getLocale(
public async checkLanguage(step, min, text) {
const locale = await AzureText.getLocale(
min.instance.textAnalyticsKey,
min.instance.textAnalyticsEndpoint,
text
);
if (locale != step.context.activity.locale.split("-")[0]) {
if (locale != step.context.activity.locale.split('-')[0]) {
switch (locale) {
case "pt":
step.context.activity.locale = "pt-BR";
case 'pt':
step.context.activity.locale = 'pt-BR';
await step.context.sendActivity(Messages[locale].changing_language);
break;
case "en":
step.context.activity.locale = "en-US";
case 'en':
step.context.activity.locale = 'en-US';
await step.context.sendActivity(Messages[locale].changing_language);
break;
default:

View file

@ -70,7 +70,7 @@ export class GBCoreService implements IGBCoreService {
private createTableQuery: (
tableName: string,
attributes: any,
options: any,
options: any
) => string;
/**
@ -136,15 +136,15 @@ export class GBCoreService implements IGBCoreService {
dialect: this.dialect,
storage: storage,
dialectOptions: {
encrypt: encrypt,
encrypt: encrypt
},
pool: {
max: 32,
min: 8,
idle: 40000,
evict: 40000,
acquire: 40000,
},
acquire: 40000
}
});
if (this.dialect === 'mssql') {
@ -153,7 +153,7 @@ export class GBCoreService implements IGBCoreService {
this.queryGenerator.createTableQuery = (
tableName,
attributes,
options,
options
) => this.createTableQueryOverride(tableName, attributes, options);
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
@ -163,7 +163,7 @@ export class GBCoreService implements IGBCoreService {
} catch (error) {
reject(error);
}
},
}
);
}
@ -174,7 +174,7 @@ export class GBCoreService implements IGBCoreService {
logger.info('Syncing database...');
return this.sequelize.sync({
alter: alter,
force: force,
force: force
});
} else {
const msg = 'Database synchronization is disabled.';
@ -257,7 +257,7 @@ export class GBCoreService implements IGBCoreService {
let sql: string = this.createTableQuery.apply(this.queryGenerator, [
tableName,
attributes,
options,
options
]);
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
@ -268,7 +268,7 @@ export class GBCoreService implements IGBCoreService {
re2,
(match: string, ...args: any[]): string => {
return 'CONSTRAINT [' + table + '_pk] ' + match;
},
}
);
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re4 = /\[([^\]]*)\]/g;
@ -283,7 +283,7 @@ export class GBCoreService implements IGBCoreService {
matches = re4.exec(fkcols);
}
return 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')';
},
}
);
}
return sql;
@ -300,7 +300,7 @@ export class GBCoreService implements IGBCoreService {
private changeColumnQueryOverride(tableName, attributes): string {
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [
tableName,
attributes,
attributes
]);
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
@ -326,7 +326,7 @@ export class GBCoreService implements IGBCoreService {
fkcols +
')'
);
},
}
);
}
return sql;

View file

@ -36,35 +36,45 @@
'use strict';
const logger = require("../../../src/logger");
const Path = require("path");
const UrlJoin = require("url-join");
const Fs = require("fs");
const WaitUntil = require("wait-until");
const express = require("express");
const logger = require('../../../src/logger');
const Path = require('path');
const UrlJoin = require('url-join');
const Fs = require('fs');
const WaitUntil = require('wait-until');
const express = require('express');
import { KBService } from "./../../kb.gbapp/services/KBService";
import { GBImporter } from "./GBImporter";
import { IGBCoreService, IGBInstance } from "botlib";
import { GBConfigService } from "./GBConfigService";
import { GBError } from "botlib";
import { GuaribasPackage, GuaribasInstance } from "../models/GBModel";
import { IGBPackage } from "botlib";
import { AzureSearch } from "pragmatismo-io-framework";
import { AzureDeployerService } from "../../azuredeployer.gbapp/services/AzureDeployerService";
import { IGBCoreService, IGBInstance } from 'botlib';
import { GBError } from 'botlib';
import { IGBPackage } from 'botlib';
import { AzureSearch } from 'pragmatismo-io-framework';
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel';
import { KBService } from './../../kb.gbapp/services/KBService';
import { GBConfigService } from './GBConfigService';
import { GBImporter } from './GBImporter';
/** Deployer service for bots, themes, ai and more. */
export class GBDeployer {
core: IGBCoreService;
importer: GBImporter;
workDir: string = "./work";
static deployFolder = "packages";
public static deployFolder = 'packages';
public core: IGBCoreService;
public importer: GBImporter;
public workDir: string = './work';
constructor(core: IGBCoreService, importer: GBImporter) {
this.core = core;
this.importer = importer;
}
public static getConnectionStringFromInstance(instance: GuaribasInstance) {
return `Server=tcp:${
instance.storageServer
}.database.windows.net,1433;Database=${instance.storageName};User ID=${
instance.storageUsername
};Password=${
instance.storagePassword
};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
}
/**
*
* Performs package deployment in all .gbai or default.
@ -73,18 +83,18 @@ export class GBDeployer {
public deployPackages(
core: IGBCoreService,
server: any,
appPackages: Array<IGBPackage>
appPackages: IGBPackage[]
) {
let _this = this;
const _this = this;
return new Promise((resolve: any, reject: any): any => {
let totalPackages = 0;
let additionalPath = GBConfigService.get("ADDITIONAL_DEPLOY_PATH");
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
let paths = [GBDeployer.deployFolder];
if (additionalPath) {
paths = paths.concat(additionalPath.toLowerCase().split(";"));
paths = paths.concat(additionalPath.toLowerCase().split(';'));
}
let botPackages = new Array<string>();
let gbappPackages = new Array<string>();
const botPackages = new Array<string>();
const gbappPackages = new Array<string>();
let generalPackages = new Array<string>();
function doIt(path) {
@ -94,14 +104,14 @@ export class GBDeployer {
.map(name => Path.join(source, name))
.filter(isDirectory);
let dirs = getDirectories(path);
const dirs = getDirectories(path);
dirs.forEach(element => {
if (element.startsWith(".")) {
if (element.startsWith('.')) {
logger.info(`Ignoring ${element}...`);
} else {
if (element.endsWith(".gbot")) {
if (element.endsWith('.gbot')) {
botPackages.push(element);
} else if (element.endsWith(".gbapp")) {
} else if (element.endsWith('.gbapp')) {
gbappPackages.push(element);
} else {
generalPackages.push(element);
@ -124,11 +134,11 @@ export class GBDeployer {
gbappPackages.forEach(e => {
// Skips .gbapp inside deploy folder.
if (!e.startsWith("packages")) {
if (!e.startsWith('packages')) {
logger.info(`Deploying app: ${e}...`);
import(e)
.then(m => {
let p = new m.Package();
const p = new m.Package();
p.loadPackage(core, core.sequelize);
appPackages.push(p);
logger.info(`App (.gbapp) deployed: ${e}.`);
@ -169,40 +179,40 @@ export class GBDeployer {
/** Then all remaining generalPackages are loaded. */
generalPackages = generalPackages.filter(p => !p.endsWith(".git"));
generalPackages = generalPackages.filter(p => !p.endsWith('.git'));
generalPackages.forEach(filename => {
let filenameOnly = Path.basename(filename);
const filenameOnly = Path.basename(filename);
logger.info(`Deploying package: ${filename}...`);
/** Handles apps for general bots - .gbapp must stay out of deploy folder. */
if (
Path.extname(filename) === ".gbapp" ||
Path.extname(filename) === ".gblib"
Path.extname(filename) === '.gbapp' ||
Path.extname(filename) === '.gblib'
) {
/** Themes for bots. */
} else if (Path.extname(filename) === ".gbtheme") {
server.use("/themes/" + filenameOnly, express.static(filename));
} else if (Path.extname(filename) === '.gbtheme') {
server.use('/themes/' + filenameOnly, express.static(filename));
logger.info(
`Theme (.gbtheme) assets accessible at: ${"/themes/" +
`Theme (.gbtheme) assets accessible at: ${'/themes/' +
filenameOnly}.`
);
/** Knowledge base for bots. */
} else if (Path.extname(filename) === ".gbkb") {
} else if (Path.extname(filename) === '.gbkb') {
server.use(
"/kb/" + filenameOnly + "/subjects",
express.static(UrlJoin(filename, "subjects"))
'/kb/' + filenameOnly + '/subjects',
express.static(UrlJoin(filename, 'subjects'))
);
logger.info(
`KB (.gbkb) assets accessible at: ${"/kb/" + filenameOnly}.`
`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`
);
} else if (Path.extname(filename) === ".gbui") {
} else if (Path.extname(filename) === '.gbui') {
// Already Handled
} else {
/** Unknown package format. */
let err = new Error(`Package type not handled: ${filename}.`);
const err = new Error(`Package type not handled: ${filename}.`);
reject(err);
}
totalPackages++;
@ -218,7 +228,7 @@ export class GBDeployer {
.done(function(result) {
if (botPackages.length === 0) {
logger.warn(
"No external packages to load, please use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder."
'No external packages to load, please use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder.'
);
} else {
logger.info(`Package deployment done.`);
@ -233,17 +243,17 @@ export class GBDeployer {
* Deploys a bot to the storage.
*/
async deployBot(localPath: string): Promise<IGBInstance> {
let packageType = Path.extname(localPath);
let packageName = Path.basename(localPath);
let instance = await this.importer.importIfNotExistsBotPackage(
public async deployBot(localPath: string): Promise<IGBInstance> {
const packageType = Path.extname(localPath);
const packageName = Path.basename(localPath);
const instance = await this.importer.importIfNotExistsBotPackage(
packageName,
localPath
);
return instance;
}
async deployPackageToStorage(
public async deployPackageToStorage(
instanceId: number,
packageName: string
): Promise<GuaribasPackage> {
@ -253,7 +263,7 @@ export class GBDeployer {
});
}
deployTheme(localPath: string) {
public deployTheme(localPath: string) {
// DISABLED: Until completed, "/ui/public".
// FsExtra.copy(localPath, this.workDir + packageName)
// .then(() => {
@ -265,26 +275,26 @@ export class GBDeployer {
// })
}
async deployPackageFromLocalPath(localPath: string) {
let packageType = Path.extname(localPath);
public async deployPackageFromLocalPath(localPath: string) {
const packageType = Path.extname(localPath);
switch (packageType) {
case ".gbot":
case '.gbot':
return this.deployBot(localPath);
case ".gbtheme":
case '.gbtheme':
return this.deployTheme(localPath);
// PACKAGE: Put in package logic.
case ".gbkb":
let service = new KBService(this.core.sequelize);
case '.gbkb':
const service = new KBService(this.core.sequelize);
return service.deployKb(this.core, this, localPath);
case ".gbui":
case '.gbui':
break;
default:
var err = GBError.create(
const err = GBError.create(
`GuaribasBusinessError: Unknow package type: ${packageType}.`
);
Promise.reject(err);
@ -292,30 +302,30 @@ export class GBDeployer {
}
}
async undeployPackageFromLocalPath(instance: IGBInstance, localPath: string) {
let packageType = Path.extname(localPath);
let packageName = Path.basename(localPath);
public async undeployPackageFromLocalPath(instance: IGBInstance, localPath: string) {
const packageType = Path.extname(localPath);
const packageName = Path.basename(localPath);
let p = await this.getPackageByName(instance.instanceId, packageName);
const p = await this.getPackageByName(instance.instanceId, packageName);
switch (packageType) {
case ".gbot":
case '.gbot':
// TODO: this.undeployBot(packageName, localPath)
break;
case ".gbtheme":
case '.gbtheme':
// TODO: this.undeployTheme(packageName, localPath)
break;
case ".gbkb":
let service = new KBService(this.core.sequelize);
case '.gbkb':
const service = new KBService(this.core.sequelize);
return service.undeployKbFromStorage(instance, this, p.packageId);
case ".gbui":
case '.gbui':
break;
default:
var err = GBError.create(
const err = GBError.create(
`GuaribasBusinessError: Unknown package type: ${packageType}.`
);
Promise.reject(err);
@ -323,27 +333,17 @@ export class GBDeployer {
}
}
public static getConnectionStringFromInstance(instance: GuaribasInstance) {
return `Server=tcp:${
instance.storageServer
}.database.windows.net,1433;Database=${instance.storageName};User ID=${
instance.storageUsername
};Password=${
instance.storagePassword
};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
}
public async rebuildIndex(instance: GuaribasInstance) {
let search = new AzureSearch(
const search = new AzureSearch(
instance.searchKey,
instance.searchHost,
instance.searchIndex,
instance.searchIndexer
);
let connectionString = GBDeployer.getConnectionStringFromInstance(instance);
const connectionString = GBDeployer.getConnectionStringFromInstance(instance);
const dsName = "gb";
const dsName = 'gb';
try {
await search.deleteDataSource(dsName);
} catch (err) {
@ -356,8 +356,8 @@ export class GBDeployer {
await search.createDataSource(
dsName,
dsName,
"GuaribasQuestion",
"azuresql",
'GuaribasQuestion',
'azuresql',
connectionString
);
@ -375,11 +375,11 @@ export class GBDeployer {
);
}
async getPackageByName(
public async getPackageByName(
instanceId: number,
packageName: string
): Promise<GuaribasPackage> {
var where = { packageName: packageName, instanceId: instanceId };
const where = { packageName: packageName, instanceId: instanceId };
return GuaribasPackage.findOne({
where: where
});
@ -390,11 +390,11 @@ export class GBDeployer {
* Hot deploy processing.
*
*/
async scanBootPackage() {
const deployFolder = "packages";
let bootPackage = GBConfigService.get("BOOT_PACKAGE");
public async scanBootPackage() {
const deployFolder = 'packages';
const bootPackage = GBConfigService.get('BOOT_PACKAGE');
if (bootPackage === "none") {
if (bootPackage === 'none') {
return Promise.resolve(true);
} else {
return this.deployPackageFromLocalPath(

View file

@ -30,42 +30,41 @@
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
import Fs = require("fs")
import Path = require("path")
import { IGBCoreService, IGBInstance } from "botlib"
import { SecService } from "../../security.gblib/services/SecService"
import { GuaribasInstance } from "../models/GBModel"
const UrlJoin = require('url-join');
import { IGBCoreService, IGBInstance } from 'botlib';
import fs = require('fs');
import path = require('path');
import { SecService } from '../../security.gblib/services/SecService';
import { GuaribasInstance } from '../models/GBModel';
export class GBImporter {
core: IGBCoreService
public core: IGBCoreService;
constructor(core: IGBCoreService) {
this.core = core
this.core = core;
}
async importIfNotExistsBotPackage(
public async importIfNotExistsBotPackage(
packageName: string,
localPath: string) {
let packageJson = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "package.json"), "utf8")
)
const packageJson = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8')
);
let botId = packageJson.botId
const botId = packageJson.botId;
let instance = await this.core.loadInstance(botId)
const instance = await this.core.loadInstance(botId);
if (instance) {
return Promise.resolve(instance)
return Promise.resolve(instance);
} else {
return this.createInstanceInternal(packageName, localPath, packageJson)
return this.createInstanceInternal(packageName, localPath, packageJson);
}
}
@ -75,20 +74,20 @@ export class GBImporter {
packageJson: any
) {
const settings = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "settings.json"), "utf8")
)
Fs.readFileSync(UrlJoin(localPath, 'settings.json'), 'utf8')
);
const servicesJson = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "services.json"), "utf8")
)
Fs.readFileSync(UrlJoin(localPath, 'services.json'), 'utf8')
);
packageJson = Object.assign(packageJson, settings, servicesJson)
packageJson = {...packageJson, ...settings, ...servicesJson};
GuaribasInstance.create(packageJson).then((instance: IGBInstance) => {
let service = new SecService()
const service = new SecService();
// TODO: service.importSecurityFile(localPath, instance)
Promise.resolve(instance)
})
Promise.resolve(instance);
});
}
}
}

View file

@ -36,48 +36,48 @@
'use strict';
const { DialogSet, TextPrompt } = require("botbuilder-dialogs");
const UrlJoin = require("url-join");
const express = require("express");
const logger = require("../../../src/logger");
const request = require("request-promise-native");
var AuthenticationContext = require("adal-node").AuthenticationContext;
const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
const UrlJoin = require('url-join');
const express = require('express');
const logger = require('../../../src/logger');
const request = require('request-promise-native');
const AuthenticationContext = require('adal-node').AuthenticationContext;
import {
AutoSaveStateMiddleware,
BotFrameworkAdapter,
BotStateSet,
ConversationState,
MemoryStorage,
UserState,
AutoSaveStateMiddleware
} from "botbuilder";
UserState
} from 'botbuilder';
import { GBMinInstance, IGBPackage } from "botlib";
import { GBAnalyticsPackage } from "../../analytics.gblib";
import { GBCorePackage } from "../../core.gbapp";
import { GBKBPackage } from "../../kb.gbapp";
import { GBDeployer } from "./GBDeployer";
import { GBSecurityPackage } from "../../security.gblib";
import { GBAdminPackage } from "./../../admin.gbapp/index";
import { GBCustomerSatisfactionPackage } from "../../customer-satisfaction.gbapp";
import { GBWhatsappPackage } from "../../whatsapp.gblib";
import { GBMinInstance, IGBPackage } from 'botlib';
import {
IGBAdminService,
IGBCoreService,
IGBConversationalService
} from "botlib";
import { GuaribasInstance } from "../models/GBModel";
import { Messages } from "../strings";
IGBConversationalService,
IGBCoreService
} from 'botlib';
import { GBAnalyticsPackage } from '../../analytics.gblib';
import { GBCorePackage } from '../../core.gbapp';
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
import { GBKBPackage } from '../../kb.gbapp';
import { GBSecurityPackage } from '../../security.gblib';
import { GBWhatsappPackage } from '../../whatsapp.gblib';
import { GuaribasInstance } from '../models/GBModel';
import { Messages } from '../strings';
import { GBAdminPackage } from './../../admin.gbapp/index';
import { GBDeployer } from './GBDeployer';
/** Minimal service layer for a bot. */
export class GBMinService {
core: IGBCoreService;
conversationalService: IGBConversationalService;
adminService: IGBAdminService;
deployer: GBDeployer;
public core: IGBCoreService;
public conversationalService: IGBConversationalService;
public adminService: IGBAdminService;
public deployer: GBDeployer;
corePackage = "core.gbai";
public corePackage = 'core.gbai';
/**
* Static initialization of minimal instance.
@ -107,40 +107,40 @@ export class GBMinService {
*
* */
async buildMin(
public async buildMin(
server: any,
appPackages: Array<IGBPackage>,
appPackages: IGBPackage[],
instances: GuaribasInstance[]
): Promise<GBMinInstance> {
// Serves default UI on root address '/'.
let uiPackage = "default.gbui";
const uiPackage = 'default.gbui';
server.use(
"/",
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, "build"))
'/',
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build'))
);
Promise.all(
instances.map(async instance => {
// Gets the authorization key for each instance from Bot Service.
let webchatToken = await this.getWebchatToken(instance);
const webchatToken = await this.getWebchatToken(instance);
// Serves the bot information object via HTTP so clients can get
// instance information stored on server.
server.get("/instances/:botId", (req, res) => {
server.get('/instances/:botId', (req, res) => {
(async () => {
// Returns the instance object to clients requesting bot info.
let botId = req.params.botId;
let instance = await this.core.loadInstance(botId);
const botId = req.params.botId;
const instance = await this.core.loadInstance(botId);
if (instance) {
let speechToken = await this.getSTSToken(instance);
const speechToken = await this.getSTSToken(instance);
let theme = instance.theme;
if (!theme) {
theme = "default.gbtheme";
theme = 'default.gbtheme';
}
res.send(
@ -156,7 +156,7 @@ export class GBMinService {
})
);
} else {
let error = `Instance not found: ${botId}.`;
const error = `Instance not found: ${botId}.`;
res.sendStatus(error);
logger.error(error);
}
@ -165,7 +165,7 @@ export class GBMinService {
// Build bot adapter.
var { min, adapter, conversationState } = await this.buildBotAdapter(
const { min, adapter, conversationState } = await this.buildBotAdapter(
instance
);
@ -175,7 +175,7 @@ export class GBMinService {
// Serves individual URL for each bot conversational interface...
let url = `/api/messages/${instance.botId}`;
const url = `/api/messages/${instance.botId}`;
server.post(url, async (req, res) => {
return this.receiver(
adapter,
@ -193,14 +193,14 @@ export class GBMinService {
// Serves individual URL for each bot user interface.
let uiUrl = `/${instance.botId}`;
const uiUrl = `/${instance.botId}`;
server.use(
uiUrl,
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, "build"))
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build'))
);
logger.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
let state = `${instance.instanceId}${Math.floor(
const state = `${instance.instanceId}${Math.floor(
Math.random() * 1000000000
)}`;
@ -211,7 +211,7 @@ export class GBMinService {
let authorizationUrl = UrlJoin(
min.instance.authenticatorAuthorityHostUrl,
min.instance.authenticatorTenant,
"/oauth2/authorize"
'/oauth2/authorize'
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.authenticatorClientId
@ -227,57 +227,57 @@ export class GBMinService {
// access token that can be used to access the user owned resource.
server.get(`/${min.instance.botId}/token`, async (req, res) => {
let state = await min.adminService.getValue(
const state = await min.adminService.getValue(
min.instance.instanceId,
"AntiCSRFAttackState"
'AntiCSRFAttackState'
);
if (req.query.state != state) {
let msg =
"WARNING: state field was not provided as anti-CSRF token";
const msg =
'WARNING: state field was not provided as anti-CSRF token';
logger.error(msg);
throw new Error(msg);
}
var authenticationContext = new AuthenticationContext(
const authenticationContext = new AuthenticationContext(
UrlJoin(
min.instance.authenticatorAuthorityHostUrl,
min.instance.authenticatorTenant
)
);
let resource = "https://graph.microsoft.com";
const resource = 'https://graph.microsoft.com';
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
UrlJoin(instance.botEndpoint, min.instance.botId, "/token"),
UrlJoin(instance.botEndpoint, min.instance.botId, '/token'),
resource,
instance.authenticatorClientId,
instance.authenticatorClientSecret,
async (err, token) => {
if (err) {
let msg = `Error acquiring token: ${err}`;
const msg = `Error acquiring token: ${err}`;
logger.error(msg);
res.send(msg);
} else {
await this.adminService.setValue(
instance.instanceId,
"refreshToken",
'refreshToken',
token.refreshToken
);
await this.adminService.setValue(
instance.instanceId,
"accessToken",
'accessToken',
token.accessToken
);
await this.adminService.setValue(
instance.instanceId,
"expiresOn",
'expiresOn',
token.expiresOn.toString()
);
await this.adminService.setValue(
instance.instanceId,
"AntiCSRFAttackState",
'AntiCSRFAttackState',
null
);
@ -305,8 +305,57 @@ export class GBMinService {
);
}
/**
* Get Webchat key from Bot Service.
*
* @param instance The Bot instance.
*
*/
public async getWebchatToken(instance: any) {
const options = {
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
method: 'POST',
headers: {
Authorization: `Bearer ${instance.webchatKey}`
}
};
try {
const json = await request(options);
return Promise.resolve(JSON.parse(json));
} catch (error) {
const msg = `[botId:${instance.botId}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
return Promise.reject(new Error(msg));
}
}
/**
* Gets a Speech to Text / Text to Speech token from the provider.
*
* @param instance The general bot instance.
*
*/
public async getSTSToken(instance: any) {
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
const options = {
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': instance.speechKey
}
};
try {
return await request(options);
} catch (error) {
const msg = `Error calling Speech to Text client. Error is: ${error}.`;
return Promise.reject(new Error(msg));
}
}
private async buildBotAdapter(instance: any) {
let adapter = new BotFrameworkAdapter({
const adapter = new BotFrameworkAdapter({
appId: instance.marketplaceId,
appPassword: instance.marketplacePassword
});
@ -318,7 +367,7 @@ export class GBMinService {
// The minimal bot is built here.
let min = new GBMinInstance();
const min = new GBMinInstance();
min.botId = instance.botId;
min.bot = adapter;
min.userState = userState;
@ -326,16 +375,16 @@ export class GBMinService {
min.conversationalService = this.conversationalService;
min.adminService = this.adminService;
min.instance = await this.core.loadInstance(min.botId);
min.userProfile = conversationState.createProperty("userProfile");
const dialogState = conversationState.createProperty("dialogState");
min.userProfile = conversationState.createProperty('userProfile');
const dialogState = conversationState.createProperty('dialogState');
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt("textPrompt"));
min.dialogs.add(new TextPrompt('textPrompt'));
return { min, adapter, conversationState };
}
private invokeLoadBot(appPackages: any[], min: any, server: any) {
let sysPackages = new Array<IGBPackage>();
const sysPackages = new Array<IGBPackage>();
// NOTE: A semicolon is necessary before this line.
[
GBCorePackage,
@ -346,21 +395,21 @@ export class GBMinService {
GBCustomerSatisfactionPackage,
GBWhatsappPackage
].forEach(sysPackage => {
let p = Object.create(sysPackage.prototype) as IGBPackage;
const p = Object.create(sysPackage.prototype) as IGBPackage;
p.loadBot(min);
sysPackages.push(p);
if (sysPackage.name === "GBWhatsappPackage") {
let url = "/instances/:botId/whatsapp";
if (sysPackage.name === 'GBWhatsappPackage') {
const url = '/instances/:botId/whatsapp';
server.post(url, (req, res) => {
p["channel"].received(req, res);
p.channel.received(req, res);
});
}
}, this);
}, this);
appPackages.forEach(e => {
e.sysPackages = sysPackages;
e.loadBot(min);
}, this);
}, this);
}
/**
@ -378,16 +427,16 @@ export class GBMinService {
return adapter.processActivity(req, res, async context => {
const state = conversationState.get(context);
const step = await min.dialogs.createContext(context, state);
step.context.activity.locale = "en-US"; // TODO: Make dynamic.
step.context.activity.locale = 'en-US'; // TODO: Make dynamic.
try {
const user = await min.userProfile.get(context, {});
if (!user.loaded) {
await min.conversationalService.sendEvent(step, "loadInstance", {
await min.conversationalService.sendEvent(step, 'loadInstance', {
instanceId: instance.instanceId,
botId: instance.botId,
theme: instance.theme?instance.theme:"default.gbtheme" ,
theme: instance.theme ? instance.theme : 'default.gbtheme' ,
secret: instance.webchatKey
});
user.loaded = true;
@ -401,32 +450,32 @@ export class GBMinService {
}, ${context.activity.channelId}, {context.activity.value})`
);
if (
context.activity.type === "conversationUpdate" &&
context.activity.type === 'conversationUpdate' &&
context.activity.membersAdded.length > 0
) {
let member = context.activity.membersAdded[0];
if (member.name === "GeneralBots") {
const member = context.activity.membersAdded[0];
if (member.name === 'GeneralBots') {
logger.info(`Bot added to conversation, starting chat...`);
appPackages.forEach(e => {
e.onNewSession(min, step);
});
// Processes the root dialog.
await step.beginDialog("/");
await step.beginDialog('/');
} else {
logger.info(`Member added to conversation: ${member.name}`);
}
// Processes messages.
} else if (context.activity.type === "message") {
} else if (context.activity.type === 'message') {
// Checks for /admin request.
if (context.activity.text === "admin") {
await step.beginDialog("/admin");
if (context.activity.text === 'admin') {
await step.beginDialog('/admin');
// Checks for /menu JSON signature.
} else if (context.activity.text.startsWith('{"title"')) {
await step.beginDialog("/menu", {
await step.beginDialog('/menu', {
data: JSON.parse(context.activity.text)
});
@ -435,102 +484,53 @@ export class GBMinService {
if (step.activeDialog) {
await step.continueDialog();
} else {
await step.beginDialog("/answer", {
await step.beginDialog('/answer', {
query: context.activity.text
});
}
}
// Processes events.
} else if (context.activity.type === "event") {
} else if (context.activity.type === 'event') {
// Empties dialog stack before going to the target.
await step.endAll();
if (context.activity.name === "whoAmI") {
await step.beginDialog("/whoAmI");
} else if (context.activity.name === "showSubjects") {
await step.beginDialog("/menu");
} else if (context.activity.name === "giveFeedback") {
await step.beginDialog("/feedback", {
if (context.activity.name === 'whoAmI') {
await step.beginDialog('/whoAmI');
} else if (context.activity.name === 'showSubjects') {
await step.beginDialog('/menu');
} else if (context.activity.name === 'giveFeedback') {
await step.beginDialog('/feedback', {
fromMenu: true
});
} else if (context.activity.name === "showFAQ") {
await step.beginDialog("/faq");
} else if (context.activity.name === "answerEvent") {
await step.beginDialog("/answerEvent", {
} else if (context.activity.name === 'showFAQ') {
await step.beginDialog('/faq');
} else if (context.activity.name === 'answerEvent') {
await step.beginDialog('/answerEvent', {
questionId: (context.activity as any).data,
fromFaq: true
});
} else if (context.activity.name === "quality") {
await step.beginDialog("/quality", {
} else if (context.activity.name === 'quality') {
await step.beginDialog('/quality', {
score: (context.activity as any).data
});
} else if (context.activity.name === "updateToken") {
let token = (context.activity as any).data;
await step.beginDialog("/adminUpdateToken", { token: token });
} else if (context.activity.name === 'updateToken') {
const token = (context.activity as any).data;
await step.beginDialog('/adminUpdateToken', { token: token });
} else {
await step.continueDialog();
}
}
} catch (error) {
let msg = `ERROR: ${error.message} ${error.stack ? error.stack : ""}`;
const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`;
logger.error(msg);
await step.context.sendActivity(
Messages[step.context.activity.locale].very_sorry_about_error
);
await step.beginDialog("/ask", { isReturning: true });
await step.beginDialog('/ask', { isReturning: true });
}
});
}
/**
* Get Webchat key from Bot Service.
*
* @param instance The Bot instance.
*
*/
async getWebchatToken(instance: any) {
let options = {
url: "https://directline.botframework.com/v3/directline/tokens/generate",
method: "POST",
headers: {
Authorization: `Bearer ${instance.webchatKey}`
}
};
try {
let json = await request(options);
return Promise.resolve(JSON.parse(json));
} catch (error) {
let msg = `[botId:${instance.botId}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
return Promise.reject(new Error(msg));
}
}
/**
* Gets a Speech to Text / Text to Speech token from the provider.
*
* @param instance The general bot instance.
*
*/
async getSTSToken(instance: any) {
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
let options = {
url: "https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken",
method: "POST",
headers: {
"Ocp-Apim-Subscription-Key": instance.speechKey
}
};
try {
return await request(options);
} catch (error) {
let msg = `Error calling Speech to Text client. Error is: ${error}.`;
return Promise.reject(new Error(msg));
}
}
}

View file

@ -1,20 +1,20 @@
export const Messages = {
"en-US": {
show_video: "I will show you a video, please wait...",
good_morning: "good morning",
good_evening: "good evening",
good_night: "good night",
hi: (msg ) => `Hello, ${msg}.`,
'en-US': {
show_video: 'I will show you a video, please wait...',
good_morning: 'good morning',
good_evening: 'good evening',
good_night: 'good night',
hi: (msg) => `Hello, ${msg}.`,
very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`
},
"pt-BR": {
show_video: "Vou te mostrar um vídeo. Por favor, aguarde...",
good_morning: "bom dia",
good_evening: "boa tarde",
good_night: "boa noite",
hi: (msg ) => `Oi, ${msg}.`,
'pt-BR': {
show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...',
good_morning: 'bom dia',
good_evening: 'boa tarde',
good_night: 'boa noite',
hi: (msg) => `Oi, ${msg}.`,
very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`
}
};

View file

@ -1,12 +1,12 @@
import { expect } from 'chai'
import 'mocha'
import {GBImporter} from '../services/GBImporter'
import { expect } from 'chai';
import 'mocha';
import {GBImporter} from '../services/GBImporter';
describe('Hello function', () => {
it('should return empty test', () => {
let service = new GBImporter(null);
const service = new GBImporter(null);
//service.importIfNotExistsBotPackage(null, null);
const result = 0;
expect(result).to.equal(0);

View file

@ -36,13 +36,13 @@
'use strict';
import { CSService } from "../services/CSService";
import { AzureText } from "pragmatismo-io-framework";
import { GBMinInstance } from "botlib";
import { IGBDialog } from "botlib";
import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
import { WaterfallDialog } from "botbuilder-dialogs";
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance } from 'botlib';
import { IGBDialog } from 'botlib';
import { AzureText } from 'pragmatismo-io-framework';
import { CSService } from '../services/CSService';
import { Messages } from '../strings';
export class FeedbackDialog extends IGBDialog {
/**
@ -51,13 +51,13 @@ export class FeedbackDialog extends IGBDialog {
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService();
min.dialogs.add(
new WaterfallDialog("/feedbackNumber", [
new WaterfallDialog('/feedbackNumber', [
async step => {
let locale = step.context.activity.locale;
const locale = step.context.activity.locale;
// TODO: Migrate to 4.*+ await step.prompt("choicePrompt", Messages[locale].what_about_me, [
// "1",
// "2",
@ -68,8 +68,8 @@ export class FeedbackDialog extends IGBDialog {
return await step.next();
},
async step => {
let locale = step.context.activity.locale;
let rate = step.result.entity;
const locale = step.context.activity.locale;
const rate = step.result.entity;
const user = await min.userProfile.get(context, {});
await service.updateConversationRate(user.conversation, rate);
await step.context.sendActivity(Messages[locale].thanks);
@ -78,19 +78,19 @@ export class FeedbackDialog extends IGBDialog {
])
);
min.dialogs.add(new WaterfallDialog("/feedback", [
min.dialogs.add(new WaterfallDialog('/feedback', [
async step => {
let locale = step.context.activity.locale;
const locale = step.context.activity.locale;
if (step.result.fromMenu) {
await step.context.sendActivity(Messages[locale].about_suggestions);
}
await step.prompt("textPrompt", Messages[locale].what_about_service);
await step.prompt('textPrompt', Messages[locale].what_about_service);
return await step.next();
},
async step => {
let locale = step.context.activity.locale;
let rate = await AzureText.getSentiment(
const locale = step.context.activity.locale;
const rate = await AzureText.getSentiment(
min.instance.textAnalyticsKey,
min.instance.textAnalyticsEndpoint,
min.conversationalService.getCurrentLanguage(step),
@ -104,7 +104,7 @@ export class FeedbackDialog extends IGBDialog {
// TODO: Record.
}
await step.replaceDialog("/ask", { isReturning: true });
await step.replaceDialog('/ask', { isReturning: true });
return await step.next();
}
]));

View file

@ -36,14 +36,14 @@
'use strict';
import { IGBDialog } from "botlib";
import { IGBDialog } from 'botlib';
import { GBMinInstance } from "botlib";
import { CSService } from "../services/CSService";
import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
import { WaterfallDialog } from "botbuilder-dialogs";
const logger = require("../../../src/logger");
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance } from 'botlib';
import { CSService } from '../services/CSService';
import { Messages } from '../strings';
const logger = require('../../../src/logger');
export class QualityDialog extends IGBDialog {
/**
@ -52,18 +52,18 @@ export class QualityDialog extends IGBDialog {
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService();
min.dialogs.add( new WaterfallDialog("/quality", [
min.dialogs.add(new WaterfallDialog('/quality', [
async step => {
const locale = step.context.activity.locale;
const user = await min.userProfile.get(context, {});
var score = step.result;
const score = step.result;
setTimeout(
() => min.conversationalService.sendEvent(step, "stop", null),
() => min.conversationalService.sendEvent(step, 'stop', null),
400
);
@ -77,7 +77,7 @@ export class QualityDialog extends IGBDialog {
user.lastQuestion,
user.lastQuestionId
);
await step.replaceDialog("/ask", { isReturning: true });
await step.replaceDialog('/ask', { isReturning: true });
}
return await step.next();
}

View file

@ -34,34 +34,34 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
import { GuaribasQuestionAlternate } from './models/index'
import { QualityDialog } from './dialogs/QualityDialog'
import { FeedbackDialog } from './dialogs/FeedbackDialog'
import { GBMinInstance, IGBPackage, IGBCoreService } from "botlib"
const UrlJoin = require('url-join');
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { FeedbackDialog } from './dialogs/FeedbackDialog';
import { QualityDialog } from './dialogs/QualityDialog';
import { GuaribasQuestionAlternate } from './models/index';
import { Sequelize } from 'sequelize-typescript'
import { Sequelize } from 'sequelize-typescript';
export class GBCustomerSatisfactionPackage implements IGBPackage {
sysPackages: IGBPackage[] = null
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public sysPackages: IGBPackage[] = null;
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
core.sequelize.addModels([
GuaribasQuestionAlternate
])
]);
}
unloadPackage(core: IGBCoreService): void {
public unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
FeedbackDialog.setup(min.bot, min)
QualityDialog.setup(min.bot, min)
public loadBot(min: GBMinInstance): void {
FeedbackDialog.setup(min.bot, min);
QualityDialog.setup(min.bot, min);
}
unloadBot(min: GBMinInstance): void {
public unloadBot(min: GBMinInstance): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -34,34 +34,34 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
import {
DataTypes,
DataTypeUUIDv4,
DataTypeDate,
DataTypeDecimal
} from "sequelize"
DataTypeDecimal,
DataTypes,
DataTypeUUIDv4
} from 'sequelize';
import {
Sequelize,
Table,
Column,
Model,
HasMany,
AutoIncrement,
BelongsTo,
BelongsToMany,
Length,
ForeignKey,
Column,
CreatedAt,
UpdatedAt,
DataType,
ForeignKey,
HasMany,
IsUUID,
Length,
Model,
PrimaryKey,
AutoIncrement
} from "sequelize-typescript"
Sequelize,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { GuaribasInstance } from "../../core.gbapp/models/GBModel"
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
@Table
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
@ -69,16 +69,16 @@ export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate>
@PrimaryKey
@AutoIncrement
@Column
quickAnswerId: number
public quickAnswerId: number;
@Column questionTyped: string
@Column public questionTyped: string;
@Column questionText: string
@Column public questionText: string;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number
public instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance
public instance: GuaribasInstance;
}

View file

@ -30,12 +30,12 @@
| |
\*****************************************************************************/
import { GuaribasQuestionAlternate } from '../models'
import { GuaribasConversation } from '../../analytics.gblib/models'
import { GuaribasConversation } from '../../analytics.gblib/models';
import { GuaribasQuestionAlternate } from '../models';
export class CSService {
async resolveQuestionAlternate(
public async resolveQuestionAlternate(
instanceId: number,
questionTyped: string): Promise<GuaribasQuestionAlternate> {
@ -44,24 +44,24 @@ export class CSService {
instanceId: instanceId,
questionTyped: questionTyped
}
})
});
}
async insertQuestionAlternate(
public async insertQuestionAlternate(
instanceId: number,
questionTyped: string,
questionText: string): Promise<GuaribasQuestionAlternate> {
return GuaribasQuestionAlternate.create({
questionTyped: questionTyped,
questionText: questionText
})
});
}
async updateConversationRate(
public async updateConversationRate(
conversation: GuaribasConversation,
rate: number
): Promise<GuaribasConversation> {
conversation.rate = rate
return conversation.save()
conversation.rate = rate;
return conversation.save();
}
}

View file

@ -1,22 +1,22 @@
export const Messages = {
"en-US": {
about_suggestions: "Suggestions are welcomed and improve my quality...",
what_about_service: "What about my service?",
glad_you_liked: "I'm glad you liked. I'm here for you.",
we_will_improve: "Let's take note of that, thanks for sharing.",
what_about_me: "What about the service, please rate between 1 and 5.",
thanks: "Thanks!",
im_sorry_lets_try: "I'm sorry. Let's try again...",
great_thanks: "Great, thanks for sharing your thoughts."
'en-US': {
about_suggestions: 'Suggestions are welcomed and improve my quality...',
what_about_service: 'What about my service?',
glad_you_liked: 'I\'m glad you liked. I\'m here for you.',
we_will_improve: 'Let\'s take note of that, thanks for sharing.',
what_about_me: 'What about the service, please rate between 1 and 5.',
thanks: 'Thanks!',
im_sorry_lets_try: 'I\'m sorry. Let\'s try again...',
great_thanks: 'Great, thanks for sharing your thoughts.'
},
"pt-BR": {
about_suggestions: "Sugestões melhoram muito minha qualidade...",
what_about_service:"O que achou do meu atendimento?",
glad_you_liked: "Bom saber que você gostou. Conte comigo.",
we_will_improve: "Vamos registrar sua questão, obrigado pela sinceridade.",
what_about_me: "O que achou do meu atendimento, de 1 a 5?",
thanks: "Obrigado!",
im_sorry_lets_try: "Desculpe-me, vamos tentar novamente.",
great_thanks: "Ótimo, obrigado por contribuir com sua resposta."
'pt-BR': {
about_suggestions: 'Sugestões melhoram muito minha qualidade...',
what_about_service: 'O que achou do meu atendimento?',
glad_you_liked: 'Bom saber que você gostou. Conte comigo.',
we_will_improve: 'Vamos registrar sua questão, obrigado pela sinceridade.',
what_about_me: 'O que achou do meu atendimento, de 1 a 5?',
thanks: 'Obrigado!',
im_sorry_lets_try: 'Desculpe-me, vamos tentar novamente.',
great_thanks: 'Ótimo, obrigado por contribuir com sua resposta.'
}
};

View file

@ -36,15 +36,15 @@
'use strict';
import { IGBDialog } from "botlib";
import { AzureText } from "pragmatismo-io-framework";
import { GBMinInstance } from "botlib";
import { KBService } from "./../services/KBService";
import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
import { WaterfallDialog } from "botbuilder-dialogs";
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { IGBDialog } from 'botlib';
import { GBMinInstance } from 'botlib';
import { AzureText } from 'pragmatismo-io-framework';
import { Messages } from '../strings';
import { KBService } from './../services/KBService';
const logger = require("../../../src/logger");
const logger = require('../../../src/logger');
export class AskDialog extends IGBDialog {
/**
@ -53,18 +53,18 @@ export class AskDialog extends IGBDialog {
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
static setup(bot: BotAdapter, min: GBMinInstance) {
public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(min.core.sequelize);
min.dialogs.add(
new WaterfallDialog("/answerEvent", [
new WaterfallDialog('/answerEvent', [
async step => {
if (step.options && step.options["questionId"]) {
let question = await service.getQuestionById(
if (step.options && step.options.questionId) {
const question = await service.getQuestionById(
min.instance.instanceId,
step.options["questionId"]
step.options.questionId
);
let answer = await service.getAnswerById(
const answer = await service.getAnswerById(
min.instance.instanceId,
question.answerId
);
@ -73,7 +73,7 @@ export class AskDialog extends IGBDialog {
await service.sendAnswer(min.conversationalService, step, answer);
await step.replaceDialog("/ask", { isReturning: true });
await step.replaceDialog('/ask', { isReturning: true });
}
return await step.next();
}
@ -81,32 +81,32 @@ export class AskDialog extends IGBDialog {
);
min.dialogs.add(
new WaterfallDialog("/answer", [
new WaterfallDialog('/answer', [
async step => {
const user = await min.userProfile.get(step.context, {});
let text = step.options["query"];
let text = step.options.query;
if (!text) {
throw new Error(`/answer being called with no args.query text.`);
}
let locale = step.context.activity.locale;
const locale = step.context.activity.locale;
// Stops any content on projector.
await min.conversationalService.sendEvent(step, "stop", null);
await min.conversationalService.sendEvent(step, 'stop', null);
// Handle extra text from FAQ.
if (step.options && step.options["query"]) {
text = step.options["query"];
} else if (step.options && step.options["fromFaq"]) {
if (step.options && step.options.query) {
text = step.options.query;
} else if (step.options && step.options.fromFaq) {
await step.context.sendActivity(Messages[locale].going_answer);
}
// Spells check the input text before sending Search or NLP.
if (min.instance.spellcheckerKey) {
let data = await AzureText.getSpelledText(
const data = await AzureText.getSpelledText(
min.instance.spellcheckerKey,
text
);
@ -121,7 +121,7 @@ export class AskDialog extends IGBDialog {
user.lastQuestion = text;
await min.userProfile.set(step.context, user);
let resultsA = await service.ask(
const resultsA = await service.ask(
min.instance,
text,
min.instance.searchScore,
@ -147,11 +147,11 @@ export class AskDialog extends IGBDialog {
// Goes to ask loop, again.
return await step.replaceDialog("/ask", { isReturning: true });
return await step.replaceDialog('/ask', { isReturning: true });
} else {
// Second time running Search, now with no filter.
let resultsB = await service.ask(
const resultsB = await service.ask(
min.instance,
text,
min.instance.searchScore,
@ -172,7 +172,7 @@ export class AskDialog extends IGBDialog {
// Informs user that a broader search will be used.
if (user.subjects.length > 0) {
let subjectText = `${KBService.getSubjectItemsSeparatedBySpaces(
const subjectText = `${KBService.getSubjectItemsSeparatedBySpaces(
user.subjects
)}`;
await step.context.sendActivity(Messages[locale].wider_answer);
@ -185,13 +185,13 @@ export class AskDialog extends IGBDialog {
step,
resultsB.answer
);
return await step.replaceDialog("/ask", { isReturning: true });
return await step.replaceDialog('/ask', { isReturning: true });
} else {
if (
!(await min.conversationalService.routeNLP(step, min, text))
) {
await step.context.sendActivity(Messages[locale].did_not_find);
return await step.replaceDialog("/ask", { isReturning: true });
return await step.replaceDialog('/ask', { isReturning: true });
}
}
}
@ -200,7 +200,7 @@ export class AskDialog extends IGBDialog {
);
min.dialogs.add(
new WaterfallDialog("/ask", [
new WaterfallDialog('/ask', [
async step => {
const locale = step.context.activity.locale;
const user = await min.userProfile.get(step.context, {});
@ -212,23 +212,23 @@ export class AskDialog extends IGBDialog {
// Three forms of asking.
if (step.options && step.options["firstTime"]) {
if (step.options && step.options.firstTime) {
text = Messages[locale].ask_first_time;
} else if (step.options && step.options["isReturning"]) {
} else if (step.options && step.options.isReturning) {
text = Messages[locale].anything_else;
} else if (user.subjects.length > 0) {
text = Messages[locale].which_question;
} else {
throw new Error("Invalid use of /ask");
throw new Error('Invalid use of /ask');
}
if (text.length > 0) {
return await step.prompt("textPrompt", text);
return await step.prompt('textPrompt', text);
}
return await step.next();
},
async step => {
return await step.replaceDialog("/answer", { query: step.result });
return await step.replaceDialog('/answer', { query: step.result });
}
])
);

View file

@ -34,41 +34,41 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
import { KBService } from './../services/KBService'
import { IGBDialog } from "botlib"
import { BotAdapter } from "botbuilder"
import { Messages } from "../strings";
import { GBMinInstance } from "botlib"
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { IGBDialog } from 'botlib';
import { GBMinInstance } from 'botlib';
import { Messages } from '../strings';
import { KBService } from './../services/KBService';
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) {
public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(min.core.sequelize)
const service = new KBService(min.core.sequelize);
min.dialogs.add(new WaterfallDialog("/faq", [
min.dialogs.add(new WaterfallDialog('/faq', [
async step => {
let data = await service.getFaqBySubjectArray("faq", null)
const data = await service.getFaqBySubjectArray('faq', null);
const locale = step.context.activity.locale;
if (data) {
await min.conversationalService.sendEvent(step, "play", {
playerType: "bullet",
await min.conversationalService.sendEvent(step, 'play', {
playerType: 'bullet',
data: data.slice(0, 10)
})
await step.context.sendActivity(Messages[locale].see_faq) // TODO: RND messages.
await step.endDialog()
});
await step.context.sendActivity(Messages[locale].see_faq); // TODO: RND messages.
await step.endDialog();
return await step.next();
}
}
]))
]));
}
}

View file

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

View file

@ -34,45 +34,45 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
const UrlJoin = require('url-join');
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index'
import { GBMinInstance, IGBPackage } from "botlib"
import { GBMinInstance, IGBPackage } from 'botlib';
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
import { AskDialog } from "./dialogs/AskDialog"
import { FaqDialog } from "./dialogs/FaqDialog"
import { MenuDialog } from "./dialogs/MenuDialog"
import { Sequelize } from 'sequelize-typescript'
import { IGBCoreService } from 'botlib'
import { IGBCoreService } from 'botlib';
import { Sequelize } from 'sequelize-typescript';
import { AskDialog } from './dialogs/AskDialog';
import { FaqDialog } from './dialogs/FaqDialog';
import { MenuDialog } from './dialogs/MenuDialog';
export class GBKBPackage implements IGBPackage {
sysPackages: IGBPackage[] = null
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public sysPackages: IGBPackage[] = null;
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
core.sequelize.addModels([
GuaribasAnswer,
GuaribasQuestion,
GuaribasSubject
])
}
unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
]);
AskDialog.setup(min.bot, min)
FaqDialog.setup(min.bot, min)
MenuDialog.setup(min.bot, min)
}
unloadBot(min: GBMinInstance): void {
public unloadPackage(core: IGBCoreService): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public loadBot(min: GBMinInstance): void {
AskDialog.setup(min.bot, min);
FaqDialog.setup(min.bot, min);
MenuDialog.setup(min.bot, min);
}
public unloadBot(min: GBMinInstance): void {
}
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -30,40 +30,57 @@
| |
\*****************************************************************************/
const logger = require("../../../src/logger")
const Path = require("path")
const Fs = require("fs")
const promise = require('bluebird')
const parse = promise.promisify(require('csv-parse'))
const UrlJoin = require("url-join")
const marked = require("marked")
const path = require("path")
const asyncPromise = require('async-promises')
const walkPromise = require('walk-promise')
import { Messages } from "../strings";
const logger = require('../../../src/logger');
const Path = require('path');
const Fs = require('fs');
const promise = require('bluebird');
const parse = promise.promisify(require('csv-parse'));
const UrlJoin = require('url-join');
const marked = require('marked');
const path = require('path');
const asyncPromise = require('async-promises');
const walkPromise = require('walk-promise');
import { Messages } from '../strings';
import { Sequelize } from 'sequelize-typescript'
import { GBConfigService } from './../../core.gbapp/services/GBConfigService'
import { GuaribasQuestion, GuaribasAnswer, GuaribasSubject } from "../models"
import { IGBCoreService, IGBConversationalService, IGBInstance } from "botlib"
import { AzureSearch } from "pragmatismo-io-framework"
import { GBDeployer } from "../../core.gbapp/services/GBDeployer"
import { GuaribasPackage } from "../../core.gbapp/models/GBModel"
import { IGBConversationalService, IGBCoreService, IGBInstance } from 'botlib';
import { AzureSearch } from 'pragmatismo-io-framework';
import { Sequelize } from 'sequelize-typescript';
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
export class KBServiceSearchResults {
answer: GuaribasAnswer
questionId: number
public answer: GuaribasAnswer;
public questionId: number;
}
export class KBService {
sequelize: Sequelize
public sequelize: Sequelize;
constructor(sequelize: Sequelize) {
this.sequelize = sequelize
this.sequelize = sequelize;
}
async getQuestionById(
public static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
if (!subjects) { return ''; }
const out = [];
subjects.forEach(subject => {
out.push(subject.title);
});
return out.join(', ');
}
public static getSubjectItemsSeparatedBySpaces(subjects: GuaribasSubject[]) {
const out = [];
subjects.forEach(subject => {
out.push(subject.internalId);
});
return out.join(' ');
}
public async getQuestionById(
instanceId: number,
questionId: number
): Promise<GuaribasQuestion> {
@ -72,10 +89,10 @@ export class KBService {
instanceId: instanceId,
questionId: questionId
}
})
});
}
async getAnswerById(
public async getAnswerById(
instanceId: number,
answerId: number
): Promise<GuaribasAnswer> {
@ -84,47 +101,47 @@ export class KBService {
instanceId: instanceId,
answerId: answerId
}
})
});
}
async getAnswerByText(
public async getAnswerByText(
instanceId: number,
text: string
): Promise<any> {
const Op = Sequelize.Op
const Op = Sequelize.Op;
let question = await GuaribasQuestion.findOne({
const question = await GuaribasQuestion.findOne({
where: {
instanceId: instanceId,
content: { [Op.like]: `%${text.trim()}%` }
}
})
});
if (question) {
let answer = await GuaribasAnswer.findOne({
const answer = await GuaribasAnswer.findOne({
where: {
instanceId: instanceId,
answerId: question.answerId
}
})
return Promise.resolve({ question: question, answer: answer })
});
return Promise.resolve({ question: question, answer: answer });
}
return Promise.resolve(null)
return Promise.resolve(null);
}
async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
public async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
return new Promise<GuaribasAnswer>(
(resolve, reject) => {
GuaribasAnswer.create(obj).then(item => {
resolve(item)
resolve(item);
}).error((reason) => {
reject(reason)
})
})
reject(reason);
});
});
}
async ask(
public async ask(
instance: IGBInstance,
query: string,
searchScore: number,
@ -133,185 +150,165 @@ export class KBService {
// Builds search query.
query = query.toLowerCase()
query = query.replace("?", " ")
query = query.replace("!", " ")
query = query.replace(".", " ")
query = query.replace("/", " ")
query = query.replace("\\", " ")
query = query.toLowerCase();
query = query.replace('?', ' ');
query = query.replace('!', ' ');
query = query.replace('.', ' ');
query = query.replace('/', ' ');
query = query.replace('\\', ' ');
if (subjects) {
let text = KBService.getSubjectItemsSeparatedBySpaces(subjects)
const text = KBService.getSubjectItemsSeparatedBySpaces(subjects);
if (text) {
query = `${query} ${text}`
query = `${query} ${text}`;
}
}
// TODO: Filter by instance. what = `${what}&$filter=instanceId eq ${instanceId}`
try {
if (instance.searchKey && GBConfigService.get("STORAGE_DIALECT") == "mssql") {
let service = new AzureSearch(
if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') == 'mssql') {
const service = new AzureSearch(
instance.searchKey,
instance.searchHost,
instance.searchIndex,
instance.searchIndexer
)
let results = await service.search(query)
);
const results = await service.search(query);
if (results && results.length > 0 &&
results[0]["@search.score"] >= searchScore) {
let value = await this.getAnswerById(
results[0]['@search.score'] >= searchScore) {
const value = await this.getAnswerById(
instance.instanceId,
results[0].answerId)
results[0].answerId);
if (value) {
return Promise.resolve({ answer: value, questionId: results[0].questionId })
}
else {
return Promise.resolve({ answer: null, questionId: 0 })
return Promise.resolve({ answer: value, questionId: results[0].questionId });
} else {
return Promise.resolve({ answer: null, questionId: 0 });
}
}
} else {
let data = await this.getAnswerByText(instance.instanceId, query)
const data = await this.getAnswerByText(instance.instanceId, query);
if (data) {
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 })
return Promise.resolve({ answer: null, questionId: 0 });
}
}
}
catch (reason) {
} catch (reason) {
return Promise.reject(new Error(reason));
}
}
static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
if (!subjects) return ""
let out = []
subjects.forEach(subject => {
out.push(subject.title)
})
return out.join(", ")
}
static getSubjectItemsSeparatedBySpaces(subjects: GuaribasSubject[]) {
let out = []
subjects.forEach(subject => {
out.push(subject.internalId)
})
return out.join(" ")
}
async getSubjectItems(
public async getSubjectItems(
instanceId: number,
parentId: number
): Promise<GuaribasSubject[]> {
var where = { parentSubjectId: parentId, instanceId: instanceId }
const where = { parentSubjectId: parentId, instanceId: instanceId };
return GuaribasSubject.findAll({
where: where
})
});
}
async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
let where = {
public async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
const where = {
from: from
}
};
if (subjects) {
if (subjects[0]) {
where["subject1"] = subjects[0].internalId
where.subject1 = subjects[0].internalId;
}
if (subjects[1]) {
where["subject2"] = subjects[1].internalId
where.subject2 = subjects[1].internalId;
}
if (subjects[2]) {
where["subject3"] = subjects[2].internalId
where.subject3 = subjects[2].internalId;
}
if (subjects[3]) {
where["subject4"] = subjects[3].internalId
where.subject4 = subjects[3].internalId;
}
}
return await GuaribasQuestion.findAll({
where: where
})
});
}
async importKbTabularFile(
public async importKbTabularFile(
filePath: string,
instanceId: number,
packageId: number
): Promise<GuaribasQuestion[]> {
let file = Fs.readFileSync(filePath, "UCS-2")
let opts = {
delimiter: "\t"
}
const file = Fs.readFileSync(filePath, 'UCS-2');
const opts = {
delimiter: '\t'
};
let lastQuestion: GuaribasQuestion;
let lastAnswer: GuaribasAnswer;
let data = await parse(file, opts)
const data = await parse(file, opts);
return asyncPromise.eachSeries(data, async line => {
// Extracts values from columns in the current line.
let subjectsText = line[0]
var from = line[1]
var to = line[2]
var question = line[3]
var answer = line[4]
const subjectsText = line[0];
const from = line[1];
const to = line[2];
const question = line[3];
let answer = line[4];
// Skips the first line.
if (!(subjectsText === "subjects" && from == "from")) {
let format = ".txt"
if (!(subjectsText === 'subjects' && from == 'from')) {
let format = '.txt';
// Extracts answer from external media if any.
if (answer.indexOf(".md") > -1) {
let mediaFilename = UrlJoin(path.dirname(filePath), "..", "articles", answer)
if (answer.indexOf('.md') > -1) {
const mediaFilename = UrlJoin(path.dirname(filePath), '..', 'articles', answer);
if (Fs.existsSync(mediaFilename)) {
answer = Fs.readFileSync(mediaFilename, "utf8")
format = ".md"
answer = Fs.readFileSync(mediaFilename, 'utf8');
format = '.md';
} else {
logger.info(`[GBImporter] File not found: ${mediaFilename}.`)
answer = ""
logger.info(`[GBImporter] File not found: ${mediaFilename}.`);
answer = '';
}
}
// Processes subjects hierarchy splitting by dots.
let subjectArray = subjectsText.split(".")
const subjectArray = subjectsText.split('.');
let subject1: string, subject2: string, subject3: string,
subject4: string
var indexer = 0
subject4: string;
let indexer = 0;
subjectArray.forEach(element => {
if (indexer == 0) {
subject1 = subjectArray[indexer].substring(0, 63)
subject1 = subjectArray[indexer].substring(0, 63);
} else if (indexer == 1) {
subject2 = subjectArray[indexer].substring(0, 63)
subject2 = subjectArray[indexer].substring(0, 63);
} else if (indexer == 2) {
subject3 = subjectArray[indexer].substring(0, 63)
subject3 = subjectArray[indexer].substring(0, 63);
} else if (indexer == 3) {
subject4 = subjectArray[indexer].substring(0, 63)
subject4 = subjectArray[indexer].substring(0, 63);
}
indexer++
})
indexer++;
});
// Now with all the data ready, creates entities in the store.
let answer1 = await GuaribasAnswer.create({
const answer1 = await GuaribasAnswer.create({
instanceId: instanceId,
content: answer,
format: format,
packageId: packageId,
prevId: lastQuestion ? lastQuestion.questionId : 0,
})
prevId: lastQuestion ? lastQuestion.questionId : 0
});
let question1 = await GuaribasQuestion.create({
const question1 = await GuaribasQuestion.create({
from: from,
to: to,
subject1: subject1,
@ -322,41 +319,41 @@ export class KBService {
instanceId: instanceId,
answerId: answer1.answerId,
packageId: packageId
})
});
if (lastAnswer && lastQuestion) {
await lastAnswer.updateAttributes({ nextId: lastQuestion.questionId })
await lastAnswer.updateAttributes({ nextId: lastQuestion.questionId });
}
lastAnswer = answer1
lastQuestion = question1
lastAnswer = answer1;
lastQuestion = question1;
return Promise.resolve(lastQuestion)
return Promise.resolve(lastQuestion);
} else {
// Skips the header.
return Promise.resolve(null)
return Promise.resolve(null);
}
})
});
}
async sendAnswer(conversationalService: IGBConversationalService,
step: any, answer: GuaribasAnswer) {
public async sendAnswer(conversationalService: IGBConversationalService,
step: any, answer: GuaribasAnswer) {
if (answer.content.endsWith('.mp4')) {
await conversationalService.sendEvent(step, "play", {
playerType: "video",
await conversationalService.sendEvent(step, 'play', {
playerType: 'video',
data: answer.content
})
});
} else if (answer.content.length > 140 &&
step.context._activity.channelId === "webchat") {
step.context._activity.channelId === 'webchat') {
const locale = step.context.activity.locale;
await step.context.sendActivity(Messages[locale].will_answer_projector) // TODO: Handle rnd.
var html = answer.content
await step.context.sendActivity(Messages[locale].will_answer_projector); // TODO: Handle rnd.
let html = answer.content;
if (answer.format === ".md") {
if (answer.format === '.md') {
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
@ -367,23 +364,23 @@ export class KBService {
smartLists: true,
smartypants: false,
xhtml: false
})
html = marked(answer.content)
});
html = marked(answer.content);
}
await conversationalService.sendEvent(step, "play",
{
playerType: "markdown", data: {
await conversationalService.sendEvent(step, 'play',
{
playerType: 'markdown', data: {
content: html, answer: answer,
prevId: answer.prevId, nextId: answer.nextId
}
})
});
} else {
await step.context.sendActivity(answer.content)
await conversationalService.sendEvent(step, "stop", null)
await step.context.sendActivity(answer.content);
await conversationalService.sendEvent(step, 'stop', null);
}
}
async importKbPackage(
public async importKbPackage(
localPath: string,
packageStorage: GuaribasPackage,
instance: IGBInstance
@ -393,8 +390,8 @@ export class KBService {
await this.importSubjectFile(
packageStorage.packageId,
UrlJoin(localPath, "subjects.json"),
instance)
UrlJoin(localPath, 'subjects.json'),
instance);
// Import all .tsv files in the tabular directory.
@ -402,41 +399,40 @@ export class KBService {
localPath,
instance,
packageStorage.packageId
)
);
}
async importKbTabularDirectory(
public async importKbTabularDirectory(
localPath: string,
instance: IGBInstance,
packageId: number
): Promise<any> {
let files = await walkPromise(UrlJoin(localPath, "tabular"))
const files = await walkPromise(UrlJoin(localPath, 'tabular'));
return Promise.all(files.map(async file => {
if (file.name.endsWith(".tsv")) {
if (file.name.endsWith('.tsv')) {
return this.importKbTabularFile(
UrlJoin(file.root, file.name),
instance.instanceId,
packageId)
packageId);
}
}))
}));
}
async importSubjectFile(
public async importSubjectFile(
packageId: number,
filename: string,
instance: IGBInstance
): Promise<any> {
var subjects = JSON.parse(Fs.readFileSync(filename, "utf8"))
const subjects = JSON.parse(Fs.readFileSync(filename, 'utf8'));
const doIt = async (subjects: GuaribasSubject[], parentSubjectId: number) => {
return asyncPromise.eachSeries(subjects, async item => {
let mediaFilename = item.id + ".png"
const mediaFilename = item.id + '.png';
let value = await GuaribasSubject.create({
const value = await GuaribasSubject.create({
internalId: item.id,
parentSubjectId: parentSubjectId,
instanceId: instance.instanceId,
@ -445,20 +441,19 @@ export class KBService {
title: item.title,
description: item.description,
packageId: packageId
})
});
if (item.children) {
return Promise.resolve(doIt(item.children, value.subjectId))
return Promise.resolve(doIt(item.children, value.subjectId));
} else {
return Promise.resolve(item);
}
else {
return Promise.resolve(item)
}
})
}
return doIt(subjects.children, null)
});
};
return doIt(subjects.children, null);
}
async undeployKbFromStorage(
public async undeployKbFromStorage(
instance: IGBInstance,
deployer: GBDeployer,
packageId: number
@ -466,41 +461,41 @@ export class KBService {
await GuaribasQuestion.destroy({
where: { instanceId: instance.instanceId, packageId: packageId }
})
});
await GuaribasAnswer.destroy({
where: { instanceId: instance.instanceId, packageId: packageId }
})
});
await GuaribasSubject.destroy({
where: { instanceId: instance.instanceId, packageId: packageId }
})
});
await GuaribasPackage.destroy({
where: { instanceId: instance.instanceId, packageId: packageId }
})
});
await deployer.rebuildIndex(instance)
await deployer.rebuildIndex(instance);
}
/**
* Deploys a knowledge base to the storage using the .gbkb format.
*
*
* @param localPath Path to the .gbkb folder.
*/
async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
let packageType = Path.extname(localPath)
let packageName = Path.basename(localPath)
logger.info(`[GBDeployer] Opening package: ${localPath}`)
let packageObject = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "package.json"), "utf8")
)
public async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
const packageType = Path.extname(localPath);
const packageName = Path.basename(localPath);
logger.info(`[GBDeployer] Opening package: ${localPath}`);
const packageObject = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8')
);
let instance = await core.loadInstance(packageObject.botId)
logger.info(`[GBDeployer] Importing: ${localPath}`)
let p = await deployer.deployPackageToStorage(
const instance = await core.loadInstance(packageObject.botId);
logger.info(`[GBDeployer] Importing: ${localPath}`);
const p = await deployer.deployPackageToStorage(
instance.instanceId,
packageName)
await this.importKbPackage(localPath, p, instance)
packageName);
await this.importKbPackage(localPath, p, instance);
deployer.rebuildIndex(instance)
logger.info(`[GBDeployer] Finished import of ${localPath}`)
deployer.rebuildIndex(instance);
logger.info(`[GBDeployer] Finished import of ${localPath}`);
}
}

View file

@ -1,38 +1,38 @@
export const Messages = {
"en-US": {
did_not_find: "I'm sorry I didn't find anything.",
changing_language: "OK, changing language to English...",
going_answer: "Great choice, now looking for your answer...",
'en-US': {
did_not_find: 'I\'m sorry I didn\'t find anything.',
changing_language: 'OK, changing language to English...',
going_answer: 'Great choice, now looking for your answer...',
wider_answer: subjectText =>
`Answering to you in a broader way... Not just about ${subjectText}.`,
which_question: "What's your question?",
anything_else: "So, may I help with anything else?",
here_is_subjects: "Here are some subjects to choose from...",
menu_select: "Select",
which_question: 'What\'s your question?',
anything_else: 'So, may I help with anything else?',
here_is_subjects: 'Here are some subjects to choose from...',
menu_select: 'Select',
lets_search: query =>
`Vamos pesquisar sobre ${query}... O que deseja saber?`,
see_faq:
"Please take a look at the FAQ I've prepared for you. You can click on them to get the answer.",
'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
will_answer_projector:
"I'll answer on the projector to a better experience...",
ask_first_time: "What are you looking for?"
'I\'ll answer on the projector to a better experience...',
ask_first_time: 'What are you looking for?'
},
"pt-BR": {
did_not_find: "Desculpe-me, não encontrei nada a respeito.",
changing_language: "OK, mundando de idioma para o Português...",
going_answer: "Ótima escolha, procurando resposta para sua questão...",
'pt-BR': {
did_not_find: 'Desculpe-me, não encontrei nada a respeito.',
changing_language: 'OK, mundando de idioma para o Português...',
going_answer: 'Ótima escolha, procurando resposta para sua questão...',
wider_answer: subjectText =>
`Vou te responder de modo mais abrangente... Não apenas sobre ${subjectText}`,
which_question: "Qual a pergunta?",
anything_else: "Então, posso ajudar em algo a mais?",
here_is_subjects: "Aqui estão algumas categorias de assuntos...",
menu_select: "Selecionar",
which_question: 'Qual a pergunta?',
anything_else: 'Então, posso ajudar em algo a mais?',
here_is_subjects: 'Aqui estão algumas categorias de assuntos...',
menu_select: 'Selecionar',
lets_search: query =>
`Let's search about ${query}... What do you want to know?`,
see_faq:
"Veja algumas perguntas mais frequentes logo na tela. Clique numa delas para eu responder.",
'Veja algumas perguntas mais frequentes logo na tela. Clique numa delas para eu responder.',
will_answer_projector:
"Vou te responder na tela para melhor visualização...",
ask_first_time: "Sobre como eu poderia ajudar?"
'Vou te responder na tela para melhor visualização...',
ask_first_time: 'Sobre como eu poderia ajudar?'
}
};

View file

@ -34,40 +34,39 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
const UrlJoin = require('url-join');
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { GBMinInstance, IGBPackage, IGBCoreService } from "botlib"
import { Sequelize } from "sequelize-typescript"
import { GuaribasUser, GuaribasGroup, GuaribasUserGroup } from "./models"
import { Sequelize } from 'sequelize-typescript';
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
export class GBSecurityPackage implements IGBPackage {
sysPackages: IGBPackage[] = null
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public sysPackages: IGBPackage[] = null;
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
core.sequelize.addModels([
GuaribasGroup,
GuaribasUser,
GuaribasUserGroup
])
]);
core
core;
}
unloadPackage(core: IGBCoreService): void {
public unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
public loadBot(min: GBMinInstance): void {
}
unloadBot(min: GBMinInstance): void {
public unloadBot(min: GBMinInstance): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -34,60 +34,60 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
import {
DataTypes,
DataTypeUUIDv4,
DataTypeDate,
DataTypeDecimal
} from "sequelize"
DataTypeDecimal,
DataTypes,
DataTypeUUIDv4
} from 'sequelize';
import {
Sequelize,
Table,
Column,
Model,
HasMany,
AutoIncrement,
BelongsTo,
BelongsToMany,
Length,
ForeignKey,
Column,
CreatedAt,
UpdatedAt,
DataType,
ForeignKey,
HasMany,
IsUUID,
Length,
Model,
PrimaryKey,
AutoIncrement
} from "sequelize-typescript"
Sequelize,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { GuaribasInstance } from "../../core.gbapp/models/GBModel"
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
@Table
export class GuaribasUser extends Model<GuaribasUser> {
@PrimaryKey
@AutoIncrement
@Column
userId: number
public userId: number;
@Column displayName: string
@Column public displayName: string;
@Column userSystemId: string
@Column userName: string
@Column public userSystemId: string;
@Column public userName: string;
@Column defaultChannel: string
@Column public defaultChannel: string;
@Column email: string
@Column public email: string;
@Column(DataType.STRING(512))
internalAddress: string
public internalAddress: string;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number
public instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance
public instance: GuaribasInstance;
}
@Table
@ -95,40 +95,40 @@ export class GuaribasGroup extends Model<GuaribasGroup> {
@PrimaryKey
@AutoIncrement
@Column
groupId: number
public groupId: number;
@Length({ min: 0, max: 512 })
@Column
displayName: string
public displayName: string;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number
public instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance
public instance: GuaribasInstance;
}
@Table
export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
@ForeignKey(() => GuaribasUser)
@Column
userId: number
public userId: number;
@ForeignKey(() => GuaribasGroup)
@Column
groupId: number
public groupId: number;
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number
public instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance
public instance: GuaribasInstance;
@BelongsTo(() => GuaribasGroup)
group: GuaribasGroup
public group: GuaribasGroup;
@BelongsTo(() => GuaribasUser)
user: GuaribasUser
public user: GuaribasUser;
}

View file

@ -30,48 +30,48 @@
| |
\*****************************************************************************/
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 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');
import { GBServiceCallback, GBService, IGBInstance } from "botlib"
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from "../models"
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
export class SecService extends GBService {
async importSecurityFile(localPath: string, instance: IGBInstance) {
let security = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, "security.json"), "utf8")
)
public async importSecurityFile(localPath: string, instance: IGBInstance) {
const security = JSON.parse(
Fs.readFileSync(UrlJoin(localPath, 'security.json'), 'utf8')
);
security.groups.forEach(group => {
let groupDb = GuaribasGroup.build({
const groupDb = GuaribasGroup.build({
instanceId: instance.instanceId,
displayName: group.displayName
})
});
groupDb.save().then(groupDb => {
group.users.forEach(user => {
let userDb = GuaribasUser.build({
const userDb = GuaribasUser.build({
instanceId: instance.instanceId,
groupId: groupDb.groupId,
userName: user.userName
})
});
userDb.save().then(userDb => {
let userGroup = GuaribasUserGroup.build()
userGroup.groupId = groupDb.groupId
userGroup.userId = userDb.userId
userGroup.save()
})
})
})
})
const userGroup = GuaribasUserGroup.build();
userGroup.groupId = groupDb.groupId;
userGroup.userId = userDb.userId;
userGroup.save();
});
});
});
});
}
async ensureUser(
public async ensureUser(
instanceId: number,
userSystemId: string,
userName: string,
@ -83,24 +83,24 @@ export class SecService extends GBService {
(resolve, reject) => {
GuaribasUser.findOne({
attributes: ["instanceId", "internalAddress"],
attributes: ['instanceId', 'internalAddress'],
where: {
instanceId: instanceId,
userSystemId: userSystemId
}
}).then(user => {
if (!user) {
user = GuaribasUser.build()
user = GuaribasUser.build();
}
user.userSystemId = userSystemId
user.userName = userName
user.displayName = displayName
user.internalAddress = address
user.email = userName
user.defaultChannel = channelName
user.save()
resolve(user)
}).error(reason => reject(reason))
})
user.userSystemId = userSystemId;
user.userName = userName;
user.displayName = displayName;
user.internalAddress = address;
user.email = userName;
user.defaultChannel = channelName;
user.save();
resolve(user);
}).error(reject);
});
}
}

View file

@ -34,43 +34,41 @@
* @fileoverview General Bots server core.
*/
'use strict'
'use strict';
const UrlJoin = require("url-join")
const UrlJoin = require('url-join');
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { GBMinInstance, IGBPackage, IGBCoreService } from "botlib"
import { Sequelize } from "sequelize-typescript"
import { WhatsappDirectLine } from "./services/WhatsappDirectLine"
import { Sequelize } from 'sequelize-typescript';
import { WhatsappDirectLine } from './services/WhatsappDirectLine';
export class GBWhatsappPackage implements IGBPackage {
sysPackages: IGBPackage[] = null
channel: WhatsappDirectLine
public sysPackages: IGBPackage[] = null;
public channel: WhatsappDirectLine;
loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
}
unloadPackage(core: IGBCoreService): void {
public unloadPackage(core: IGBCoreService): void {
}
loadBot(min: GBMinInstance): void {
public loadBot(min: GBMinInstance): void {
// Only loads engine if it is defined on services.json.
if (min.instance.whatsappBotKey != "") {
if (min.instance.whatsappBotKey != '') {
this.channel = new WhatsappDirectLine(min.botId, min.instance.whatsappBotKey, min.instance.whatsappServiceKey,
min.instance.whatsappServiceNumber, min.instance.whatsappServiceUrl, min.instance.whatsappServiceWebhookUrl)
min.instance.whatsappServiceNumber, min.instance.whatsappServiceUrl, min.instance.whatsappServiceWebhookUrl);
}
}
unloadBot(min: GBMinInstance): void {
public unloadBot(min: GBMinInstance): void {
}
onNewSession(min: GBMinInstance, step: any): void {
public onNewSession(min: GBMinInstance, step: any): void {
}
}

View file

@ -30,45 +30,45 @@
| |
\*****************************************************************************/
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 Swagger = require('swagger-client')
const rp = require('request-promise')
import * as request from "request-promise-native"
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 Swagger = require('swagger-client');
const rp = require('request-promise');
import * as request from 'request-promise-native';
import { GBServiceCallback, GBService, IGBInstance } from "botlib"
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
export class WhatsappDirectLine extends GBService {
pollInterval = 1000
directLineClientName = 'DirectLineClient'
directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json'
public pollInterval = 1000;
public directLineClientName = 'DirectLineClient';
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
directLineClient: any
whatsappServiceKey: string
whatsappServiceNumber: string
whatsappServiceUrl: string
whatsappServiceWebhookUrl: string
botId: string
watermark: string = null
public directLineClient: any;
public whatsappServiceKey: string;
public whatsappServiceNumber: string;
public whatsappServiceUrl: string;
public whatsappServiceWebhookUrl: string;
public botId: string;
public watermark: string = null;
conversationIds = {}
public conversationIds = {};
constructor(botId, directLineSecret, whatsappServiceKey, whatsappServiceNumber, whatsappServiceUrl, whatsappServiceWebhookUrl) {
super()
super();
this.botId = botId
this.whatsappServiceKey = whatsappServiceKey
this.whatsappServiceNumber = whatsappServiceNumber
this.whatsappServiceUrl = whatsappServiceUrl
this.whatsappServiceWebhookUrl = whatsappServiceWebhookUrl
this.botId = botId;
this.whatsappServiceKey = whatsappServiceKey;
this.whatsappServiceNumber = whatsappServiceNumber;
this.whatsappServiceUrl = whatsappServiceUrl;
this.whatsappServiceWebhookUrl = whatsappServiceWebhookUrl;
// TODO: Migrate to Swagger 3.
this.directLineClient = rp(this.directLineSpecUrl)
@ -76,16 +76,16 @@ export class WhatsappDirectLine extends GBService {
return new Swagger({
spec: JSON.parse(spec.trim()),
usePromise: true
})
});
})
.then(async (client) => {
client.clientAuthorizations.add('AuthorizationBotConnector',
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' +
directLineSecret, 'header'))
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' +
directLineSecret, 'header'));
var options = {
const options = {
method: 'POST',
url: UrlJoin(this.whatsappServiceUrl, "webhook"),
url: UrlJoin(this.whatsappServiceUrl, 'webhook'),
qs:
{
token: this.whatsappServiceKey,
@ -96,67 +96,66 @@ export class WhatsappDirectLine extends GBService {
{
'cache-control': 'no-cache'
}
}
};
try {
const result = await request.post(options)
logger.info(result)
const result = await request.post(options);
logger.info(result);
} catch (error) {
logger.error('Error initializing 3rd party Whatsapp provider.', error)
logger.error('Error initializing 3rd party Whatsapp provider.', error);
}
return client
return client;
})
.catch((err) => {
logger.error('Error initializing DirectLine client', err)
})
logger.error('Error initializing DirectLine client', err);
});
}
received(req, res) {
let text = req.body.messages[0].body
let from = req.body.messages[0].author.split('@')[0]
let fromName = req.body.messages[0].senderName
public received(req, res) {
const text = req.body.messages[0].body;
const from = req.body.messages[0].author.split('@')[0];
const fromName = req.body.messages[0].senderName;
if (req.body.messages[0].fromMe) {
return // Exit here.
return; // Exit here.
}
logger.info(`GBWhatsapp: Hook called. from: ${from}(${fromName}), text: ${text})`)
logger.info(`GBWhatsapp: Hook called. from: ${from}(${fromName}), text: ${text})`);
let conversationId = this.conversationIds[from]
const conversationId = this.conversationIds[from];
this.directLineClient.then((client) => {
if (this.conversationIds[from] == null) {
logger.info(`GBWhatsapp: Starting new conversation on Bot.`)
logger.info(`GBWhatsapp: Starting new conversation on Bot.`);
client.Conversations.Conversations_StartConversation()
.then((response) => {
return response.obj.conversationId
return response.obj.conversationId;
})
.then((conversationId) => {
this.conversationIds[from] = conversationId
this.conversationIds[from] = conversationId;
this.inputMessage(client, conversationId, text,
from, fromName)
from, fromName);
this.pollMessages(client, conversationId, from, fromName)
this.pollMessages(client, conversationId, from, fromName);
})
.catch((err) => {
console.error('Error starting conversation', err)
})
console.error('Error starting conversation', err);
});
} else {
this.inputMessage(client, conversationId, text,
from, fromName)
from, fromName);
}
res.end()
})
res.end();
});
}
inputMessage(client, conversationId, text, from, fromName) {
public inputMessage(client, conversationId, text, from, fromName) {
client.Conversations.Conversations_PostActivity(
{
@ -172,15 +171,15 @@ export class WhatsappDirectLine extends GBService {
replyToId: from
}
}).catch((err) => {
logger.error(`GBWhatsapp: Error receiving message: ${err}.`)
})
logger.error(`GBWhatsapp: Error receiving message: ${err}.`);
});
}
pollMessages(client, conversationId, from, fromName) {
public pollMessages(client, conversationId, from, fromName) {
logger.info(`GBWhatsapp: Starting polling message for conversationId:
${conversationId}.`)
logger.info(`GBWhatsapp: Starting polling message for conversationId:
${conversationId}.`);
setInterval(() => {
client.Conversations.Conversations_GetActivities({
@ -188,67 +187,67 @@ export class WhatsappDirectLine extends GBService {
conversationId, watermark: this.watermark
})
.then((response) => {
this.watermark = response.obj.watermark
return response.obj.activities
this.watermark = response.obj.watermark;
return response.obj.activities;
})
.then((activities) => {
this.printMessages(activities, conversationId, from, fromName)
})
}, this.pollInterval)
this.printMessages(activities, conversationId, from, fromName);
});
}, this.pollInterval);
}
printMessages(activities, conversationId, from, fromName) {
public printMessages(activities, conversationId, from, fromName) {
if (activities && activities.length) {
// Ignore own messages.
activities = activities.filter((m) => { return (m.from.id === "GeneralBots") && m.type === "message" })
if (activities.length) {
activities = activities.filter((m) => (m.from.id === 'GeneralBots') && m.type === 'message');
if (activities.length) {
// Print other messages.
activities.forEach(activity => {
this.printMessage(activity, conversationId, from, fromName)
})
this.printMessage(activity, conversationId, from, fromName);
});
}
}
}
printMessage(activity, conversationId, from, fromName) {
public printMessage(activity, conversationId, from, fromName) {
let output = ""
let output = '';
if (activity.text) {
logger.info(`GBWhatsapp: MSG: ${activity.text}`)
output = activity.text
logger.info(`GBWhatsapp: MSG: ${activity.text}`);
output = activity.text;
}
if (activity.attachments) {
activity.attachments.forEach((attachment) => {
switch (attachment.contentType) {
case "application/vnd.microsoft.card.hero":
output += `\n${this.renderHeroCard(attachment)}`
break
case 'application/vnd.microsoft.card.hero':
output += `\n${this.renderHeroCard(attachment)}`;
break;
case "image/png":
logger.info('Opening the requested image ' + attachment.contentUrl)
output += `\n${attachment.contentUrl}`
break
case 'image/png':
logger.info('Opening the requested image ' + attachment.contentUrl);
output += `\n${attachment.contentUrl}`;
break;
}
})
});
}
this.sendToDevice(conversationId, from, fromName, output)
this.sendToDevice(conversationId, from, fromName, output);
}
renderHeroCard(attachment) {
return `${attachment.content.title} - ${attachment.content.text}`
public renderHeroCard(attachment) {
return `${attachment.content.title} - ${attachment.content.text}`;
}
async sendToDevice(conversationId, to, toName, msg) {
var options = {
public async sendToDevice(conversationId, to, toName, msg) {
const options = {
method: 'POST',
url: UrlJoin(this.whatsappServiceUrl, 'message'),
qs:
@ -261,8 +260,8 @@ export class WhatsappDirectLine extends GBService {
{
'cache-control': 'no-cache'
}
}
};
const result = await request.get(options)
const result = await request.get(options);
}
}

View file

@ -37,30 +37,30 @@
'use strict';
const logger = require("./logger");
const express = require("express");
const bodyParser = require("body-parser");
const logger = require('./logger');
const express = require('express');
const bodyParser = require('body-parser');
const opn = require('opn');
import { GBConfigService } from "../packages/core.gbapp/services/GBConfigService";
import { GBConversationalService } from "../packages/core.gbapp/services/GBConversationalService";
import { GBMinService } from "../packages/core.gbapp/services/GBMinService";
import { GBDeployer } from "../packages/core.gbapp/services/GBDeployer";
import { GBWhatsappPackage } from "./../packages/whatsapp.gblib/index";
import { GBCoreService } from "../packages/core.gbapp/services/GBCoreService";
import { GBImporter } from "../packages/core.gbapp/services/GBImporter";
import { GBAnalyticsPackage } from "../packages/analytics.gblib";
import { GBCorePackage } from "../packages/core.gbapp";
import { GBKBPackage } from "../packages/kb.gbapp";
import { GBSecurityPackage } from "../packages/security.gblib";
import { GBAdminPackage } from "../packages/admin.gbapp/index";
import { GBCustomerSatisfactionPackage } from "../packages/customer-satisfaction.gbapp";
import { GBAdminService } from "../packages/admin.gbapp/services/GBAdminService";
import { GuaribasInstance } from "../packages/core.gbapp/models/GBModel";
import { AzureDeployerService } from "../packages/azuredeployer.gbapp/services/AzureDeployerService";
import { IGBPackage, IGBInstance } from "botlib";
import { IGBInstance, IGBPackage } from 'botlib';
import { GBAdminPackage } from '../packages/admin.gbapp/index';
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService';
import { GBAnalyticsPackage } from '../packages/analytics.gblib';
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService';
import { GBCorePackage } from '../packages/core.gbapp';
import { GuaribasInstance } from '../packages/core.gbapp/models/GBModel';
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService';
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService';
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService';
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
import { GBImporter } from '../packages/core.gbapp/services/GBImporter';
import { GBMinService } from '../packages/core.gbapp/services/GBMinService';
import { GBCustomerSatisfactionPackage } from '../packages/customer-satisfaction.gbapp';
import { GBKBPackage } from '../packages/kb.gbapp';
import { GBSecurityPackage } from '../packages/security.gblib';
import { GBWhatsappPackage } from './../packages/whatsapp.gblib/index';
let appPackages = new Array<IGBPackage>();
const appPackages = new Array<IGBPackage>();
/**
* General Bots open-core entry point.
@ -71,7 +71,7 @@ export class GBServer {
* Program entry-point.
*/
static run() {
public static run() {
logger.info(`The Bot Server is in STARTING mode...`);
@ -79,8 +79,8 @@ export class GBServer {
// bot instance. This allows the same server to attend multiple Bot on
// the Marketplace until GB get serverless.
let port = process.env.port || process.env.PORT || 4242;
let server = express();
const port = process.env.port || process.env.PORT || 4242;
const server = express();
server.use(bodyParser.json()); // to support JSON-encoded bodies
server.use(
@ -99,15 +99,15 @@ export class GBServer {
// Reads basic configuration, initialize minimal services.
GBConfigService.init();
let core = new GBCoreService();
const core = new GBCoreService();
// Ensures cloud / on-premises infrastructure is setup.
logger.info(`Establishing a development local proxy (ngrok)...`);
let proxyAddress = await core.ensureProxy(port);
const proxyAddress = await core.ensureProxy(port);
let deployer = new GBDeployer(core, new GBImporter(core));
let azureDeployer = new AzureDeployerService(deployer);
const deployer = new GBDeployer(core, new GBImporter(core));
const azureDeployer = new AzureDeployerService(deployer);
try {
await core.initDatabase();
@ -117,7 +117,7 @@ export class GBServer {
bootInstance = await azureDeployer.deployFarm(proxyAddress);
} catch (error) {
logger.warn(
"In case of error, please cleanup any infrastructure objects created during this procedure and .env before running again."
'In case of error, please cleanup any infrastructure objects created during this procedure and .env before running again.'
);
throw error;
}
@ -132,13 +132,13 @@ export class GBServer {
// Check admin password.
let conversationalService = new GBConversationalService(core);
let adminService = new GBAdminService(core);
let password = GBConfigService.get("ADMIN_PASS");
const conversationalService = new GBConversationalService(core);
const adminService = new GBAdminService(core);
const password = GBConfigService.get('ADMIN_PASS');
if (!GBAdminService.StrongRegex.test(password)) {
throw new Error(
"Please, define a really strong password in ADMIN_PASS environment variable before running the server."
'Please, define a really strong password in ADMIN_PASS environment variable before running the server.'
);
}
@ -155,7 +155,7 @@ export class GBServer {
GBWhatsappPackage
].forEach(e => {
logger.info(`Loading sys package: ${e.name}...`);
let p = Object.create(e.prototype) as IGBPackage;
const p = Object.create(e.prototype) as IGBPackage;
p.loadPackage(core, core.sequelize);
});
@ -165,9 +165,9 @@ export class GBServer {
let instances: GuaribasInstance[];
try {
instances = await core.loadInstances();
let instance = instances[0];
if (process.env.NODE_ENV === "development") {
const instance = instances[0];
if (process.env.NODE_ENV === 'development') {
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
await azureDeployer.updateBotProxy(
@ -177,30 +177,30 @@ export class GBServer {
);
}
} catch (error) {
if (error.parent.code === "ELOGIN") {
let group = GBConfigService.get("CLOUD_GROUP");
let serverName = GBConfigService.get("STORAGE_SERVER").split(
".database.windows.net"
if (error.parent.code === 'ELOGIN') {
const group = GBConfigService.get('CLOUD_GROUP');
const serverName = GBConfigService.get('STORAGE_SERVER').split(
'.database.windows.net'
)[0];
await azureDeployer.openStorageFirewall(group, serverName);
} else {
// Check if storage is empty and needs formatting.
let isInvalidObject =
const isInvalidObject =
error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE.
if (isInvalidObject) {
if (GBConfigService.get("STORAGE_SYNC") != "true") {
throw `Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${
if (GBConfigService.get('STORAGE_SYNC') != 'true') {
throw new Error(`Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${
error.message
}.`;
}.`);
} else {
logger.info(
`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`
);
}
} else {
throw `Cannot connect to operating storage: ${error.message}.`;
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
}
}
}
@ -213,7 +213,7 @@ export class GBServer {
// If instances is undefined here it's because storage has been formatted.
// Load all instances from .gbot found on deploy package directory.
if (!instances) {
let saveInstance = new GuaribasInstance(bootInstance);
const saveInstance = new GuaribasInstance(bootInstance);
await saveInstance.save();
instances = await core.loadInstances();
}
@ -221,7 +221,7 @@ export class GBServer {
// Setup server dynamic (per bot instance) resources and listeners.
logger.info(`Publishing instances...`);
let minService = new GBMinService(
const minService = new GBMinService(
core,
conversationalService,
adminService,
@ -230,13 +230,13 @@ export class GBServer {
await minService.buildMin(server, appPackages, instances);
logger.info(`The Bot Server is in RUNNING mode...`);
if (process.env.NODE_ENV === "development") {
if (process.env.NODE_ENV === 'development') {
opn('http://localhost:4242');
}
return core;
} catch (err) {
logger.error(`STOP: ${err} ${err.stack ? err.stack : ""}`);
logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
process.exit(1);
}
})();

View file

@ -30,7 +30,7 @@
| |
\*****************************************************************************/
const { createLogger, format, transports } = require('winston')
const { createLogger, format, transports } = require('winston');
const config = {
levels: {
@ -53,7 +53,7 @@ const config = {
silly: 'magenta',
custom: 'yellow'
}
}
};
const logger = createLogger({
format: format.combine(
@ -62,11 +62,11 @@ const logger = createLogger({
format.label({ label: 'GeneralBots' }),
format.timestamp(),
format.printf(nfo => {
return `${nfo.timestamp} [${nfo.label}] ${nfo.level}: ${nfo.message}`
return `${nfo.timestamp} [${nfo.label}] ${nfo.level}: ${nfo.message}`;
})
),
levels: config.levels,
transports: [new transports.Console()]
})
transports: [new transports.Console()]
});
module.exports=logger
module.exports = logger;