From d0c04ed6764d479ee66b1387a7da2f3a7955ed33 Mon Sep 17 00:00:00 2001 From: Rodrigo Rodriguez Date: Tue, 19 May 2020 12:36:17 -0300 Subject: [PATCH] new(core.gbapp): New activationCode for multiple bot activation on the same channel. --- DATABASE-CHANGES.md | 8 ++ packages/admin.gbapp/dialogs/AdminDialog.ts | 134 ++++++++++++------ packages/core.gbapp/models/GBModel.ts | 3 + packages/core.gbapp/services/GBDeployer.ts | 1 + packages/core.gbapp/services/GBMinService.ts | 4 +- packages/core.gbapp/strings.ts | 14 +- .../services/WhatsappDirectLine.ts | 2 +- 7 files changed, 117 insertions(+), 49 deletions(-) diff --git a/DATABASE-CHANGES.md b/DATABASE-CHANGES.md index 548ce2a6..55c25c00 100644 --- a/DATABASE-CHANGES.md +++ b/DATABASE-CHANGES.md @@ -43,4 +43,12 @@ ALTER TABLE dbo.GuaribasInstance ADD translatorEndpoint nvarchar(64) NULL GO +# 1.7.9 + +ALTER TABLE dbo.GuaribasInstance ADD + activationCode nvarchar(16) NULL +GO + + + ``` \ No newline at end of file diff --git a/packages/admin.gbapp/dialogs/AdminDialog.ts b/packages/admin.gbapp/dialogs/AdminDialog.ts index 230ac39d..e1dd4a18 100644 --- a/packages/admin.gbapp/dialogs/AdminDialog.ts +++ b/packages/admin.gbapp/dialogs/AdminDialog.ts @@ -40,13 +40,11 @@ const crypto = require('crypto'); import { WaterfallDialog } from 'botbuilder-dialogs'; import { GBMinInstance, IGBDialog, GBLog } from 'botlib'; import urlJoin = require('url-join'); -import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { GBImporter } from '../../core.gbapp/services/GBImporterService'; import { Messages } from '../strings'; import { GBAdminService } from '../services/GBAdminService'; import { CollectionUtil } from 'pragmatismo-io-framework'; -import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; /** @@ -54,6 +52,15 @@ import { GBConversationalService } from '../../core.gbapp/services/GBConversatio */ export class AdminDialog extends IGBDialog { + + public static isIntentYes(locale, utterance) { + return utterance.toLowerCase().match(Messages[locale].affirmative_sentences); + } + + public static isIntentNo(locale, utterance) { + return utterance.toLowerCase().match(Messages[locale].negative_sentences); + } + /** * Setup dialogs flows and define services call. * @@ -75,7 +82,7 @@ export class AdminDialog extends IGBDialog { const locale = step.context.activity.locale; const prompt = Messages[locale].authenticate; - return await min.conversationalService.prompt (min, step, prompt); + return await min.conversationalService.prompt(min, step, prompt); }, async step => { const locale = step.context.activity.locale; @@ -84,7 +91,7 @@ export class AdminDialog extends IGBDialog { if (sensitive === min.instance.adminPass) { await min.conversationalService.sendText(min, step, Messages[locale].welcome); - return await min.conversationalService.prompt (min, step, Messages[locale].which_task); + return await min.conversationalService.prompt(min, step, Messages[locale].which_task); } else { await min.conversationalService.sendText(min, step, Messages[locale].wrong_password); @@ -155,57 +162,96 @@ export class AdminDialog extends IGBDialog { min.dialogs.add( new WaterfallDialog('/publish', [ async step => { - const botId = min.instance.botId; + if (step.activeDialog.state.options.confirm) { + return await step.next('sim'); + } + else { + const locale = step.context.activity.locale; + return await min.conversationalService.prompt(min, step, Messages[locale].publish_type_yes); + } + }, + async step => { const locale = step.context.activity.locale; - await min.conversationalService.sendText(min, step, Messages[locale].working('Publishing')); - - step.activeDialog.state.options.args = (step.options as any).args; - let args = step.activeDialog.state.options.args.split(' '); - let filename = args[0]; - const packages = []; - if (filename === null) { - await min.conversationalService.sendText(min, step, `Starting publishing for all bot packages...`); - packages.push(`${botId}.gbkb`); - packages.push(`${botId}.gbdialog`); - packages.push(`${botId}.gbot`); - packages.push(`${botId}.gbtheme`); - packages.push(`${botId}.gbapp`); - packages.push(`${botId}.gblib`); - } else { - await min.conversationalService.sendText(min, step, `Starting publishing for ${filename}...`); - packages.push(filename); - } - try { + // If the user says yes, starts publishing. - await CollectionUtil.asyncForEach(packages, async packageName => { + if (AdminDialog.isIntentYes(locale, step.result)) { - const cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${process.env.STORAGE_LIBRARY}/${botId}.gbai/${packageName}`; + let from = step.context.activity.from.id; - if (await (deployer as any).getStoragePackageByName(min.instance.instanceId, - packageName) !== null) { // TODO: Move to interface. - const cmd2 = `undeployPackage ${packageName}`; - await GBAdminService.undeployPackageCommand(cmd2, min); + if (AdminDialog.canSendBroadcast(from)) { + + const botId = min.instance.botId; + const locale = step.context.activity.locale; + await min.conversationalService.sendText(min, step, Messages[locale].working('Publishing')); + + step.activeDialog.state.options.args = (step.options as any).args; + let args = step.activeDialog.state.options.args.split(' '); + let filename = args[0]; + const packages = []; + if (filename === null) { + await min.conversationalService.sendText(min, step, `Starting publishing for all bot packages...`); + packages.push(`${botId}.gbkb`); + packages.push(`${botId}.gbdialog`); + packages.push(`${botId}.gbot`); + packages.push(`${botId}.gbtheme`); + packages.push(`${botId}.gbapp`); + packages.push(`${botId}.gblib`); + } else { + await min.conversationalService.sendText(min, step, `Starting publishing for ${filename}...`); + packages.push(filename); } - await GBAdminService.deployPackageCommand(min, cmd1, deployer); - if (packageName.endsWith('.gbkb')) { - await min.conversationalService.sendText(min, step, 'Rebuilding my own index, wait a minute, please...'); - await GBAdminService.rebuildIndexPackageCommand(min, deployer); + + try { + + await CollectionUtil.asyncForEach(packages, async packageName => { + + const cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${process.env.STORAGE_LIBRARY}/${botId}.gbai/${packageName}`; + + if (await (deployer as any).getStoragePackageByName(min.instance.instanceId, + packageName) !== null) { // TODO: Move to interface. + const cmd2 = `undeployPackage ${packageName}`; + await GBAdminService.undeployPackageCommand(cmd2, min); + } + await GBAdminService.deployPackageCommand(min, cmd1, deployer); + if (packageName.endsWith('.gbkb')) { + await min.conversationalService.sendText(min, step, 'Rebuilding my own index, wait a minute, please...'); + await GBAdminService.rebuildIndexPackageCommand(min, deployer); + } + await min.conversationalService.sendText(min, step, `Finished publishing ${packageName}.`); + }); + } catch (error) { + await min.conversationalService.sendText(min, step, error.message); + } + await min.conversationalService.sendText(min, step, Messages[locale].publish_success); + if (!step.activeDialog.state.options.confirm) { + return await step.replaceDialog('/ask', { isReturning: true }); + } + else + { + return await step.endDialog(); } - await min.conversationalService.sendText(min, step, `Finished publishing ${packageName}.`); - }); - return await step.replaceDialog('/ask', { isReturning: true }); - - } catch (error) { - await min.conversationalService.sendText(min, step, error.message); + } else { + await min.conversationalService.sendText(min, step, Messages[locale].publish_must_be_admin); + } } - await step.replaceDialog('/ask', { isReturning: true }); - + await min.conversationalService.sendText(min, step, Messages[locale].publish_canceled); }])); } + /** +* Check if the specified phone can receive a message by running +* the /broadcast command with specific phone numbers. +* @param phone Phone number to check (eg.: +5521900002233) +*/ + public static canSendBroadcast(phone: string): Boolean { + const list = process.env.SECURITY_CAN_PUBLISH.split(';'); + return list.includes(phone); + } + + private static setupSecurityDialogs(min: GBMinInstance) { min.dialogs.add( new WaterfallDialog('/setupSecurity', [ @@ -213,14 +259,14 @@ export class AdminDialog extends IGBDialog { const locale = step.context.activity.locale; const prompt = Messages[locale].enter_authenticator_tenant; - return await min.conversationalService.prompt (min, step, prompt); + return await min.conversationalService.prompt(min, step, prompt); }, async step => { step.activeDialog.state.authenticatorTenant = step.result; const locale = step.context.activity.locale; const prompt = Messages[locale].enter_authenticator_authority_host_url; - return await min.conversationalService.prompt (min, step, prompt); + return await min.conversationalService.prompt(min, step, prompt); }, async step => { step.activeDialog.state.authenticatorAuthorityHostUrl = step.result; diff --git a/packages/core.gbapp/models/GBModel.ts b/packages/core.gbapp/models/GBModel.ts index 630c9fb3..b86d85da 100644 --- a/packages/core.gbapp/models/GBModel.ts +++ b/packages/core.gbapp/models/GBModel.ts @@ -75,6 +75,9 @@ export class GuaribasInstance extends Model @Column public title: string; + @Column({ type: DataType.STRING(16) }) + public activationCode: string; + @Column public description: string; diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts index f2ddd1be..35b2e526 100644 --- a/packages/core.gbapp/services/GBDeployer.ts +++ b/packages/core.gbapp/services/GBDeployer.ts @@ -164,6 +164,7 @@ export class GBDeployer implements IGBDeployer { accessToken, (application as any).id); instance.adminPass = GBAdminService.getRndPassword(); instance.title = botId; + instance.activationCode = instance.botId; await this.core.saveInstance(instance); diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index d14f965c..2ddf3a3c 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -145,11 +145,11 @@ export class GBMinService { let activeMin; if (process.env.WHATSAPP_WELCOME_DISABLED !== "true") { - const toSwitchMin = GBServer.globals.minInstances.filter(p => p.instance.botId === text)[0]; + const toSwitchMin = GBServer.globals.minInstances.filter(p => p.instance.activationCode === text)[0]; activeMin = toSwitchMin ? toSwitchMin : GBServer.globals.minBoot; let sec = new SecService(); - const instance = await this.core.loadInstanceByBotId(activeMin.botId); + let user = await sec.getUserFromSystemId(id); if (user === null) { diff --git a/packages/core.gbapp/strings.ts b/packages/core.gbapp/strings.ts index 0488a15b..80319359 100644 --- a/packages/core.gbapp/strings.ts +++ b/packages/core.gbapp/strings.ts @@ -10,7 +10,12 @@ export const Messages = { very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`, canceled: 'Canceled. If I can be useful, let me know how', 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." , + affirmative_sentences: /^(sim|s|positivo|afirmativo|claro|evidente|sem dúvida|confirmo|confirmar|confirmado|uhum)/i, + publish_must_be_admin: 'Seu telefone precisa estar com privilégios administrativos para realizar publicação.', + publish_success: 'Publicação realizada.', + publish_type_yes: 'Por favor, digite *Sim* para continuar com a publicação.', + publish_canceled: 'Publicação cancelada.', }, 'pt-BR': { show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...', @@ -21,6 +26,11 @@ export const Messages = { very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`, canceled: 'Cancelado, avise como posso ser útil novamente.', 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.", + affirmative_sentences: /^(sim|s|positivo|afirmativo|claro|evidente|sem dúvida|confirmo|confirmar|confirmado|uhum)/i, + publish_must_be_admin: 'Seu telefone precisa estar com privilégios administrativos para realizar publicação.', + publish_success: 'Publicação realizada.', + publish_type_yes: 'Por favor, digite *Sim* para continuar com a publicação.', + publish_canceled: 'Publicação cancelada.', } }; diff --git a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts index ae3ade64..20cb808a 100644 --- a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts +++ b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts @@ -271,7 +271,7 @@ export class WhatsappDirectLine extends GBService { conversationId: conversationId, activity: { textFormat: 'plain', - text: text, + text: text, type: 'message', from: { id: from,