* NEW: kb.gbapp now has a complete browser of excel articles.

* FIX: Some security improved.
* NEW: Protocol changes for exchanging questions between UI and Bot Server.
This commit is contained in:
Rodrigo Rodriguez 2018-09-20 12:35:47 -03:00
parent 708a27e419
commit 379ade60fb
10 changed files with 170 additions and 101 deletions

View file

@ -107,7 +107,7 @@ export class AdminDialog extends IGBDialog {
let importer = new GBImporter(min.core);
let deployer = new GBDeployer(min.core, importer);
min.dialogs.add("/admin", [
min.dialogs.add("/adminRat", [
async dc => {
await AdminDialog.refreshAdminToken(min, dc);
// await dc.context.sendActivity(
@ -137,7 +137,7 @@ export class AdminDialog extends IGBDialog {
}
]);
min.dialogs.add("/admin1", [
min.dialogs.add("/admin", [
async (dc, args) => {
const prompt = "Please, authenticate:";
await dc.prompt("textPrompt", prompt);

View file

@ -291,9 +291,9 @@ export class GBMinService {
}
logger.info(
`[RCV]: ${context.activity.type}, ChannelID: ${
`[User]: ${context.activity.type}, ChannelID: ${
context.activity.channelId
}, Name: ${context.activity.name}, Text: ${context.activity.text}.`
} Text: ${context.activity.text}.`
)
if (
context.activity.type === "conversationUpdate" &&
@ -358,9 +358,9 @@ export class GBMinService {
})
} else if (context.activity.name === "showFAQ") {
await dc.begin("/faq")
} else if (context.activity.name === "ask") {
await dc.begin("/answer", {
query: (context.activity as any).data,
} else if (context.activity.name === "answerEvent") {
await dc.begin("/answerEvent", {
questionId: (context.activity as any).data,
fromFaq: true
})
@ -374,7 +374,7 @@ export class GBMinService {
}
}
} catch (error) {
let msg = `Error in main activity: ${error.message}`
let msg = `Error in main activity: ${error.message} ${error.stack? error.stack:""}`
logger.error(msg)
}
})

View file

@ -57,9 +57,11 @@ class GBUIApp extends React.Component {
token: null,
instanceClient: null
};
window.user = this.getUser()
}
sendToken(token) {
setTimeout(() => {
window.botConnection
.postActivity({
@ -69,7 +71,7 @@ class GBUIApp extends React.Component {
locale: "en-us",
textFormat: "plain",
timestamp: new Date().toISOString(),
from: { id: "webUser", name: "You" }
from: this.getUser()
})
.subscribe(() => {
window.userAgentApplication.logout();
@ -86,10 +88,11 @@ class GBUIApp extends React.Component {
locale: "en-us",
textFormat: "plain",
timestamp: new Date().toISOString(),
from: { id: "webUser", name: "You" }
from: this.getUser()
})
.subscribe(console.log("EVENT SENT TO Guaribas."));
}
getUser() {
return { id: "webUser@gb", name: "You" };
}

View file

@ -41,7 +41,7 @@ class SideBarMenu extends React.Component {
locale: "en-us",
textFormat: "plain",
timestamp: new Date().toISOString(),
from: { id: "webUser", name: "You" }
from: window.user
})
.subscribe(console.log("success"));
}

View file

@ -39,12 +39,12 @@ class RenderItem extends Component {
window.botConnection
.postActivity({
type: "event",
name: "ask",
data: item.content,
name: "answerEvent",
data: item.questionId,
locale: "en-us",
textFormat: "plain",
timestamp: new Date().toISOString(),
from: { id: "webUser", name: "You" }
from: window.user
})
.subscribe(console.log("success"));
},400);

View file

@ -33,6 +33,7 @@
import React, { Component } from "react";
class GBMarkdownPlayer extends Component {
send(value) {
setTimeout(() => {
window.botConnection
@ -49,15 +50,35 @@ class GBMarkdownPlayer extends Component {
}, 400);
}
sendAnswer(text) {
setTimeout(() => {
window.botConnection
.postActivity({
type: "event",
name: "answerEvent",
data: text,
locale: "en-us",
textFormat: "plain",
timestamp: new Date().toISOString(),
from: window.user
})
.subscribe(console.log("success"));
}, 400);
}
constructor() {
super();
this.state = {
content: ""
content: "",
prevId: 0,
nextId: 0
};
}
play(data) {
this.setState({ content: data });
this.setState({ content: data.content, prevId: data.prevId, nextId: data.nextId });
}
stop() {
@ -78,7 +99,7 @@ class GBMarkdownPlayer extends Component {
render() {
var quality =
var quality =
<div className="gb-markdown-player-quality">
<span ref={i => (this.quality = i)}>Is the answer OK?</span>
&nbsp;&nbsp;
@ -91,18 +112,34 @@ class GBMarkdownPlayer extends Component {
</button>
</div>;
if (this.state.content === "") {
quality = "";
}
var next = "", prev = "";
if (this.state.content === "") {
quality = "";
}
if (this.state.prevId) {
prev = <a style={{ color: 'blue' }}
onPress={() => this.sendAnswer(this.state.prevId)}>
Back
</a>
}
if (this.state.nextId) {
next = <a style={{ color: 'blue' }}
onPress={() => this.sendAnswer(this.state.nextId)}>
Next
</a>
}
return (
<div ref={i => (this.playerText = i)} className="media-player">
<div className="media-player-container">
<div className="media-player-scroll">
<div><span>{prev}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>{next}</span></div>
<span dangerouslySetInnerHTML={this.createMarkup()} />
</div>
</div>
{quality}
</div>
{quality}
</div>
);
}

View file

@ -39,6 +39,7 @@ import { KBService } from "./../services/KBService";
import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
import { LuisRecognizer } from "botbuilder-ai";
import { GuaribasQuestion } from "../models";
const logger = require("../../../src/logger");
@ -58,6 +59,24 @@ export class AskDialog extends IGBDialog {
serviceEndpoint: min.instance.nlpServerUrl
});
min.dialogs.add("/answerEvent", [
async (dc, args) => {
if (args && args.questionId) {
let question = await service.getQuestionById(min.instance.instanceId, args.questionId);
let answer = await service.getAnswerById(min.instance.instanceId, question.answerId)
// Sends the answer to all outputs, including projector.
await service.sendAnswer(
min.conversationalService,
dc,
answer
);
}
}])
min.dialogs.add("/answer", [
async (dc, args) => {
// Initialize values.

View file

@ -84,7 +84,7 @@ export class MenuDialog extends IGBDialog {
)
await min.conversationalService.sendEvent(dc, "play", {
playerType: "bullet",
data: data.slice(0, 6)
data: data.slice(0, 10)
})
}
} else {
@ -109,12 +109,13 @@ export class MenuDialog extends IGBDialog {
var subject = item
var card = CardFactory.heroCard(
subject.title,
subject.description,
CardFactory.images([
UrlJoin(
"/kb",
min.instance.kb,
"subjects",
subject.internalId + ".png" // TODO: or fallback to subject.png
"subject.png"
)
]),
CardFactory.actions([
@ -123,7 +124,9 @@ export class MenuDialog extends IGBDialog {
title: Messages[locale].menu_select,
value: JSON.stringify({
title: subject.title,
description: subject.description,
subjectId: subject.subjectId,
internalId: subject.internalId,
to: subject.to
})
}

View file

@ -54,7 +54,8 @@ import {
DataType,
IsUUID,
PrimaryKey,
AutoIncrement
AutoIncrement,
HasOne
} from "sequelize-typescript"
import { GuaribasUser } from "../../security.gblib/models"
@ -71,6 +72,7 @@ export class GuaribasSubject extends Model<GuaribasSubject> {
@Column title: string
@Column(DataType.STRING(512))
@Column description: string
@Column from: string
@ -201,6 +203,20 @@ export class GuaribasAnswer extends Model<GuaribasAnswer> {
@HasMany(() => GuaribasQuestion)
questions: GuaribasQuestion[]
@HasOne(() => GuaribasQuestion)
prev: GuaribasQuestion
@HasOne(() => GuaribasQuestion)
next: GuaribasQuestion
@ForeignKey(() => GuaribasQuestion)
@Column
nextId: number
@ForeignKey(() => GuaribasQuestion)
@Column
prevId: number
@ForeignKey(() => GuaribasInstance)
@Column
instanceId: number

View file

@ -63,23 +63,28 @@ export class KBService {
this.sequelize = sequelize
}
async getQuestionById(
instanceId: number,
questionId: number
): Promise<GuaribasQuestion> {
return GuaribasQuestion.findOne({
where: {
instanceId: instanceId,
questionId: questionId
}
})
}
async getAnswerById(
instanceId: number,
answerId: number
): Promise<GuaribasAnswer> {
return new Promise<GuaribasAnswer>(
(resolve, reject) => {
GuaribasAnswer.findAll({
where: {
instanceId: instanceId,
answerId: answerId
}
}).then((item: GuaribasAnswer[]) => {
resolve(item[0])
}).error((reason) => {
reject(reason)
})
})
return GuaribasAnswer.findOne({
where: {
instanceId: instanceId,
answerId: answerId
}
})
}
async getAnswerByText(
@ -291,7 +296,7 @@ export class KBService {
static getSubjectItemsSeparatedBySpaces(subjects: GuaribasSubject[]) {
let out = []
subjects.forEach(subject => {
out.push(subject.title)
out.push(subject.internalId)
})
return out.join(" ")
}
@ -300,66 +305,37 @@ export class KBService {
instanceId: number,
parentId: number
): Promise<GuaribasSubject[]> {
return new Promise<GuaribasSubject[]>(
(resolve, reject) => {
var where = { parentSubjectId: parentId, instanceId: instanceId }
GuaribasSubject.findAll({
where: where
})
.then((values: GuaribasSubject[]) => {
resolve(values)
})
.error(reason => {
reject(reason)
})
})
var where = { parentSubjectId: parentId, instanceId: instanceId }
return GuaribasSubject.findAll({
where: where
})
}
async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
return new Promise<GuaribasQuestion[]>(
(resolve, reject) => {
let where = {
from: from
}
let where = {
from: from
}
if (subjects) {
if (subjects[0]) {
where["subject1"] = subjects[0].internalId
}
if (subjects) {
if (subjects[0]) {
where["subject1"] = subjects[0].title
}
if (subjects[1]) {
where["subject2"] = subjects[1].internalId
}
if (subjects[1]) {
where["subject2"] = subjects[1].title
}
if (subjects[2]) {
where["subject3"] = subjects[2].internalId
}
if (subjects[2]) {
where["subject3"] = subjects[2].title
}
if (subjects[3]) {
where["subject4"] = subjects[3].title
}
}
GuaribasQuestion.findAll({
where: where
})
.then((items: GuaribasQuestion[]) => {
if (!items) items = []
if (items.length == 0) {
resolve([])
} else {
resolve(items)
}
})
.catch(reason => {
if (reason.message.indexOf("no such table: IGBInstance") != -1) {
resolve([])
} else {
reject(reason)
logger.info(`GuaribasServiceError: ${reason}`)
}
})
})
if (subjects[3]) {
where["subject4"] = subjects[3].internalId
}
}
return await GuaribasQuestion.findAll({
where: where
})
}
async importKbTabularFile(
@ -373,6 +349,9 @@ export class KBService {
delimiter: "\t"
}
let lastQuestion: GuaribasQuestion;
let lastAnswer: GuaribasAnswer;
let data = await parse(file, opts)
return asyncPromise.eachSeries(data, async line => {
@ -428,9 +407,11 @@ export class KBService {
instanceId: instanceId,
content: answer,
format: format,
packageId: packageId
packageId: packageId,
prevId: lastQuestion ? lastQuestion.questionId : 0,
})
await GuaribasQuestion.create({
let question1 = await GuaribasQuestion.create({
from: from,
to: to,
subject1: subject1,
@ -443,7 +424,13 @@ export class KBService {
packageId: packageId
})
return Promise.resolve(question)
if (lastAnswer && lastQuestion) {
await lastAnswer.updateAttributes({ nextId: lastQuestion.questionId })
}
lastAnswer = answer1
lastQuestion = question1
return Promise.resolve(lastQuestion)
} else {
@ -465,7 +452,7 @@ export class KBService {
} else if (answer.content.length > 140 &&
dc.context._activity.channelId === "webchat") {
const locale = dc.context.activity.locale;
await dc.context.sendActivity(Messages[locale].will_answer_projector) // TODO: Handle rnd.
var html = answer.content
@ -483,7 +470,13 @@ export class KBService {
})
html = marked(answer.content)
}
await conversationalService.sendEvent(dc, "play", { playerType: "markdown", data: html })
await conversationalService.sendEvent(dc, "play",
{
playerType: "markdown", data: {
content: html, answer: answer,
prevId: answer.prevId, nextId: answer.nextId
}
})
} else {
await dc.context.sendActivity(answer.content)
await conversationalService.sendEvent(dc, "stop", null)
@ -560,10 +553,8 @@ export class KBService {
else {
return Promise.resolve(item)
}
})
}
return doIt(subjects.children, null)
}
@ -603,11 +594,11 @@ export class KBService {
)
let instance = await core.loadInstance(packageObject.botId)
logger.info(`[GBDeployer] Beginning importing: ${localPath}`)
logger.info(`[GBDeployer] Importing: ${localPath}`)
let p = await deployer.deployPackageToStorage(
instance.instanceId,
packageName)
await this.importKbPackage(localPath, p, instance)
logger.info(`[GBDeployer] Finished importing ${localPath}`)
logger.info(`[GBDeployer] Finished import of ${localPath}`)
}
}