General Bots in now almost globalized.

This commit is contained in:
Rodrigo Rodriguez 2018-09-14 12:56:54 -03:00
parent c573e33754
commit 7a33f6942e
19 changed files with 359 additions and 371 deletions

View file

@ -41,6 +41,7 @@ import { GBImporter } from '../../core.gbapp/services/GBImporter'
import { GBConfigService } from '../../core.gbapp/services/GBConfigService' import { GBConfigService } from '../../core.gbapp/services/GBConfigService'
import { KBService } from './../../kb.gbapp/services/KBService' import { KBService } from './../../kb.gbapp/services/KBService'
import { BotAdapter } from "botbuilder" import { BotAdapter } from "botbuilder"
import {messages} from "./Strings"
import { reject } from "async" import { reject } from "async"
/** /**

View file

@ -30,46 +30,48 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict" "use strict";
import { IGBDialog } from "botlib" import { IGBDialog } from "botlib";
import { GBMinInstance } from "botlib" import { GBMinInstance } from "botlib";
import { BotAdapter } from "botbuilder" import { BotAdapter } from "botbuilder";
const messages = require("./strings.json").messages import { Messages } from "../strings";
export class WelcomeDialog extends IGBDialog { export class WelcomeDialog extends IGBDialog {
/** /**
* Setup dialogs flows and define services call. * Setup dialogs flows and define services call.
* *
* @param bot The bot adapter. * @param bot The bot adapter.
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add("/", [ min.dialogs.add("/", [
async (dc, args) => { async (dc, args) => {
const user = min.userState.get(dc.context);
const locale = dc.context.activity.locale;
const user = min.userState.get(dc.context)
let loc = dc.context.activity.locale;
if (!user.once) { if (!user.once) {
user.once = true user.once = true;
var a = new Date() var a = new Date();
const date = a.getHours() const date = a.getHours();
var msg = var msg = 4;
date < 12 ? messages[loc].good_morning : date < 18 ? date < 12
messages[loc].good_evening : messages[loc].good_night ? Messages[locale].good_morning
: date < 18
? Messages[locale].good_evening
: Messages[locale].good_night;
let messages1 = [`Oi, ${msg}.`, `Oi!`, `Olá, ${msg}`, `Olá!`] await dc.context.sendActivity(Messages[locale].hi(msg));
await dc.context.sendActivity(messages1[0])
if (dc.context.activity && dc.context.activity.type == "message" && if (
dc.context.activity.text != "") { dc.context.activity &&
await dc.replace("/answer", { query: dc.context.activity.text }) dc.context.activity.type == "message" &&
dc.context.activity.text != ""
) {
await dc.replace("/answer", { query: dc.context.activity.text });
} }
} }
} }
]) ]);
} }
} }

View file

@ -30,37 +30,36 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict" "use strict";
import { IGBDialog } from "botlib"
import { GBMinInstance } from "botlib"
import { BotAdapter } from "botbuilder"
import { IGBDialog } from "botlib";
import { GBMinInstance } from "botlib";
import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
export class WhoAmIDialog extends IGBDialog { export class WhoAmIDialog extends IGBDialog {
/** /**
* Setup dialogs flows and define services call. * Setup dialogs flows and define services call.
* *
* @param bot The bot adapter. * @param bot The bot adapter.
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add("/whoAmI", [ min.dialogs.add("/whoAmI", [
async (dc, args) => { async dc => {
await dc.context.sendActivity(`${min.instance.description}`) let locale = dc.context.activity.locale;
await dc.context.sendActivity(`${min.instance.description}`);
if (min.instance.whoAmIVideo) { if (min.instance.whoAmIVideo) {
await dc.context.sendActivity("show_video") await dc.context.sendActivity(Messages[locale].show_video);
await min.conversationalService.sendEvent(dc, "play", { await min.conversationalService.sendEvent(dc, "play", {
playerType: "video", playerType: "video",
data: min.instance.whoAmIVideo.trim() data: min.instance.whoAmIVideo.trim()
}) });
} }
await dc.replace('/ask', { isReturning: true }) await dc.replace("/ask", { isReturning: true });
} }
]) ]);
} }
} }

View file

@ -1,16 +0,0 @@
{
"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"
},
"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"
}
}
}

View file

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

View file

@ -182,7 +182,7 @@ export class GBMinService {
uiUrl, uiUrl,
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, "build")) express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, "build"))
) )
logger.info(`Bot UI ${uiPackage} acessible at: ${uiUrl}.`) logger.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`)
// Setups handlers. // Setups handlers.
// send: function (context.activity, next) { // send: function (context.activity, next) {

View file

@ -0,0 +1,16 @@
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}.`
},
"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}.`
}
};

View file

@ -1,5 +0,0 @@
export const messages =
{
show_video: "I will show you a video, please wait..."
}
;

View file

@ -1,4 +0,0 @@
export const messages =
{
show_video: "Vou te mostrar um vídeo. Por favor, aguarde..."
}

View file

@ -30,13 +30,14 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict" "use strict";
import { CSService } from "../services/CSService" import { CSService } from "../services/CSService";
import { AzureText } from "pragmatismo-io-framework" import { AzureText } from "pragmatismo-io-framework";
import { GBMinInstance } from "botlib" import { GBMinInstance } from "botlib";
import { IGBDialog } from "botlib" import { IGBDialog } from "botlib";
import { BotAdapter } from "botbuilder" import { BotAdapter } from "botbuilder";
import { Messages } from "../strings";
export class FeedbackDialog extends IGBDialog { export class FeedbackDialog extends IGBDialog {
/** /**
@ -46,70 +47,55 @@ export class FeedbackDialog extends IGBDialog {
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService() const service = new CSService();
min.dialogs.add("/feedbackNumber", [ min.dialogs.add("/feedbackNumber", [
async dc => { async dc => {
let messages = [ let locale = dc.context.activity.locale;
"O que achou do meu atendimento, de 1 a 5?", await dc.prompt("choicePrompt", Messages[locale].what_about_me, [
"Qual a nota do meu atendimento?",
"Como define meu atendimento numa escala de 1 a 5?"
]
await dc.prompt("choicePrompt", messages[0], [
"1", "1",
"2", "2",
"3", "3",
"4", "4",
" 5" "5"
]) ]);
}, },
async (dc, value) => { async (dc, value) => {
let rate = value.entity let locale = dc.context.activity.locale;
const user = min.userState.get(dc.context) let rate = value.entity;
await service.updateConversationRate(user.conversation, rate) const user = min.userState.get(dc.context);
let messages = ["Obrigado!", "Obrigado por responder."] await service.updateConversationRate(user.conversation, rate);
await dc.context.sendActivity(messages[0]) // TODO: Handle rnd. await dc.context.sendActivity(Messages[locale].thanks);
} }
]) ]);
min.dialogs.add("/feedback", [ min.dialogs.add("/feedback", [
async (dc, args) => { async (dc, args) => {
let locale = dc.context.activity.locale;
if (args && args.fromMenu) { if (args && args.fromMenu) {
let messages = [ await dc.context.sendActivity(Messages[locale].about_suggestions);
"Sugestões melhoram muito minha qualidade...",
"Obrigado pela sua iniciativa de sugestão."
]
await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
} }
let messages = [ await dc.prompt("textPrompt", Messages[locale].what_about_service);
"O que achou do meu atendimento?",
"Como foi meu atendimento?",
"Gostaria de dizer algo sobre meu atendimento?"
]
await dc.prompt("textPrompt", messages[0])
}, },
async (dc, value) => { async (dc, value) => {
let locale = dc.context.activity.locale;
let rate = await AzureText.getSentiment( let rate = await AzureText.getSentiment(
min.instance.textAnalyticsKey, min.instance.textAnalyticsKey,
min.instance.textAnalyticsServerUrl, min.instance.textAnalyticsServerUrl,
min.conversationalService.getCurrentLanguage(dc), min.conversationalService.getCurrentLanguage(dc),
value value
) );
if (rate > 0.50) { if (rate > 0.5) {
await dc.context.sendActivity( await dc.context.sendActivity(Messages[locale].glad_you_liked);
"Bom saber que você gostou. Conte comigo."
)
} else { } else {
await dc.context.sendActivity( await dc.context.sendActivity(Messages[locale].we_will_improve);
"Vamos registrar sua questão, obrigado pela sinceridade."
)
// TODO: Record. // TODO: Record.
} }
await dc.replace("/ask", { isReturning: true }) await dc.replace("/ask", { isReturning: true });
} }
]) ]);
} }
} }

View file

@ -30,61 +30,50 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict" "use strict";
import { IGBDialog } from "botlib" import { IGBDialog } from "botlib";
import { GBMinInstance } from "botlib" import { GBMinInstance } from "botlib";
import { CSService } from "../services/CSService" import { CSService } from "../services/CSService";
import { BotAdapter } from "botbuilder" import { BotAdapter } from "botbuilder";
const logger = require("../../../src/logger") import { Messages } from "../strings";
const logger = require("../../../src/logger");
export class QualityDialog extends IGBDialog { export class QualityDialog extends IGBDialog {
/** /**
* Setup dialogs flows and define services call. * Setup dialogs flows and define services call.
* *
* @param bot The bot adapter. * @param bot The bot adapter.
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService();
const service = new CSService()
min.dialogs.add("/quality", [ min.dialogs.add("/quality", [
async (dc, args) => { async (dc, args) => {
const user = min.userState.get(dc.context) const locale = dc.context.activity.locale;
var score = args.score const user = min.userState.get(dc.context);
var score = args.score;
setTimeout( setTimeout(
() => min.conversationalService.sendEvent(dc, "stop", null), () => min.conversationalService.sendEvent(dc, "stop", null),
400 400
) );
if (score == 0) { if (score == 0) {
let msg = [ await dc.context.sendActivity(Messages[locale].im_sorry_lets_try);
"Desculpe-me, vamos tentar novamente.",
"Lamento... Vamos tentar novamente!",
"Desculpe-me. Por favor, tente escrever de outra forma?"
]
await dc.context.sendActivity(msg[0])
} else { } else {
let msg = [ await dc.context.sendActivity(Messages[locale].great_thanks);
"Ótimo, obrigado por contribuir com sua resposta.",
"Certo, obrigado pela informação.",
"Obrigado pela contribuição."
]
await dc.context.sendActivity(msg[0])
await service.insertQuestionAlternate( await service.insertQuestionAlternate(
min.instance.instanceId, min.instance.instanceId,
user.lastQuestion, user.lastQuestion,
user.lastQuestionId user.lastQuestionId
) );
await dc.replace("/ask", { isReturning: true });
await dc.replace('/ask', {isReturning: true})
} }
} }
]) ]);
} }
} }

View file

@ -0,0 +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."
},
"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

@ -61,12 +61,12 @@ class SideBarMenu extends React.Component {
<div className="IconsMenu"> <div className="IconsMenu">
<div className="iconMenu"> <div className="iconMenu">
<span className="iconText" onClick={() => this.send("showFAQ")}> <span className="iconText" onClick={() => this.send("showFAQ")}>
Perguntas frequentes FAQ
</span> </span>
</div> </div>
<div className="iconMenu"> <div className="iconMenu">
<span className="iconText" onClick={() => this.send("whoAmI")}> <span className="iconText" onClick={() => this.send("whoAmI")}>
Quem é você? Who are You?
</span> </span>
</div> </div>
<div className="iconMenu"> <div className="iconMenu">
@ -74,7 +74,7 @@ class SideBarMenu extends React.Component {
className="iconText" className="iconText"
onClick={() => this.send("showSubjects")} onClick={() => this.send("showSubjects")}
> >
Assuntos Subjects
</span> </span>
</div> </div>
<div className="iconMenu"> <div className="iconMenu">
@ -82,7 +82,7 @@ class SideBarMenu extends React.Component {
className="iconText" className="iconText"
onClick={() => this.send("giveFeedback")} onClick={() => this.send("giveFeedback")}
> >
Sugestão Suggestions
</span> </span>
</div> </div>
</div> </div>

View file

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

View file

@ -35,6 +35,7 @@
import { KBService } from './../services/KBService' import { KBService } from './../services/KBService'
import { IGBDialog } from "botlib" import { IGBDialog } from "botlib"
import { BotAdapter } from "botbuilder" import { BotAdapter } from "botbuilder"
import { Messages } from "../strings";
import { GBMinInstance } from "botlib" import { GBMinInstance } from "botlib"
export class FaqDialog extends IGBDialog { export class FaqDialog extends IGBDialog {
@ -51,19 +52,14 @@ export class FaqDialog extends IGBDialog {
min.dialogs.add("/faq", [ min.dialogs.add("/faq", [
async (dc, args) => { async (dc, args) => {
let data = await service.getFaqBySubjectArray("faq", null) let data = await service.getFaqBySubjectArray("faq", null)
const locale = dc.context.activity.locale;
if (data) { if (data) {
await min.conversationalService.sendEvent(dc, "play", { await min.conversationalService.sendEvent(dc, "play", {
playerType: "bullet", playerType: "bullet",
data: data.slice(0, 10) data: data.slice(0, 10)
}) })
let messages = [ await dc.context.sendActivity(Messages[locale].see_faq) // TODO: RND messages.
"Veja algumas perguntas mais frequentes logo na tela. Clique numa delas para eu responder.",
"Você pode clicar em alguma destas perguntas da tela que eu te respondo de imediato.",
"Veja a lista que eu preparei logo aí na tela..."
]
await dc.context.sendActivity(messages[0]) // TODO: RND messages.
await dc.endAll() await dc.endAll()
} }
} }

View file

@ -30,137 +30,138 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict" "use strict";
const UrlJoin = require("url-join") const UrlJoin = require("url-join");
import { BotAdapter, CardFactory, MessageFactory } from "botbuilder" import { BotAdapter, CardFactory, MessageFactory } from "botbuilder";
import { IGBDialog } from "botlib" import { IGBDialog } from "botlib";
import { GBMinInstance } from "botlib" import { GBMinInstance } from "botlib";
import { GuaribasSubject } from '../models' import { GuaribasSubject } from "../models";
import { KBService } from "../services/KBService" import { KBService } from "../services/KBService";
import { Messages } from "../strings";
import { AzureText } from "pragmatismo-io-framework";
export class MenuDialog extends IGBDialog { export class MenuDialog extends IGBDialog {
/** /**
* Setup dialogs flows and define services call. * Setup dialogs flows and define services call.
* *
* @param bot The bot adapter. * @param bot The bot adapter.
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
static setup(bot: BotAdapter, min: GBMinInstance) { static setup(bot: BotAdapter, min: GBMinInstance) {
var service = new KBService(min.core.sequelize);
var service = new KBService(min.core.sequelize)
bot
min.dialogs.add("/menu", [ min.dialogs.add("/menu", [
async (dc, args) => { async (dc, args) => {
var rootSubjectId = null const locale = dc.context.activity.locale;
var rootSubjectId = null;
if (args && args.data) { if (args && args.data) {
var subject = args.data var subject = args.data;
// If there is a shortcut specified as subject destination, go there. // If there is a shortcut specified as subject destination, go there.
if (subject.to) { if (subject.to) {
let dialog = subject.to.split(":")[1] let dialog = subject.to.split(":")[1];
await dc.replace("/" + dialog) await dc.replace("/" + dialog);
await dc.end() await dc.end();
return return;
} }
// Adds to bot a perception of a new subject. // Adds to bot a perception of a new subject.
const user = min.userState.get(dc.context) const user = min.userState.get(dc.context);
user.subjects.push(subject) user.subjects.push(subject);
rootSubjectId = subject.subjectId rootSubjectId = subject.subjectId;
// Whenever a subject is selected, shows a faq about it. // Whenever a subject is selected, shows a faq about it.
if (user.subjects.length > 0) { if (user.subjects.length > 0) {
let data = await service.getFaqBySubjectArray("menu", user.subjects) let data = await service.getFaqBySubjectArray(
"menu",
user.subjects
);
await min.conversationalService.sendEvent(dc, "play", { await min.conversationalService.sendEvent(dc, "play", {
playerType: "bullet", playerType: "bullet",
data: data.slice(0, 6) data: data.slice(0, 6)
}) });
} }
} else { } else {
const user = min.userState.get(dc.context) const user = min.userState.get(dc.context);
user.subjects = [] user.subjects = [];
let messages = [ await dc.context.sendActivity(Messages[locale].here_is_subjects); // TODO: Handle rnd.
"Aqui estão algumas categorias de assuntos...", user.isAsking = false;
"Selecionando o assunto você pode me ajudar a encontrar a resposta certa...",
"Você pode selecionar algum dos assuntos abaixo e perguntar algo..."
]
await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
user.isAsking = false
} }
const msg = MessageFactory.text('') const msg = MessageFactory.text("");
var attachments = [] var attachments = [];
let data = await service.getSubjectItems( let data = await service.getSubjectItems(
min.instance.instanceId, min.instance.instanceId,
rootSubjectId) rootSubjectId
);
msg.attachmentLayout = 'carousel' msg.attachmentLayout = "carousel";
data.forEach(function (item: GuaribasSubject) { data.forEach(function(item: GuaribasSubject) {
var subject = item;
var subject = item
var card = CardFactory.heroCard( var card = CardFactory.heroCard(
subject.title, subject.title,
CardFactory.images([UrlJoin( CardFactory.images([
"/kb", UrlJoin(
min.instance.kb, "/kb",
"subjects", min.instance.kb,
subject.internalId + ".png" // TODO: or fallback to subject.png "subjects",
)]), subject.internalId + ".png" // TODO: or fallback to subject.png
)
]),
CardFactory.actions([ CardFactory.actions([
{ {
type: 'postBack', type: "postBack",
title: 'Selecionar', title: Messages[locale].menu_select,
value: JSON.stringify({ value: JSON.stringify({
title: subject.title, title: subject.title,
subjectId: subject.subjectId, subjectId: subject.subjectId,
to: subject.to to: subject.to
}) })
}])) }
])
);
attachments.push(card) attachments.push(card);
});
})
if (attachments.length == 0) { if (attachments.length == 0) {
const user = min.userState.get(dc.context) const user = min.userState.get(dc.context);
if (user.subjects && user.subjects.length > 0) { if (user.subjects && user.subjects.length > 0) {
await dc.context.sendActivity( await dc.context.sendActivity(
`Vamos pesquisar sobre ${KBService.getFormattedSubjectItems( Messages[locale].lets_search(
user.subjects KBService.getFormattedSubjectItems(user.subjects)
)}?` )
) );
} }
await dc.replace("/ask", {}) await dc.replace("/ask", {});
} else { } else {
msg.attachments = attachments msg.attachments = attachments;
await dc.context.sendActivity(msg) await dc.context.sendActivity(msg);
} }
const user = min.userState.get(dc.context) const user = min.userState.get(dc.context);
user.isAsking = true user.isAsking = true;
}, },
async (dc, value) => { async (dc, value) => {
var text = value var text = value;
if (text === "no" || text === "n") { // TODO: Migrate to a common. const locale = dc.context.activity.locale;
await dc.replace("/feedback") if (AzureText.isIntentNo(locale, text)) {
await dc.replace("/feedback");
} else { } else {
await dc.replace("/ask") await dc.replace("/ask");
} }
} }
]) ]);
} }
} }

View file

@ -40,6 +40,7 @@ const marked = require("marked")
const path = require("path") const path = require("path")
const asyncPromise = require('async-promises') const asyncPromise = require('async-promises')
const walkPromise = require('walk-promise') const walkPromise = require('walk-promise')
import { Messages } from "../strings";
import { Sequelize } from 'sequelize-typescript' import { Sequelize } from 'sequelize-typescript'
import { GBConfigService } from './../../core.gbapp/services/GBConfigService' import { GBConfigService } from './../../core.gbapp/services/GBConfigService'
@ -397,8 +398,7 @@ export class KBService {
format = ".md" format = ".md"
} else { } else {
logger.info(`[GBImporter] File not found: ${mediaFilename}.`) logger.info(`[GBImporter] File not found: ${mediaFilename}.`)
answer = answer = ""
"Por favor, contate a administração para rever esta pergunta."
} }
} }
@ -464,13 +464,9 @@ export class KBService {
}) })
} else if (answer.content.length > 140 && } else if (answer.content.length > 140 &&
dc.context._activity.channelId === "webchat") { dc.context._activity.channelId === "webchat") {
let messages = [ const locale = dc.context.activity.locale;
"Vou te responder na tela para melhor visualização...",
"A resposta está na tela...", await dc.context.sendActivity(Messages[locale].will_answer_projector) // TODO: Handle rnd.
"Veja a resposta na tela..."
]
await dc.context.sendActivity(messages[0]) // TODO: Handle rnd.
var html = answer.content var html = answer.content
if (answer.format === ".md") { if (answer.format === ".md") {

View file

@ -0,0 +1,34 @@
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...",
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",
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.",
will_answer_projector:"I'll answer on the projector to a better experience..."
},
"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",
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.",
will_answer_projector:"Vou te responder na tela para melhor visualização..."
}
};

View file

@ -3,8 +3,6 @@
"allowJs": false, "allowJs": false,
"baseUrl": "./", "baseUrl": "./",
"declaration": false, "declaration": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop" : true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"skipLibCheck": true, "skipLibCheck": true,