From 649e08d7f2102d2388be653bd17421e370496b8e Mon Sep 17 00:00:00 2001 From: rodrigorodriguez Date: Thu, 16 Feb 2023 10:27:18 -0300 Subject: [PATCH] new(all): #327 OPEN AS web automation. --- packages/basic.gblib/services/GBVMService.ts | 66 ++++++++++--------- .../services/KeywordsExpressions.ts | 23 ++++--- .../services/WebAutomationServices.ts | 48 +++++++++----- 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index 1b19cbba..a7470a8e 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -32,7 +32,7 @@ 'use strict'; -import { GBMinInstance, GBService, IGBCoreService, GBDialogStep } from 'botlib'; +import { GBMinInstance, GBService, IGBCoreService, GBDialogStep } from 'botlib'; import * as Fs from 'fs'; import { GBServer } from '../../../src/app.js'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js'; @@ -61,10 +61,8 @@ import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js'; * Basic services for BASIC manipulation. */ export class GBVMService extends GBService { - private static DEBUGGER_PORT = 9222; - public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) { const files = await walkPromise(folder); @@ -161,7 +159,7 @@ export class GBVMService extends GBService { }); } - public async translateBASIC(filename: any, mainName: string, min:GBMinInstance) { + public async translateBASIC(filename: any, mainName: string, min: GBMinInstance) { // Converts General Bots BASIC into regular VBS let basicCode: string = Fs.readFileSync(filename, 'utf8'); @@ -276,7 +274,6 @@ export class GBVMService extends GBService { }); } - /** * Converts General Bots BASIC * @@ -312,7 +309,6 @@ export class GBVMService extends GBService { * Executes the converted JavaScript from BASIC code inside execution context. */ public static async callVM(text: string, min: GBMinInstance, step, deployer: GBDeployer, debug: boolean) { - // Creates a class DialogKeywords which is the *this* pointer // in BASIC. @@ -351,9 +347,9 @@ export class GBVMService extends GBService { }; sandbox['id'] = dk.sys().getRandomId(); - sandbox['username'] = await dk.userName({pid}); - sandbox['mobile'] = await dk.userMobile({pid}); - sandbox['from'] = await dk.userMobile({pid}); + sandbox['username'] = await dk.userName({ pid }); + sandbox['mobile'] = await dk.userMobile({ pid }); + sandbox['from'] = await dk.userMobile({ pid }); sandbox['ENTER'] = String.fromCharCode(13); sandbox['headers'] = {}; sandbox['data'] = {}; @@ -362,8 +358,10 @@ export class GBVMService extends GBService { sandbox['httpPs'] = ''; sandbox['pid'] = pid; - if (GBConfigService.get('GBVM') === 'false') { - try { + let result; + + try { + if (GBConfigService.get('GBVM') === 'false') { const vm1 = new NodeVM({ allowAsync: true, sandbox: sandbox, @@ -377,23 +375,18 @@ export class GBVMService extends GBService { } }); const s = new VMScript(code, { filename: scriptPath }); - let x = vm1.run(s); - return x; - } catch (error) { - throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`); - } - } else { - const runnerPath = urlJoin( - process.cwd(), - 'dist', - 'packages', - 'basic.gblib', - 'services', - 'vm2-process', - 'vm2ProcessRunner.js' - ); + result = vm1.run(s); + } else { + const runnerPath = urlJoin( + process.cwd(), + 'dist', + 'packages', + 'basic.gblib', + 'services', + 'vm2-process', + 'vm2ProcessRunner.js' + ); - try { const { run } = createVm2Pool({ min: 0, max: 0, @@ -407,12 +400,23 @@ export class GBVMService extends GBService { script: runnerPath }); - const result = await run(code, { filename: scriptPath, sandbox: sandbox }); + result = await run(code, { filename: scriptPath, sandbox: sandbox }); + } + } catch (error) { + throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`); + } finally { + + // Releases previous allocated OPEN semaphores. - return result; - } catch (error) { - throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`); + let keys = Object.keys(GBServer.globals.webSessions); + for (let i = 0; i < keys.length; i++) { + const session = GBServer.globals.webSessions[keys[i]]; + if (session.pid === pid) { + session.semaphore.release(); + } } } + + return result; } } diff --git a/packages/basic.gblib/services/KeywordsExpressions.ts b/packages/basic.gblib/services/KeywordsExpressions.ts index e5241c31..89d056e2 100644 --- a/packages/basic.gblib/services/KeywordsExpressions.ts +++ b/packages/basic.gblib/services/KeywordsExpressions.ts @@ -36,8 +36,7 @@ * Image processing services of conversation to be called by BASIC. */ export class KeywordsExpressions { - - private static getParams = (text: string, names) => { + private static getParams = (text: string, names) => { let ret = {}; const splitParamsButIgnoreCommasInDoublequotes = (str: string) => { return str.split(',').reduce( @@ -74,7 +73,6 @@ export class KeywordsExpressions { * Returns the list of BASIC keyword and their JS match. */ public static getKeywords() { - // Keywords from General Bots BASIC. let keywords = []; @@ -140,12 +138,19 @@ export class KeywordsExpressions { keywords[i++] = [ /^\s*open\s*(.*)/gim, ($0, $1, $2) => { - - let pos; let sessionName; - if (pos = $1.match(/\s*AS\s*\#/)){ + let kind = ''; + let pos; + + if (pos = $1.match(/\s*AS\s*\#/)) { + kind = '"AS"'; + } else if (pos = $1.match(/\s*WITH\s*\#/)) { + kind = '"WITH"'; + } + + if (pos) { let part = $1.substr($1.lastIndexOf(pos[0])); - sessionName = `"${part.substr(part.indexOf("#") + 1)}"`; + sessionName = `"${part.substr(part.indexOf('#') + 1)}"`; $1 = $1.substr(0, $1.lastIndexOf(pos[0])); } @@ -153,8 +158,8 @@ export class KeywordsExpressions { $1 = `"${$1}"`; } const params = this.getParams($1, ['url', 'username', 'password']); - - return `page = await wa.getPage({pid: pid, sessionName: ${sessionName}, ${params}})`; + + return `page = await wa.getPage({pid: pid, sessionKind: ${kind}, sessionName: ${sessionName}, ${params}})`; } ]; diff --git a/packages/basic.gblib/services/WebAutomationServices.ts b/packages/basic.gblib/services/WebAutomationServices.ts index dae6dad5..303da106 100644 --- a/packages/basic.gblib/services/WebAutomationServices.ts +++ b/packages/basic.gblib/services/WebAutomationServices.ts @@ -44,15 +44,12 @@ import urlJoin from 'url-join'; import Fs from 'fs'; import Path from 'path'; import url from 'url'; -import {Mutex, Semaphore, withTimeout} from 'async-mutex'; +import { Mutex, Semaphore, withTimeout } from 'async-mutex'; /** * Web Automation services of conversation to be called by BASIC. */ export class WebAutomationServices { - - static semaphoreWithTimeout = withTimeout(new Semaphore(5), 60 * 1000, new Error('new fancy error')); - /** * Reference to minimal bot instance. */ @@ -123,26 +120,47 @@ export class WebAutomationServices { * * @example OPEN "https://wikipedia.org" */ - - public async getPage({ pid, sessionName, url, username, password }) { + + public async getPage({ pid, sessionKind, sessionName, url, username, password }) { GBLog.info(`BASIC: Web Automation GET PAGE ${sessionName ? sessionName : ''} ${url}.`); + // Semaphore logic to block multiple entries on the same session. + let page; - if (url.startsWith('#')) { - const [value, release] = await WebAutomationServices.semaphoreWithTimeout.acquire(); + let session = GBServer.globals.webSessions[sessionName]; + + if (session) { + const [value, release] = await session.semaphore.acquire(); try { + GBServer.globals.webSessions[sessionName].release = release; page = GBServer.globals.webSessions[url.substr(1)]; - } finally { - release(); - } + } catch { + release(); + } + } + + // There is no session yet, + + if (!session && sessionKind === 'AS') { + + // A new web session is being created. + + GBServer.globals.webSessions[sessionName] = {}; + GBServer.globals.webSessions[sessionName].pid = pid; + GBServer.globals.webSessions[sessionName].page = page; + GBServer.globals.webSessions[sessionName].semaphore = withTimeout( + new Semaphore(5), + 60 * 1000, + new Error('Error waiting for OPEN keyword.') + ); + } + + if (url.startsWith('#') && sessionKind == 'WITH') { } else { if (!this.browser) { this.browser = await createBrowser(null); - } - page = (await this.browser.pages())[0]; - if (sessionName) { - GBServer.globals.webSessions[sessionName] = page; } + page = (await this.browser.pages())[0]; if (username || password) { await page.authenticate({ pid, username: username, password: password }); }