From 7348c5489433eb82a44af12b4a8f4e76f01998b4 Mon Sep 17 00:00:00 2001 From: rodrigorodriguez Date: Sun, 1 Jan 2023 14:24:53 -0300 Subject: [PATCH] fix(all): TODO items removed or moved to ALM. --- packages/core.gbapp/services/GBMinService.ts | 62 ++--- packages/default.gbdialog/bot.vbs.gbignore | 3 - .../security.gbapp/services/SecService.ts | 1 - .../services/WhatsappDirectLine.ts | 222 +++++++++--------- src/app.ts | 13 +- 5 files changed, 145 insertions(+), 156 deletions(-) diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 40face4c..3313752f 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -123,7 +123,7 @@ export class GBMinService { /** * Static initialization of minimal instance. */ - constructor ( + constructor( core: IGBCoreService, conversationalService: IGBConversationalService, adminService: IGBAdminService, @@ -138,7 +138,7 @@ export class GBMinService { /** * Constructs a new minimal instance for each bot. */ - public async buildMin (instances: IGBInstance[]) { + public async buildMin(instances: IGBInstance[]) { // Servers default UI on root address '/' if web enabled. if (process.env.DISABLE_WEB !== 'true') { @@ -184,7 +184,7 @@ export class GBMinService { const MAX_IN_PROCESS = 20; const results = new Array(promises.length); - async function doBlock (startIndex) { + async function doBlock(startIndex) { // Shallow-copy a block of promises to work on const currBlock = promises.slice(startIndex, startIndex + MAX_IN_PROCESS); // Await the completion. If any fail, it will throw and that's good. @@ -233,7 +233,7 @@ export class GBMinService { * Removes bot endpoint from web listeners and remove bot instance * from list of global server bot instances. */ - public async unmountBot (botId: string) { + public async unmountBot(botId: string) { const url = `/api/messages/${botId}`; removeRoute(GBServer.globals.server, url); @@ -248,7 +248,7 @@ export class GBMinService { * serving bot endpoint in several URL like WhatsApp endpoint, .gbkb assets, * installing all BASIC artifacts from .gbdialog and OAuth2. */ - public async mountBot (instance: IGBInstance) { + public async mountBot(instance: IGBInstance) { // Build bot adapter. const { min, adapter, conversationState } = await this.buildBotAdapter( @@ -256,7 +256,7 @@ export class GBMinService { GBServer.globals.sysPackages, GBServer.globals.appPackages ); - + // https://github.com/GeneralBots/BotServer/issues/286 // min['groupCache'] = await KBService.getGroupReplies(instance.instanceId); @@ -411,7 +411,7 @@ export class GBMinService { GBDeployer.mountGBKBAssets(`${instance.botId}.gbkb`, instance.botId, `${instance.botId}.gbkb`); } - public static isChatAPI (req, res) { + public static isChatAPI(req, res) { if (!res) { return 'GeneralBots'; } @@ -422,7 +422,7 @@ export class GBMinService { * Creates a listener that can be used by external monitors to check * bot instance health. */ - private createCheckHealthAddress (server: any, min: GBMinInstance, instance: IGBInstance) { + private createCheckHealthAddress(server: any, min: GBMinInstance, instance: IGBInstance) { server.get(`/${min.instance.botId}/check`, async (req, res) => { try { // Performs the checking of WhatsApp API if enabled for this instance. @@ -453,7 +453,7 @@ export class GBMinService { * Handle OAuth2 web service calls for token requests * on https:////token URL. */ - private handleOAuthTokenRequests (server: any, min: GBMinInstance, instance: IGBInstance) { + private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) { server.get(`/${min.instance.botId}/token`, async (req, res) => { // Checks request state by reading AntiCSRFAttackState from GB Admin infrastructure. @@ -502,7 +502,7 @@ export class GBMinService { * Handle OAuth2 web service calls for authorization requests * on https:////auth URL. */ - private handleOAuthRequests (server: any, min: GBMinInstance) { + private handleOAuthRequests(server: any, min: GBMinInstance) { server.get(`/${min.instance.botId}/auth`, (req, res) => { let authorizationUrl = urlJoin( min.instance.authenticatorAuthorityHostUrl, @@ -520,7 +520,7 @@ export class GBMinService { /** * Returns the instance object to clients requesting bot info. */ - private async handleGetInstanceForClient (req: any, res: any) { + private async handleGetInstanceForClient(req: any, res: any) { // Translates the requested botId. let botId = req.params.botId; @@ -577,7 +577,7 @@ export class GBMinService { /** * Gets Webchat token from Bot Service. */ - private async getWebchatToken (instance: any) { + private async getWebchatToken(instance: any) { const url = 'https://directline.botframework.com/v3/directline/tokens/generate'; const options = { method: 'POST', @@ -600,7 +600,7 @@ export class GBMinService { /** * Gets a Speech to Text / Text to Speech token from the provider. */ - private async getSTSToken (instance: any) { + private async getSTSToken(instance: any) { const options = { method: 'POST', headers: { @@ -620,7 +620,7 @@ export class GBMinService { /** * Builds the BOT Framework & GB infrastructures. */ - private async buildBotAdapter (instance: any, sysPackages: IGBPackage[], appPackages: IGBPackage[]) { + private async buildBotAdapter(instance: any, sysPackages: IGBPackage[], appPackages: IGBPackage[]) { // MSFT stuff. const adapter = new BotFrameworkAdapter({ @@ -715,7 +715,7 @@ export class GBMinService { await min.whatsAppDirectLine.setup(true); } else { const minBoot = GBServer.globals.minBoot as any; - if(minBoot.whatsappServiceKey){ + if (minBoot.whatsappServiceKey) { min.whatsAppDirectLine = new WhatsappDirectLine( min, min.botId, @@ -755,7 +755,7 @@ export class GBMinService { /** * Performs calling of loadBot event in all .gbapps. */ - private async invokeLoadBot (appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance) { + private async invokeLoadBot(appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance) { // Calls loadBot event in all .gbapp packages. await CollectionUtil.asyncForEach(sysPackages, async p => { @@ -789,7 +789,7 @@ export class GBMinService { } // https://github.com/GeneralBots/BotServer/issues/313 - public static userMobile (step) { + public static userMobile(step) { let mobile = WhatsappDirectLine.mobiles[step.context.activity.conversation.id]; if (!mobile && step) { @@ -802,7 +802,7 @@ export class GBMinService { /** * BOT Framework web service hook method. */ - private async receiver ( + private async receiver( req: any, res: any, conversationState: ConversationState, @@ -937,9 +937,7 @@ export class GBMinService { // Required for F0 handling of persisted conversations. GBLog.info( - `Input> ${context.activity.text} (type: ${context.activity.type}, name: ${ - context.activity.name - }, channelId: ${context.activity.channelId})` + `Input> ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})` ); // Answer to specific BOT Framework event conversationUpdate to auto start dialogs. @@ -1030,7 +1028,7 @@ export class GBMinService { /** * Called to handle all event sent by .gbui clients. */ - private async processEventActivity (min, user, context, step: GBDialogStep) { + private async processEventActivity(min, user, context, step: GBDialogStep) { if (context.activity.name === 'whoAmI') { await step.beginDialog('/whoAmI'); } else if (context.activity.name === 'showSubjects') { @@ -1065,10 +1063,20 @@ export class GBMinService { } } + /** + * + * Checks for global exit kewywords cancelling any active dialogs. + * + * */ + + public static isGlobalQuitUtterance(locale, utterance) { + return utterance.match(Messages.global_quit); + } + /** * Called to handle all text messages sent and received by the bot. */ - private async processMessageActivity (context, min: GBMinInstance, step: GBDialogStep) { + private async processMessageActivity(context, min: GBMinInstance, step: GBDialogStep) { const sec = new SecService(); if (!context.activity.text) { @@ -1109,12 +1117,6 @@ export class GBMinService { } } - // Checks for global exit kewywords cancelling any active dialogs. - - const globalQuit = (locale, utterance) => { - return utterance.match(Messages.global_quit); - }; - // Files in .gbdialog can be called directly by typing its name normalized into JS . const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined; @@ -1142,7 +1144,7 @@ export class GBMinService { } else { await step.beginDialog(cmdOrDialogName, { args: args }); } - } else if (globalQuit(step.context.activity.locale, context.activity.text)) { + } else if (GBMinService.isGlobalQuitUtterance(step.context.activity.locale, context.activity.text)) { await step.cancelAllDialogs(); await min.conversationalService.sendText(min, step, Messages[step.context.activity.locale].canceled); } else if (context.activity.text === 'admin') { diff --git a/packages/default.gbdialog/bot.vbs.gbignore b/packages/default.gbdialog/bot.vbs.gbignore index a42382e3..465c8ad0 100644 --- a/packages/default.gbdialog/bot.vbs.gbignore +++ b/packages/default.gbdialog/bot.vbs.gbignore @@ -54,9 +54,6 @@ else talk "The maximum number of payments is 60" end if - - ' TODO: This must be reviewed in terms of financing logic. - nInstallments = parseInt(installments) vamount = parseFloat(amount) initialPayment = vamount * 0.3 ' 30% of the value diff --git a/packages/security.gbapp/services/SecService.ts b/packages/security.gbapp/services/SecService.ts index 2ee53dae..6027a827 100644 --- a/packages/security.gbapp/services/SecService.ts +++ b/packages/security.gbapp/services/SecService.ts @@ -185,7 +185,6 @@ export class SecService extends GBService { item !== userSystemId && !(await this.isAgentSystemId(item)) ) { - // TODO: Optimize loop. agentSystemId = item; } }); diff --git a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts index d5d8b539..639e1e4b 100644 --- a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts +++ b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts @@ -34,7 +34,7 @@ import urlJoin from 'url-join'; import Swagger from 'swagger-client'; import Path from 'path'; import Fs from 'fs'; -import { GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib'; +import { GBError, GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib'; import { CollectionUtil } from 'pragmatismo-io-framework'; import { GBServer } from '../../../src/app.js'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js'; @@ -130,123 +130,123 @@ export class WhatsappDirectLine extends GBService { case 'GeneralBots': const minBoot = GBServer.globals.minBoot as any; - // Initialize the browser using a local profile for each bot. + // Initialize the browser using a local profile for each bot. - const gbaiName = `${this.min.botId}.gbai`; - const localName = Path.join('work', gbaiName, 'profile'); + const gbaiName = `${this.min.botId}.gbai`; + const localName = Path.join('work', gbaiName, 'profile'); - const createClient = async browserWSEndpoint => { - let puppeteer: any = { - headless: false, - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - '--disable-dev-shm-usage', - '--disable-accelerated-2d-canvas', - '--no-first-run', - '--no-zygote', - '--single-process', - '--disable-gpu', - '--disable-infobars', - '--disable-features=site-per-process', - `--user-data-dir=${localName}` - ] - }; - if (browserWSEndpoint) { - puppeteer = { browserWSEndpoint: browserWSEndpoint }; - } + const createClient = async browserWSEndpoint => { + let puppeteer: any = { + headless: false, + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-dev-shm-usage', + '--disable-accelerated-2d-canvas', + '--no-first-run', + '--no-zygote', + '--single-process', + '--disable-gpu', + '--disable-infobars', + '--disable-features=site-per-process', + `--user-data-dir=${localName}` + ] + }; + if (browserWSEndpoint) { + puppeteer = { browserWSEndpoint: browserWSEndpoint }; + } - const client = (this.customClient = new wpp.Client({ - authStrategy: new wpp.LocalAuth({ - clientId: this.min.botId, - dataPath: localName - }), - puppeteer: puppeteer - })); + const client = (this.customClient = new wpp.Client({ + authStrategy: new wpp.LocalAuth({ + clientId: this.min.botId, + dataPath: localName + }), + puppeteer: puppeteer + })); - client.on( - 'message', - (async message => { - await this.WhatsAppCallback(message, null); - }).bind(this) - ); + client.on( + 'message', + (async message => { + await this.WhatsAppCallback(message, null); + }).bind(this) + ); - client.on( - 'qr', - (async qr => { - const adminNumber = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null); - const adminEmail = this.min.core.getParam(this.min.instance, 'Bot Admin E-mail', null); + client.on( + 'qr', + (async qr => { + const adminNumber = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null); + const adminEmail = this.min.core.getParam(this.min.instance, 'Bot Admin E-mail', null); - // Sends QR Code to boot bot admin. + // Sends QR Code to boot bot admin. - const msg = `Please, scan QR Code with for bot ${this.botId}.`; - GBLog.info(msg); - qrcode.generate(qr, { small: true, scale: 0.5 }); + const msg = `Please, scan QR Code with for bot ${this.botId}.`; + GBLog.info(msg); + qrcode.generate(qr, { small: true, scale: 0.5 }); - // While handling other bots uses boot instance of this class to send QR Codes. + // While handling other bots uses boot instance of this class to send QR Codes. - const s = new DialogKeywords(this.min, null, null); - const qrBuf = await s.getQRCode(qr); - const gbaiName = `${this.min.botId}.gbai`; - const localName = Path.join( - 'work', - gbaiName, - 'cache', - `qr${GBAdminService.getRndReadableIdentifier()}.png` - ); - Fs.writeFileSync(localName, qrBuf); - const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName)); - GBServer.globals.minBoot.whatsAppDirectLine.sendFileToDevice( - adminNumber, - url, - Path.basename(localName), - msg - ); - - s.sendEmail({ to: adminEmail, subject: `Check your WhatsApp for bot ${this.botId}`, body: msg }); - }).bind(this) - ); - - client.on('authenticated', async () => { - this.browserWSEndpoint = client.pupBrowser.wsEndpoint(); - GBLog.verbose(`GBWhatsApp: QR Code authenticated for ${this.botId}.`); - }); - - client.on('ready', async () => { - client.pupBrowser.on( - 'disconnected', - (async () => { - GBLog.info(`Browser terminated. Restarting ${this.min.botId} WhatsApp native provider.`); - await createClient.bind(this)(null); - }).bind(this) + const s = new DialogKeywords(this.min, null, null); + const qrBuf = await s.getQRCode(qr); + const gbaiName = `${this.min.botId}.gbai`; + const localName = Path.join( + 'work', + gbaiName, + 'cache', + `qr${GBAdminService.getRndReadableIdentifier()}.png` + ); + Fs.writeFileSync(localName, qrBuf); + const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName)); + GBServer.globals.minBoot.whatsAppDirectLine.sendFileToDevice( + adminNumber, + url, + Path.basename(localName), + msg ); - GBLog.verbose(`GBWhatsApp: Emptying chat list for ${this.botId}...`); + s.sendEmail({ to: adminEmail, subject: `Check your WhatsApp for bot ${this.botId}`, body: msg }); + }).bind(this) + ); - // Keeps the chat list cleaned. + client.on('authenticated', async () => { + this.browserWSEndpoint = client.pupBrowser.wsEndpoint(); + GBLog.verbose(`GBWhatsApp: QR Code authenticated for ${this.botId}.`); + }); - const chats = await client.getChats(); - await CollectionUtil.asyncForEach(chats, async chat => { - const sleep = ms => { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); - }; - const wait = Math.floor(Math.random() * 5000) + 1000; - await sleep(wait); - if (chat.isGroup) { - await chat.clearMessages(); - } else if (!chat.pinned) { - await chat.delete(); - } - }); + client.on('ready', async () => { + client.pupBrowser.on( + 'disconnected', + (async () => { + GBLog.info(`Browser terminated. Restarting ${this.min.botId} WhatsApp native provider.`); + await createClient.bind(this)(null); + }).bind(this) + ); + + GBLog.verbose(`GBWhatsApp: Emptying chat list for ${this.botId}...`); + + // Keeps the chat list cleaned. + + const chats = await client.getChats(); + await CollectionUtil.asyncForEach(chats, async chat => { + const sleep = ms => { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); + }; + const wait = Math.floor(Math.random() * 5000) + 1000; + await sleep(wait); + if (chat.isGroup) { + await chat.clearMessages(); + } else if (!chat.pinned) { + await chat.delete(); + } }); + }); - client.initialize(); - }; - await createClient.bind(this)(this.browserWSEndpoint); + client.initialize(); + }; + await createClient.bind(this)(this.browserWSEndpoint); - setUrl = false; + setUrl = false; break; @@ -310,10 +310,8 @@ export class WhatsappDirectLine extends GBService { public async check() { switch (this.provider) { case 'GeneralBots': + return this.customClient.getState() === 'CONNECTED'; - // TODO: Verify if browser is OK. - - return true; default: GBLog.verbose(`GBWhatsapp: Checking server...`); let url = urlJoin(this.whatsappServiceUrl, 'status') + `?token=${this.min.instance.whatsappServiceKey}`; @@ -323,8 +321,8 @@ export class WhatsappDirectLine extends GBService { }; const res = await fetch(url, options); - const json = (await res.json()); - return json ['accountStatus'] === 'authenticated'; + const json = await res.json(); + return json['accountStatus'] === 'authenticated'; } } @@ -538,7 +536,6 @@ export class WhatsappDirectLine extends GBService { await this.min.conversationalService.sendMarkdownToMobile(this.min, null, user.userSystemId, message); } } else if (text === '/qt') { - // https://github.com/GeneralBots/BotServer/issues/307 await this.sendToDeviceEx( @@ -579,8 +576,7 @@ export class WhatsappDirectLine extends GBService { locale, null ); - } else if (text === '/qt' || text === 'Sair' || text === 'Fechar') { - // TODO: Transfers only in pt-br for now. + } else if (text === '/qt' || GBMinService.isGlobalQuitUtterance(locale, text)) { await this.endTransfer(from, locale, user, agent, sec); } else { GBLog.info(`USER (${from}) TO AGENT ${agent.userSystemId}: ${text}`); @@ -836,9 +832,7 @@ export class WhatsappDirectLine extends GBService { break; case 'maytapi': - options = {}; // TODO: Code Maytapi. - - break; + throw GBError.create('Sending audio in Maytapi not supported.'); } if (options) { @@ -920,8 +914,6 @@ export class WhatsappDirectLine extends GBService { await fetch(url, options); } catch (error) { GBLog.error(`Error sending message to Whatsapp provider ${error.message}`); - - // TODO: Handle Error: socket hang up and retry. } } } diff --git a/src/app.ts b/src/app.ts index 34375d34..c318e294 100644 --- a/src/app.ts +++ b/src/app.ts @@ -66,7 +66,7 @@ export class GBServer { * Program entry-point. */ - public static run () { + public static run() { GBLog.info(`The Bot Server is in STARTING mode...`); GBServer.globals = new RootData(); GBConfigService.init(); @@ -127,7 +127,6 @@ export class GBServer { if (proxy !== undefined) { GBServer.globals.publicAddress = proxy; } else { - // Ensure that local development proxy is setup. GBLog.info(`Establishing a development local proxy (ngrok)...`); @@ -224,15 +223,15 @@ export class GBServer { const loggers = GBLog.getLogger(); winston.default(server, loggers[1]); - } GBLog.info(`The Bot Server is in RUNNING mode...`); // Opens Navigator. - // TODO: Config: core.openBrowserInDevelopment(); - + if (process.env.DEV_OPEN_BROWSER) { + core.openBrowserInDevelopment(); + } } catch (err) { GBLog.error(`STOP: ${err.message ? err.message : err} ${err.stack ? err.stack : ''}`); process.exit(1); @@ -240,8 +239,8 @@ export class GBServer { })(); }; - // TODO: Move to .gbot folder myown.com pointing to generalbots.ai/myown. - + // + if (process.env.CERTIFICATE_PFX) { const options1 = { passphrase: process.env.CERTIFICATE_PASSPHRASE,