new(core.gbapp): Translator on input text available to any language.

This commit is contained in:
Rodrigo Rodriguez 2020-05-17 19:05:18 +00:00
parent 230a9e3cbc
commit f0bb5978d4
9 changed files with 126 additions and 9 deletions

View file

@ -29,4 +29,18 @@ ALTER TABLE [dbo].[GuaribasInstance] DROP COLUMN [authenticatorClientSecret]
GO GO
```
# 1.7.8
``` SQL
ALTER TABLE dbo.GuaribasUser ADD
locale nvarchar(5) NULL
GO
ALTER TABLE dbo.GuaribasInstance ADD
translatorKey nvarchar(64) NULL
translatorEndpoint nvarchar(64) NULL
GO
``` ```

View file

@ -96,6 +96,14 @@ export class GuaribasInstance extends Model<GuaribasInstance>
@Column @Column
public textAnalyticsEndpoint: string; public textAnalyticsEndpoint: string;
@Column({ type: DataType.STRING(64) })
public translatorKey: string;
@Column
@Column({ type: DataType.STRING(64) })
public translatorEndpoint: string;
@Column @Column
public marketplacePassword: string; public marketplacePassword: string;

View file

@ -44,6 +44,7 @@ import { Messages } from '../strings';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app';
import { Readable } from 'stream' import { Readable } from 'stream'
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
import { SecService } from '../../security.gblib/services/SecService';
const urlJoin = require('url-join'); const urlJoin = require('url-join');
const PasswordGenerator = require("strict-password-generator").default; const PasswordGenerator = require("strict-password-generator").default;
const Nexmo = require('nexmo'); const Nexmo = require('nexmo');
@ -53,6 +54,8 @@ const { exec } = require('child_process')
const fs = require('fs') const fs = require('fs')
const prism = require('prism-media') const prism = require('prism-media')
const sdk = require("microsoft-cognitiveservices-speech-sdk"); const sdk = require("microsoft-cognitiveservices-speech-sdk");
const uuidv4 = require('uuid/v4');
const request = require('request-promise-native');
export interface LanguagePickerSettings { export interface LanguagePickerSettings {
defaultLocale?: string; defaultLocale?: string;
@ -513,6 +516,60 @@ export class GBConversationalService {
return Promise.resolve(false); return Promise.resolve(false);
} }
async translate(
key: string,
endPoint: string,
text: string,
language: string
): Promise<string> {
let options = {
method: 'POST',
baseUrl: endPoint,
url: 'translate',
qs: {
'api-version': '3.0',
'to': [language]
},
headers: {
'Ocp-Apim-Subscription-Key': key,
'Ocp-Apim-Subscription-Region': 'westeurope',
'Content-type': 'application/json',
'X-ClientTraceId': uuidv4().toString()
},
body: [{
'text': text
}],
json: true,
}
try {
const results = await request(options);
return results[0].translations[0].text;
} catch (error) {
const msg = `Error calling Translator service layer. Error is: ${error}.`;
return Promise.reject(new Error(msg));
}
}
public async sendText(min, step, text) {
let sec = new SecService();
const member = step.context.activity.from;
const user = await sec.ensureUser(min.instance.instanceId, member.id,
member.name, "", "web", member.name);
text = await min.conversationalService.translate(
min.instance.translatorKey,
min.instance.translatorEndpoint,
text,
user.locale
);
}
public async checkLanguage(step: GBDialogStep, min, text) { public async checkLanguage(step: GBDialogStep, min, text) {
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, 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]) {

View file

@ -44,7 +44,7 @@ const removeRoute = require('express-remove-route');
const AuthenticationContext = require('adal-node').AuthenticationContext; const AuthenticationContext = require('adal-node').AuthenticationContext;
const wash = require('washyourmouthoutwithsoap'); const wash = require('washyourmouthoutwithsoap');
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder'; import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs'; import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs';
import { import {
GBDialogStep, GBDialogStep,
@ -210,7 +210,6 @@ export class GBMinService {
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.sysPackages); const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.sysPackages);
GBServer.globals.minInstances.push(min); GBServer.globals.minInstances.push(min);
await this.deployer.deployPackage(min, 'packages/default.gbdialog');
await this.deployer.deployPackage(min, 'packages/default.gbtheme'); await this.deployer.deployPackage(min, 'packages/default.gbtheme');
// Install per bot deployed packages. // Install per bot deployed packages.
@ -434,7 +433,6 @@ export class GBMinService {
MicrosoftAppCredentials.trustServiceUrl('https://directline.botframework.com', MicrosoftAppCredentials.trustServiceUrl('https://directline.botframework.com',
new Date(new Date().setFullYear(new Date().getFullYear() + 10))); new Date(new Date().setFullYear(new Date().getFullYear() + 10)));
// The minimal bot is built here. // The minimal bot is built here.
const min = new GBMinInstance(); const min = new GBMinInstance();
@ -646,7 +644,7 @@ export class GBMinService {
// Checks for global exit kewywords cancelling any active dialogs. // Checks for global exit kewywords cancelling any active dialogs.
const globalQuit = (locale, utterance) => { const globalQuit = (locale, utterance) => {
return utterance.match(Messages[locale].global_quit); return utterance.match(Messages.global_quit);
} }
const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined; const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
@ -666,7 +664,7 @@ export class GBMinService {
let args = parts.join(' '); let args = parts.join(' ');
await step.beginDialog(dialogName, { args: args }); await step.beginDialog(dialogName, { args: args });
} else if (globalQuit(step.context.activity.locale, context.activity.text)) { } else if (globalQuit(step.context.activity.locale, context.activity.text)) { // TODO: Hard-code additional languages.
await step.cancelAllDialogs(); await step.cancelAllDialogs();
await step.context.sendActivity(Messages[step.context.activity.locale].canceled); await step.context.sendActivity(Messages[step.context.activity.locale].canceled);
} else if (context.activity.text === 'admin') { } else if (context.activity.text === 'admin') {
@ -680,8 +678,29 @@ export class GBMinService {
if (step.activeDialog !== undefined) { if (step.activeDialog !== undefined) {
await step.continueDialog(); await step.continueDialog();
} else { } else {
let query = context.activity.text;
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey,
min.instance.textAnalyticsEndpoint, query);
let sec = new SecService();
const member = step.context.activity.from;
const user = await sec.ensureUser(min.instance.instanceId, member.id,
member.name, "", "web", member.name);
user.locale = locale;
await user.save();
query = await min.conversationalService.translate(
min.instance.translatorKey,
min.instance.translatorEndpoint,
query,
'pt');
GBLog.info(`Translated text: ${query}.`)
await step.beginDialog('/answer', { await step.beginDialog('/answer', {
query: context.activity.text query: query
}); });
} }
} }

View file

@ -1,5 +1,6 @@
export const Messages = { export const Messages = {
global_quit: /^(sair|sai|chega|exit|quit|finish|end|ausfahrt|verlassen)/i,
'en-US': { 'en-US': {
show_video: 'I will show you a video, please wait...', show_video: 'I will show you a video, please wait...',
good_morning: 'good morning', good_morning: 'good morning',
@ -7,7 +8,6 @@ export const Messages = {
good_night: 'good night', good_night: 'good night',
hi: (msg) => `Hello, ${msg}.`, hi: (msg) => `Hello, ${msg}.`,
very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`, very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`,
global_quit: /^(quit|Quit)/i,
canceled: 'Canceled. If I can be useful, let me know how', canceled: 'Canceled. If I can be useful, let me know how',
whats_email: "What's your E-mail address?", whats_email: "What's your E-mail address?",
validation_enter_valid_email: "Please enter a valid e-mail." validation_enter_valid_email: "Please enter a valid e-mail."
@ -19,7 +19,6 @@ export const Messages = {
good_night: 'boa noite', good_night: 'boa noite',
hi: (msg) => `Oi, ${msg}.`, hi: (msg) => `Oi, ${msg}.`,
very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`, very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`,
global_quit: /^(sair|Sair)/i,
canceled: 'Cancelado, avise como posso ser útil novamente.', canceled: 'Cancelado, avise como posso ser útil novamente.',
whats_email: "Qual seu e-mail?", whats_email: "Qual seu e-mail?",
validation_enter_valid_email: "Por favor digite um email válido." validation_enter_valid_email: "Por favor digite um email válido."

View file

@ -0,0 +1,13 @@
rem hi
talk "Qual seu nome?"
hear name
talk "Qual seu CPF?"
hear CPF
talk "Por que você abrirá este chamado?"
hear translated motivo
talk "Seu nome: " + name
talk "Você disse em Português " + motivo.

View file

@ -44,6 +44,7 @@ import { Messages } from '../strings';
import { KBService } from './../services/KBService'; import { KBService } from './../services/KBService';
import { GuaribasAnswer } from '../models'; import { GuaribasAnswer } from '../models';
import { GBMinService } from '../../../packages/core.gbapp/services/GBMinService'; import { GBMinService } from '../../../packages/core.gbapp/services/GBMinService';
import { SecService } from '../../security.gblib/services/SecService';
/** /**
* Dialog arguments. * Dialog arguments.
@ -98,7 +99,9 @@ export class AskDialog extends IGBDialog {
}, },
async step => { async step => {
if (step.result) { if (step.result) {
return await step.replaceDialog('/answer', { query: step.result });
let query = step.result;
return await step.replaceDialog('/answer', { query: query });
} else { } else {
return await step.next(); return await step.next();
} }

View file

@ -68,6 +68,9 @@ export class GuaribasUser extends Model<GuaribasUser> {
@Column public email: string; @Column public email: string;
@Column(DataType.STRING(5))
@Column public locale: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column @Column
public instanceId: number; public instanceId: number;

View file

@ -11,6 +11,7 @@ import { CollectionUtil } from 'pragmatismo-io-framework';
* Security service layer. * Security service layer.
*/ */
export class SecService extends GBService { export class SecService extends GBService {
public async importSecurityFile(localPath: string, instance: IGBInstance) { public async importSecurityFile(localPath: string, instance: IGBInstance) {
const security = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'security.json'), 'utf8')); const security = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'security.json'), 'utf8'));
await CollectionUtil.asyncForEach(security.groups, async group => { await CollectionUtil.asyncForEach(security.groups, async group => {