Menu is back again in BtFmwV4.

This commit is contained in:
Rodrigo Rodriguez 2018-09-12 04:42:29 -03:00
parent 3a3a1e2546
commit 911aa77b11
3 changed files with 142 additions and 131 deletions

View file

@ -30,12 +30,12 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
"use strict" "use strict";
const { TextPrompt } = require("botbuilder-dialogs") const { TextPrompt } = require("botbuilder-dialogs");
const UrlJoin = require("url-join") const UrlJoin = require("url-join");
const express = require("express") const express = require("express");
const logger = require("../../../src/logger") const logger = require("../../../src/logger");
import { import {
BotFrameworkAdapter, BotFrameworkAdapter,
@ -43,32 +43,29 @@ import {
ConversationState, ConversationState,
MemoryStorage, MemoryStorage,
UserState UserState
} from "botbuilder" } from "botbuilder";
import { GBCoreService } from "./GBCoreService" import { GBCoreService } from "./GBCoreService";
import { GBConversationalService } from "./GBConversationalService" import { GBConversationalService } from "./GBConversationalService";
import * as request from "request-promise-native" import * as request from "request-promise-native";
import { import { GBMinInstance, IGBPackage } from "botlib";
GBMinInstance, import { GBAnalyticsPackage } from "../../analytics.gblib";
IGBPackage, import { GBCorePackage } from "../../core.gbapp";
} from "botlib" import { GBKBPackage } from "../../kb.gbapp";
import { GBAnalyticsPackage } from "../../analytics.gblib" import { GBDeployer } from "./GBDeployer";
import { GBCorePackage } from "../../core.gbapp" import { GBSecurityPackage } from "../../security.gblib";
import { GBKBPackage } from "../../kb.gbapp" import { GBAdminPackage } from "./../../admin.gbapp/index";
import { GBDeployer } from "./GBDeployer" import { GBCustomerSatisfactionPackage } from "../../customer-satisfaction.gbapp";
import { GBSecurityPackage } from "../../security.gblib" import { GBWhatsappPackage } from "../../whatsapp.gblib";
import { GBAdminPackage } from "./../../admin.gbapp/index"
import { GBCustomerSatisfactionPackage } from "../../customer-satisfaction.gbapp"
import { GBWhatsappPackage } from "../../whatsapp.gblib"
/** Minimal service layer for a bot. */ /** Minimal service layer for a bot. */
export class GBMinService { export class GBMinService {
core: GBCoreService core: GBCoreService;
conversationalService: GBConversationalService conversationalService: GBConversationalService;
deployer: GBDeployer deployer: GBDeployer;
corePackage = "core.gbai" corePackage = "core.gbai";
/** /**
* Static initialization of minimal instance. * Static initialization of minimal instance.
@ -80,9 +77,9 @@ export class GBMinService {
conversationalService: GBConversationalService, conversationalService: GBConversationalService,
deployer: GBDeployer deployer: GBDeployer
) { ) {
this.core = core this.core = core;
this.conversationalService = conversationalService this.conversationalService = conversationalService;
this.deployer = deployer this.deployer = deployer;
} }
/** /**
@ -102,20 +99,20 @@ export class GBMinService {
): Promise<GBMinInstance> { ): Promise<GBMinInstance> {
// Serves default UI on root address '/'. // Serves default UI on root address '/'.
let uiPackage = "default.gbui" let uiPackage = "default.gbui";
server.use( server.use(
"/", "/",
express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, "build")) express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, "build"))
) );
// Loads all bot instances from storage and starting loading them. // Loads all bot instances from storage and starting loading them.
let instances = await this.core.loadInstances() let instances = await this.core.loadInstances();
Promise.all( Promise.all(
instances.map(async instance => { instances.map(async instance => {
// Gets the authorization key for each instance from Bot Service. // Gets the authorization key for each instance from Bot Service.
let webchatToken = await this.getWebchatToken(instance) let webchatToken = await this.getWebchatToken(instance);
// Serves the bot information object via HTTP so clients can get // Serves the bot information object via HTTP so clients can get
// instance information stored on server. // instance information stored on server.
@ -124,10 +121,10 @@ export class GBMinService {
(async () => { (async () => {
// Returns the instance object to clients requesting bot info. // Returns the instance object to clients requesting bot info.
let botId = req.params.botId let botId = req.params.botId;
let instance = await this.core.loadInstance(botId) let instance = await this.core.loadInstance(botId);
if (instance) { if (instance) {
let speechToken = await this.getSTSToken(instance) let speechToken = await this.getSTSToken(instance);
res.send( res.send(
JSON.stringify({ JSON.stringify({
@ -138,28 +135,28 @@ export class GBMinService {
speechToken: speechToken, speechToken: speechToken,
conversationId: webchatToken.conversationId conversationId: webchatToken.conversationId
}) })
) );
} else { } else {
let error = `Instance not found: ${botId}.` let error = `Instance not found: ${botId}.`;
res.sendStatus(error) res.sendStatus(error);
logger.error(error) logger.error(error);
} }
})() })();
}) });
// Build bot adapter. // Build bot adapter.
var { min, adapter, conversationState } = await this.buildBotAdapter( var { min, adapter, conversationState } = await this.buildBotAdapter(
instance instance
) );
// Call the loadBot context.activity for all packages. // Call the loadBot context.activity for all packages.
this.invokeLoadBot(appPackages, min, server) this.invokeLoadBot(appPackages, min, server);
// Serves individual URL for each bot conversational interface... // Serves individual URL for each bot conversational interface...
let url = `/api/messages/${instance.botId}` let url = `/api/messages/${instance.botId}`;
server.post(url, async (req, res) => { server.post(url, async (req, res) => {
return this.receiver( return this.receiver(
adapter, adapter,
@ -169,20 +166,20 @@ export class GBMinService {
min, min,
instance, instance,
appPackages appPackages
) );
}) });
logger.info( logger.info(
`GeneralBots(${instance.engineName}) listening on: ${url}.` `GeneralBots(${instance.engineName}) listening on: ${url}.`
) );
// Serves individual URL for each bot user interface. // Serves individual URL for each bot user interface.
let uiUrl = `/${instance.botId}` let uiUrl = `/${instance.botId}`;
server.use( server.use(
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} acessible at: ${uiUrl}.`);
// Setups handlers. // Setups handlers.
// send: function (context.activity, next) { // send: function (context.activity, next) {
@ -199,32 +196,32 @@ export class GBMinService {
// ) // )
// next() // next()
}) })
) );
} }
private async buildBotAdapter(instance: any) { private async buildBotAdapter(instance: any) {
let adapter = new BotFrameworkAdapter({ let adapter = new BotFrameworkAdapter({
appId: instance.marketplaceId, appId: instance.marketplaceId,
appPassword: instance.marketplacePassword appPassword: instance.marketplacePassword
}) });
const storage = new MemoryStorage() const storage = new MemoryStorage();
const conversationState = new ConversationState(storage) const conversationState = new ConversationState(storage);
const userState = new UserState(storage) const userState = new UserState(storage);
adapter.use(new BotStateSet(conversationState, userState)) adapter.use(new BotStateSet(conversationState, userState));
// The minimal bot is built here. // The minimal bot is built here.
let min = new GBMinInstance() let min = new GBMinInstance();
min.botId = instance.botId min.botId = instance.botId;
min.bot = adapter min.bot = adapter;
min.userState = userState min.userState = userState;
min.core = this.core min.core = this.core;
min.conversationalService = this.conversationalService min.conversationalService = this.conversationalService;
min.instance = await this.core.loadInstance(min.botId) min.instance = await this.core.loadInstance(min.botId);
min.dialogs.add("textPrompt", new TextPrompt()) min.dialogs.add("textPrompt", new TextPrompt());
return { min, adapter, conversationState } return { min, adapter, conversationState };
} }
private invokeLoadBot(appPackages: any[], min: any, server: any) { private invokeLoadBot(appPackages: any[], min: any, server: any) {
@ -240,19 +237,19 @@ export class GBMinService {
GBCustomerSatisfactionPackage, GBCustomerSatisfactionPackage,
GBWhatsappPackage GBWhatsappPackage
].forEach(sysPackage => { ].forEach(sysPackage => {
logger.info(`Loading sys package: ${sysPackage.name}...`) logger.info(`Loading sys package: ${sysPackage.name}...`);
let p = Object.create(sysPackage.prototype) as IGBPackage let p = Object.create(sysPackage.prototype) as IGBPackage;
p.loadBot(min) p.loadBot(min);
e.sysPackages.push(p) e.sysPackages.push(p);
if (sysPackage.name === "GBWhatsappPackage") { if (sysPackage.name === "GBWhatsappPackage") {
let url = "/instances/:botId/whatsapp" let url = "/instances/:botId/whatsapp";
server.post(url, (req, res) => { server.post(url, (req, res) => {
p["channel"].received(req, res) p["channel"].received(req, res);
}) });
} }
}, this) }, this);
e.loadBot(min) e.loadBot(min);
}, this) }, this);
} }
/** /**
@ -268,73 +265,75 @@ export class GBMinService {
appPackages: any[] appPackages: any[]
) { ) {
return adapter.processActivity(req, res, async context => { return adapter.processActivity(req, res, async context => {
const state = conversationState.get(context) const state = conversationState.get(context);
const dc = min.dialogs.createContext(context, state) const dc = min.dialogs.createContext(context, state);
const user = min.userState.get(dc.context) const user = min.userState.get(dc.context);
if (!user.loaded) { if (!user.loaded) {
await min.conversationalService.sendEvent(dc, "loadInstance", { await min.conversationalService.sendEvent(dc, "loadInstance", {
instanceId: instance.instanceId, instanceId: instance.instanceId,
botId: instance.botId, botId: instance.botId,
theme: instance.theme, theme: instance.theme,
secret: instance.webchatKey secret: instance.webchatKey
}) });
user.loaded = true user.loaded = true;
user.subjects = [] user.subjects = [];
} }
logger.info(`[RCV]: ${context.activity.type}, ChannelID: ${
context.activity.channelId logger.info(
}, `[RCV]: ${context.activity.type}, ChannelID: ${
ConversationID: ${context.activity.conversation.id}, context.activity.channelId
Name: ${context.activity.name}, Text: ${ }, Name: ${context.activity.name}, Text: ${context.activity.text}.`
context.activity.text );
}.`)
if ( if (
context.activity.type === "conversationUpdate" && context.activity.type === "conversationUpdate" &&
context.activity.membersAdded.length > 0 context.activity.membersAdded.length > 0
) { ) {
let member = context.activity.membersAdded[0] let member = context.activity.membersAdded[0];
if (member.name === "GeneralBots") { if (member.name === "GeneralBots") {
logger.info(`Bot added to conversation, starting chat...`) logger.info(`Bot added to conversation, starting chat...`);
appPackages.forEach(e => { appPackages.forEach(e => {
e.onNewSession(min, dc) e.onNewSession(min, dc);
}) });
await dc.begin("/") await dc.begin("/");
} else { } else {
logger.info(`Member added to conversation: ${member.name}`) logger.info(`Member added to conversation: ${member.name}`);
} }
} else if (context.activity.type === "message") { } else if (context.activity.type === "message") {
// Check to see if anyone replied. If not then start echo dialog // Check to see if anyone replied. If not then start echo dialog
if (context.activity.text === "admin") { if (context.activity.text === "admin") {
await dc.begin("/admin") await dc.begin("/admin");
} else if (context.activity.text.startsWith("{\"title\"")) {
await dc.begin("/menu", {data:JSON.parse(context.activity.text)});
} else { } else {
await dc.continue() await dc.continue();
} }
} else if (context.activity.type === "event") { } else if (context.activity.type === "event") {
if (context.activity.name === "whoAmI") { if (context.activity.name === "whoAmI") {
await dc.begin("/whoAmI") await dc.begin("/whoAmI");
} else if (context.activity.name === "showSubjects") { } else if (context.activity.name === "showSubjects") {
await dc.begin("/menu") await dc.begin("/menu");
} else if (context.activity.name === "giveFeedback") { } else if (context.activity.name === "giveFeedback") {
await dc.begin("/feedback", { await dc.begin("/feedback", {
fromMenu: true fromMenu: true
}) });
} else if (context.activity.name === "showFAQ") { } else if (context.activity.name === "showFAQ") {
await dc.begin("/faq") await dc.begin("/faq");
} else if (context.activity.name === "ask") { } else if (context.activity.name === "ask") {
await dc.begin("/answer", { await dc.begin("/answer", {
query: (context.activity as any).data, query: (context.activity as any).data,
fromFaq: true fromFaq: true
}) });
} else if (context.activity.name === "quality") { } else if (context.activity.name === "quality") {
await dc.begin("/quality", { await dc.begin("/quality", {
// TODO: score: context.activity.data // TODO: score: context.activity.data
}) });
} else { } else {
await dc.continue() await dc.continue();
} }
} }
}) });
} }
/** /**
@ -350,15 +349,15 @@ export class GBMinService {
headers: { headers: {
Authorization: `Bearer ${instance.webchatKey}` Authorization: `Bearer ${instance.webchatKey}`
} }
} };
try { try {
let json = await request(options) let json = await request(options);
return Promise.resolve(JSON.parse(json)) return Promise.resolve(JSON.parse(json));
} catch (error) { } catch (error) {
let msg = `Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.` let msg = `Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
logger.error(msg) logger.error(msg);
return Promise.reject(msg) return Promise.reject(msg);
} }
} }
@ -377,14 +376,14 @@ export class GBMinService {
headers: { headers: {
"Ocp-Apim-Subscription-Key": instance.speechKey "Ocp-Apim-Subscription-Key": instance.speechKey
} }
} };
try { try {
return await request(options) return await request(options);
} catch (error) { } catch (error) {
let msg = `Error calling Speech to Text client. Error is: ${error}.` let msg = `Error calling Speech to Text client. Error is: ${error}.`;
logger.error(msg) logger.error(msg);
return Promise.reject(msg) return Promise.reject(msg);
} }
} }
} }

View file

@ -32,14 +32,13 @@
"use strict" "use strict"
import { CSService } from '../services/CSService' import { CSService } from "../services/CSService"
import { AzureText } from "pragmatismo-io-framework" import { AzureText } from "pragmatismo-io-framework"
import { GBMinInstance } from "botlib" import { GBMinInstance } from "botlib"
import { IGBDialog } from "botlib" import { IGBDialog } from "botlib"
import { BotAdapter } from 'botbuilder' import { BotAdapter } from "botbuilder"
export class FeedbackDialog extends IGBDialog { export class FeedbackDialog extends IGBDialog {
/** /**
* Setup dialogs flows and define services call. * Setup dialogs flows and define services call.
* *
@ -47,17 +46,22 @@ 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 messages = [
"O que achou do meu atendimento, de 1 a 5?", "O que achou do meu atendimento, de 1 a 5?",
"Qual a nota do meu atendimento?", "Qual a nota do meu atendimento?",
"Como define meu atendimento numa escala de 1 a 5?" "Como define meu atendimento numa escala de 1 a 5?"
] ]
await dc.prompt('choicePrompt', messages[0], ['1', '2', '3', '4', ' 5']) await dc.prompt("choicePrompt", messages[0], [
"1",
"2",
"3",
"4",
" 5"
])
}, },
async (dc, value) => { async (dc, value) => {
let rate = value.entity let rate = value.entity
@ -83,21 +87,29 @@ export class FeedbackDialog extends IGBDialog {
"Como foi meu atendimento?", "Como foi meu atendimento?",
"Gostaria de dizer algo sobre meu atendimento?" "Gostaria de dizer algo sobre meu atendimento?"
] ]
await dc.prompt('textPrompt', messages[0]) await dc.prompt("textPrompt", messages[0])
}, },
async (dc, value) => { async (dc, value) => {
let rate = await AzureText.getSentiment(min.instance.textAnalyticsKey, let rate = await AzureText.getSentiment(
min.instance.textAnalyticsKey,
min.instance.textAnalyticsServerUrl, min.instance.textAnalyticsServerUrl,
min.conversationalService.getCurrentLanguage(dc), value) min.conversationalService.getCurrentLanguage(dc),
value
)
if (rate > 0) { if (rate > 0.50) {
await dc.context.sendActivity("Bom saber que você gostou. Conte comigo.") await dc.context.sendActivity(
"Bom saber que você gostou. Conte comigo."
)
} else { } else {
await dc.context.sendActivity( await dc.context.sendActivity(
"Vamos registrar sua questão, obrigado pela sinceridade." "Vamos registrar sua questão, obrigado pela sinceridade."
) )
// TODO: Record.
} }
await dc.replace('/ask', { isReturning: true }) await dc.replace("/ask", { isReturning: true })
}]) }
])
} }
} }

View file

@ -63,7 +63,7 @@ export class MenuDialog extends IGBDialog {
// } // }
if (args && args.data) { if (args && args.data) {
var subject = JSON.parse(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.