new(all): #327 OPEN AS web automation.
This commit is contained in:
parent
4c813ce02d
commit
649e08d7f2
3 changed files with 82 additions and 55 deletions
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { GBMinInstance, GBService, IGBCoreService, GBDialogStep } from 'botlib';
|
import { GBMinInstance, GBService, IGBCoreService, GBDialogStep } from 'botlib';
|
||||||
import * as Fs from 'fs';
|
import * as Fs from 'fs';
|
||||||
import { GBServer } from '../../../src/app.js';
|
import { GBServer } from '../../../src/app.js';
|
||||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.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.
|
* Basic services for BASIC manipulation.
|
||||||
*/
|
*/
|
||||||
export class GBVMService extends GBService {
|
export class GBVMService extends GBService {
|
||||||
|
|
||||||
private static DEBUGGER_PORT = 9222;
|
private static DEBUGGER_PORT = 9222;
|
||||||
|
|
||||||
|
|
||||||
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
||||||
const files = await walkPromise(folder);
|
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
|
// Converts General Bots BASIC into regular VBS
|
||||||
|
|
||||||
let basicCode: string = Fs.readFileSync(filename, 'utf8');
|
let basicCode: string = Fs.readFileSync(filename, 'utf8');
|
||||||
|
@ -276,7 +274,6 @@ export class GBVMService extends GBService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts General Bots BASIC
|
* Converts General Bots BASIC
|
||||||
*
|
*
|
||||||
|
@ -312,7 +309,6 @@ export class GBVMService extends GBService {
|
||||||
* Executes the converted JavaScript from BASIC code inside execution context.
|
* Executes the converted JavaScript from BASIC code inside execution context.
|
||||||
*/
|
*/
|
||||||
public static async callVM(text: string, min: GBMinInstance, step, deployer: GBDeployer, debug: boolean) {
|
public static async callVM(text: string, min: GBMinInstance, step, deployer: GBDeployer, debug: boolean) {
|
||||||
|
|
||||||
// Creates a class DialogKeywords which is the *this* pointer
|
// Creates a class DialogKeywords which is the *this* pointer
|
||||||
// in BASIC.
|
// in BASIC.
|
||||||
|
|
||||||
|
@ -351,9 +347,9 @@ export class GBVMService extends GBService {
|
||||||
};
|
};
|
||||||
|
|
||||||
sandbox['id'] = dk.sys().getRandomId();
|
sandbox['id'] = dk.sys().getRandomId();
|
||||||
sandbox['username'] = await dk.userName({pid});
|
sandbox['username'] = await dk.userName({ pid });
|
||||||
sandbox['mobile'] = await dk.userMobile({pid});
|
sandbox['mobile'] = await dk.userMobile({ pid });
|
||||||
sandbox['from'] = await dk.userMobile({pid});
|
sandbox['from'] = await dk.userMobile({ pid });
|
||||||
sandbox['ENTER'] = String.fromCharCode(13);
|
sandbox['ENTER'] = String.fromCharCode(13);
|
||||||
sandbox['headers'] = {};
|
sandbox['headers'] = {};
|
||||||
sandbox['data'] = {};
|
sandbox['data'] = {};
|
||||||
|
@ -362,8 +358,10 @@ export class GBVMService extends GBService {
|
||||||
sandbox['httpPs'] = '';
|
sandbox['httpPs'] = '';
|
||||||
sandbox['pid'] = pid;
|
sandbox['pid'] = pid;
|
||||||
|
|
||||||
if (GBConfigService.get('GBVM') === 'false') {
|
let result;
|
||||||
try {
|
|
||||||
|
try {
|
||||||
|
if (GBConfigService.get('GBVM') === 'false') {
|
||||||
const vm1 = new NodeVM({
|
const vm1 = new NodeVM({
|
||||||
allowAsync: true,
|
allowAsync: true,
|
||||||
sandbox: sandbox,
|
sandbox: sandbox,
|
||||||
|
@ -377,23 +375,18 @@ export class GBVMService extends GBService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const s = new VMScript(code, { filename: scriptPath });
|
const s = new VMScript(code, { filename: scriptPath });
|
||||||
let x = vm1.run(s);
|
result = vm1.run(s);
|
||||||
return x;
|
} else {
|
||||||
} catch (error) {
|
const runnerPath = urlJoin(
|
||||||
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
|
process.cwd(),
|
||||||
}
|
'dist',
|
||||||
} else {
|
'packages',
|
||||||
const runnerPath = urlJoin(
|
'basic.gblib',
|
||||||
process.cwd(),
|
'services',
|
||||||
'dist',
|
'vm2-process',
|
||||||
'packages',
|
'vm2ProcessRunner.js'
|
||||||
'basic.gblib',
|
);
|
||||||
'services',
|
|
||||||
'vm2-process',
|
|
||||||
'vm2ProcessRunner.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { run } = createVm2Pool({
|
const { run } = createVm2Pool({
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 0,
|
max: 0,
|
||||||
|
@ -407,12 +400,23 @@ export class GBVMService extends GBService {
|
||||||
script: runnerPath
|
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 {
|
||||||
|
|
||||||
return result;
|
// Releases previous allocated OPEN semaphores.
|
||||||
} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,7 @@
|
||||||
* Image processing services of conversation to be called by BASIC.
|
* Image processing services of conversation to be called by BASIC.
|
||||||
*/
|
*/
|
||||||
export class KeywordsExpressions {
|
export class KeywordsExpressions {
|
||||||
|
private static getParams = (text: string, names) => {
|
||||||
private static getParams = (text: string, names) => {
|
|
||||||
let ret = {};
|
let ret = {};
|
||||||
const splitParamsButIgnoreCommasInDoublequotes = (str: string) => {
|
const splitParamsButIgnoreCommasInDoublequotes = (str: string) => {
|
||||||
return str.split(',').reduce(
|
return str.split(',').reduce(
|
||||||
|
@ -74,7 +73,6 @@ export class KeywordsExpressions {
|
||||||
* Returns the list of BASIC keyword and their JS match.
|
* Returns the list of BASIC keyword and their JS match.
|
||||||
*/
|
*/
|
||||||
public static getKeywords() {
|
public static getKeywords() {
|
||||||
|
|
||||||
// Keywords from General Bots BASIC.
|
// Keywords from General Bots BASIC.
|
||||||
|
|
||||||
let keywords = [];
|
let keywords = [];
|
||||||
|
@ -140,12 +138,19 @@ export class KeywordsExpressions {
|
||||||
keywords[i++] = [
|
keywords[i++] = [
|
||||||
/^\s*open\s*(.*)/gim,
|
/^\s*open\s*(.*)/gim,
|
||||||
($0, $1, $2) => {
|
($0, $1, $2) => {
|
||||||
|
|
||||||
let pos;
|
|
||||||
let sessionName;
|
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]));
|
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]));
|
$1 = $1.substr(0, $1.lastIndexOf(pos[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +159,7 @@ export class KeywordsExpressions {
|
||||||
}
|
}
|
||||||
const params = this.getParams($1, ['url', 'username', 'password']);
|
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}})`;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -44,15 +44,12 @@ import urlJoin from 'url-join';
|
||||||
import Fs from 'fs';
|
import Fs from 'fs';
|
||||||
import Path from 'path';
|
import Path from 'path';
|
||||||
import url from 'url';
|
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.
|
* Web Automation services of conversation to be called by BASIC.
|
||||||
*/
|
*/
|
||||||
export class WebAutomationServices {
|
export class WebAutomationServices {
|
||||||
|
|
||||||
static semaphoreWithTimeout = withTimeout(new Semaphore(5), 60 * 1000, new Error('new fancy error'));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to minimal bot instance.
|
* Reference to minimal bot instance.
|
||||||
*/
|
*/
|
||||||
|
@ -124,25 +121,46 @@ export class WebAutomationServices {
|
||||||
* @example OPEN "https://wikipedia.org"
|
* @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}.`);
|
GBLog.info(`BASIC: Web Automation GET PAGE ${sessionName ? sessionName : ''} ${url}.`);
|
||||||
|
|
||||||
|
// Semaphore logic to block multiple entries on the same session.
|
||||||
|
|
||||||
let page;
|
let page;
|
||||||
if (url.startsWith('#')) {
|
let session = GBServer.globals.webSessions[sessionName];
|
||||||
const [value, release] = await WebAutomationServices.semaphoreWithTimeout.acquire();
|
|
||||||
|
if (session) {
|
||||||
|
const [value, release] = await session.semaphore.acquire();
|
||||||
try {
|
try {
|
||||||
|
GBServer.globals.webSessions[sessionName].release = release;
|
||||||
page = GBServer.globals.webSessions[url.substr(1)];
|
page = GBServer.globals.webSessions[url.substr(1)];
|
||||||
} finally {
|
} catch {
|
||||||
release();
|
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 {
|
} else {
|
||||||
if (!this.browser) {
|
if (!this.browser) {
|
||||||
this.browser = await createBrowser(null);
|
this.browser = await createBrowser(null);
|
||||||
}
|
}
|
||||||
page = (await this.browser.pages())[0];
|
page = (await this.browser.pages())[0];
|
||||||
if (sessionName) {
|
|
||||||
GBServer.globals.webSessions[sessionName] = page;
|
|
||||||
}
|
|
||||||
if (username || password) {
|
if (username || password) {
|
||||||
await page.authenticate({ pid, username: username, password: password });
|
await page.authenticate({ pid, username: username, password: password });
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue