new(all): #327 OPEN AS web automation.
This commit is contained in:
parent
be5cf3f190
commit
ea99337079
8 changed files with 70 additions and 35 deletions
|
@ -1,7 +1,7 @@
|
||||||
dist: focal
|
dist: focal
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- 19.5.0
|
- 19.6.0
|
||||||
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"Dário Vieira <dario.junior3@gmail.com>"
|
"Dário Vieira <dario.junior3@gmail.com>"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "=19.5.0"
|
"node": "=19.6.0"
|
||||||
},
|
},
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
|
@ -76,6 +76,7 @@
|
||||||
"alasql": "2.1.6",
|
"alasql": "2.1.6",
|
||||||
"any-shell-escape": "0.1.1",
|
"any-shell-escape": "0.1.1",
|
||||||
"arraybuffer-to-buffer": "^0.0.7",
|
"arraybuffer-to-buffer": "^0.0.7",
|
||||||
|
"async-mutex": "^0.4.0",
|
||||||
"async-promises": "0.2.3",
|
"async-promises": "0.2.3",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"billboard.js": "3.6.3",
|
"billboard.js": "3.6.3",
|
||||||
|
|
|
@ -140,12 +140,21 @@ 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;
|
||||||
|
if (pos = $1.match(/\s*AS\s*\#/)){
|
||||||
|
let part = $1.substr($1.lastIndexOf(pos[0]));
|
||||||
|
sessionName = `"${part.substr(part.indexOf("#") + 1)}"`;
|
||||||
|
$1 = $1.substr(0, $1.lastIndexOf(pos[0]));
|
||||||
|
}
|
||||||
|
|
||||||
if (!$1.startsWith('"') && !$1.startsWith("'")) {
|
if (!$1.startsWith('"') && !$1.startsWith("'")) {
|
||||||
$1 = `"${$1}"`;
|
$1 = `"${$1}"`;
|
||||||
}
|
}
|
||||||
const params = this.getParams($1, ['url', 'username', 'password']);
|
const params = this.getParams($1, ['url', 'username', 'password']);
|
||||||
|
|
||||||
return `page = await wa.getPage({pid: pid,${params}})`;
|
return `page = await wa.getPage({pid: pid, sessionName: ${sessionName}, ${params}})`;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -44,12 +44,15 @@ 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 { pid } from 'process';
|
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.
|
||||||
*/
|
*/
|
||||||
|
@ -107,7 +110,7 @@ export class WebAutomationServices {
|
||||||
* When creating this keyword facade,a bot instance is
|
* When creating this keyword facade,a bot instance is
|
||||||
* specified among the deployer service.
|
* specified among the deployer service.
|
||||||
*/
|
*/
|
||||||
constructor (min: GBMinInstance, user, dk) {
|
constructor(min: GBMinInstance, user, dk) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.dk = dk;
|
this.dk = dk;
|
||||||
|
@ -120,25 +123,37 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example OPEN "https://wikipedia.org"
|
* @example OPEN "https://wikipedia.org"
|
||||||
*/
|
*/
|
||||||
public async getPage ({ pid, url, username, password }) {
|
|
||||||
GBLog.info(`BASIC: Web Automation GET PAGE ${url}.`);
|
public async getPage({ pid, sessionName, url, username, password }) {
|
||||||
if (!this.browser) {
|
GBLog.info(`BASIC: Web Automation GET PAGE ${sessionName ? sessionName : ''} ${url}.`);
|
||||||
this.browser = await createBrowser(null);
|
|
||||||
}
|
let page;
|
||||||
const page = (await this.browser.pages())[0];
|
if (url.startsWith('#')) {
|
||||||
if (username || password) {
|
const [value, release] = await WebAutomationServices.semaphoreWithTimeout.acquire();
|
||||||
await page.authenticate({pid, username: username, password: password });
|
try {
|
||||||
|
page = GBServer.globals.webSessions[url.substr(1)];
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.browser) {
|
||||||
|
this.browser = await createBrowser(null);
|
||||||
|
}
|
||||||
|
page = (await this.browser.pages())[0];
|
||||||
|
if (sessionName) {
|
||||||
|
GBServer.globals.webSessions[sessionName] = page;
|
||||||
|
}
|
||||||
|
if (username || password) {
|
||||||
|
await page.authenticate({ pid, username: username, password: password });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await page.goto(url);
|
await page.goto(url);
|
||||||
|
|
||||||
const handle = WebAutomationServices.cyrb53(this.min.botId + url);
|
const handle = WebAutomationServices.cyrb53(this.min.botId + url);
|
||||||
|
|
||||||
this.pageMap[handle] = page;
|
this.pageMap[handle] = page;
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPageByHandle (hash) {
|
public getPageByHandle(hash) {
|
||||||
return this.pageMap[hash];
|
return this.pageMap[hash];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +162,7 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example GET "selector"
|
* @example GET "selector"
|
||||||
*/
|
*/
|
||||||
public async getBySelector ({ handle, selector }) {
|
public async getBySelector({ handle, selector }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation GET element: ${selector}.`);
|
GBLog.info(`BASIC: Web Automation GET element: ${selector}.`);
|
||||||
await page.waitForSelector(selector);
|
await page.waitForSelector(selector);
|
||||||
|
@ -170,7 +185,7 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example GET page,"frameSelector,"elementSelector"
|
* @example GET page,"frameSelector,"elementSelector"
|
||||||
*/
|
*/
|
||||||
public async getByFrame ({ handle, frame, selector }) {
|
public async getByFrame({ handle, frame, selector }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation GET element by frame: ${selector}.`);
|
GBLog.info(`BASIC: Web Automation GET element by frame: ${selector}.`);
|
||||||
await page.waitForSelector(frame);
|
await page.waitForSelector(frame);
|
||||||
|
@ -190,7 +205,7 @@ export class WebAutomationServices {
|
||||||
/**
|
/**
|
||||||
* Simulates a mouse hover an web page element.
|
* Simulates a mouse hover an web page element.
|
||||||
*/
|
*/
|
||||||
public async hover ({ pid, handle, selector }) {
|
public async hover({ pid, handle, selector }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation HOVER element: ${selector}.`);
|
GBLog.info(`BASIC: Web Automation HOVER element: ${selector}.`);
|
||||||
await this.getBySelector({ handle, selector: selector });
|
await this.getBySelector({ handle, selector: selector });
|
||||||
|
@ -203,7 +218,7 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example CLICK page,"#idElement"
|
* @example CLICK page,"#idElement"
|
||||||
*/
|
*/
|
||||||
public async click ({ pid, handle, frameOrSelector, selector }) {
|
public async click({ pid, handle, frameOrSelector, selector }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation CLICK element: ${frameOrSelector}.`);
|
GBLog.info(`BASIC: Web Automation CLICK element: ${frameOrSelector}.`);
|
||||||
if (selector) {
|
if (selector) {
|
||||||
|
@ -219,7 +234,7 @@ export class WebAutomationServices {
|
||||||
await this.debugStepWeb(pid, page);
|
await this.debugStepWeb(pid, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async debugStepWeb (pid, page) {
|
private async debugStepWeb(pid, page) {
|
||||||
let refresh = true;
|
let refresh = true;
|
||||||
if (this.lastDebugWeb) {
|
if (this.lastDebugWeb) {
|
||||||
refresh = new Date().getTime() - this.lastDebugWeb.getTime() > 5000;
|
refresh = new Date().getTime() - this.lastDebugWeb.getTime() > 5000;
|
||||||
|
@ -229,7 +244,7 @@ export class WebAutomationServices {
|
||||||
const mobile = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null);
|
const mobile = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null);
|
||||||
const filename = page;
|
const filename = page;
|
||||||
if (mobile) {
|
if (mobile) {
|
||||||
await this.dk.sendFileTo({pid: pid, mobile, filename, caption: 'General Bots Debugger' });
|
await this.dk.sendFileTo({ pid: pid, mobile, filename, caption: 'General Bots Debugger' });
|
||||||
}
|
}
|
||||||
this.lastDebugWeb = new Date();
|
this.lastDebugWeb = new Date();
|
||||||
}
|
}
|
||||||
|
@ -240,7 +255,7 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example PRESS ENTER ON page
|
* @example PRESS ENTER ON page
|
||||||
*/
|
*/
|
||||||
public async pressKey ({ handle, char, frame }) {
|
public async pressKey({ handle, char, frame }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation PRESS ${char} ON element: ${frame}.`);
|
GBLog.info(`BASIC: Web Automation PRESS ${char} ON element: ${frame}.`);
|
||||||
if (char.toLowerCase() === 'enter') {
|
if (char.toLowerCase() === 'enter') {
|
||||||
|
@ -256,7 +271,7 @@ export class WebAutomationServices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async linkByText ({ pid, handle, text, index }) {
|
public async linkByText({ pid, handle, text, index }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation CLICK LINK TEXT: ${text} ${index}.`);
|
GBLog.info(`BASIC: Web Automation CLICK LINK TEXT: ${text} ${index}.`);
|
||||||
if (!index) {
|
if (!index) {
|
||||||
|
@ -272,7 +287,7 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example file = SCREENSHOT page
|
* @example file = SCREENSHOT page
|
||||||
*/
|
*/
|
||||||
public async screenshot ({ handle, selector }) {
|
public async screenshot({ handle, selector }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation SCREENSHOT ${selector}.`);
|
GBLog.info(`BASIC: Web Automation SCREENSHOT ${selector}.`);
|
||||||
|
|
||||||
|
@ -292,7 +307,7 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example SET page,"selector","text"
|
* @example SET page,"selector","text"
|
||||||
*/
|
*/
|
||||||
public async setElementText ({ pid, handle, selector, text }) {
|
public async setElementText({ pid, handle, selector, text }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
GBLog.info(`BASIC: Web Automation TYPE on ${selector}: ${text}.`);
|
GBLog.info(`BASIC: Web Automation TYPE on ${selector}: ${text}.`);
|
||||||
const e = await this.getBySelector({ handle, selector });
|
const e = await this.getBySelector({ handle, selector });
|
||||||
|
@ -307,9 +322,9 @@ export class WebAutomationServices {
|
||||||
*
|
*
|
||||||
* @example file = DOWNLOAD element, folder
|
* @example file = DOWNLOAD element, folder
|
||||||
*/
|
*/
|
||||||
public async download ({ handle, selector, folder }) {
|
public async download({ handle, selector, folder }) {
|
||||||
const page = this.getPageByHandle(handle);
|
const page = this.getPageByHandle(handle);
|
||||||
|
|
||||||
const element = await this.getBySelector({ handle, selector });
|
const element = await this.getBySelector({ handle, selector });
|
||||||
// https://github.com/GeneralBots/BotServer/issues/311
|
// https://github.com/GeneralBots/BotServer/issues/311
|
||||||
const container = element['_frame'] ? element['_frame'] : element['_page'];
|
const container = element['_frame'] ? element['_frame'] : element['_page'];
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { GBLog, IGBInstance } from "botlib";
|
import { GBLog, IGBInstance } from "botlib";
|
||||||
import { GuaribasLog } from "../models/GBModel";
|
import { GuaribasLog } from "../models/GBModel.js";
|
||||||
|
|
||||||
export class GBLogEx {
|
export class GBLogEx {
|
||||||
public static async error(minOrInstanceId: any, message: string) {
|
public static async error(minOrInstanceId: any, message: string) {
|
||||||
|
@ -76,6 +76,7 @@ export class GBLogEx {
|
||||||
* Finds and update user agent information to a next available person.
|
* Finds and update user agent information to a next available person.
|
||||||
*/
|
*/
|
||||||
public static async log(instance: IGBInstance, kind: string, message: string): Promise<GuaribasLog> {
|
public static async log(instance: IGBInstance, kind: string, message: string): Promise<GuaribasLog> {
|
||||||
|
message = message?message.substring(0,1023):null;
|
||||||
return await GuaribasLog.create(<GuaribasLog>{
|
return await GuaribasLog.create(<GuaribasLog>{
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
message: message,
|
message: message,
|
||||||
|
|
|
@ -36,11 +36,17 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import puppeteer from 'puppeteer-extra';
|
|
||||||
|
import {createRequire} from "module";
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const puppeteer = require('puppeteer-extra');
|
||||||
|
const hidden = require('puppeteer-extra-plugin-stealth')
|
||||||
|
|
||||||
|
// require executablePath from puppeteer
|
||||||
|
const {executablePath} = require('puppeteer')
|
||||||
import Fs from 'fs';
|
import Fs from 'fs';
|
||||||
|
|
||||||
// const StealthPlugin from 'puppeteer-extra-plugin-stealth')
|
|
||||||
// puppeteer.use(StealthPlugin());
|
|
||||||
|
|
||||||
import { NextFunction, Request, Response } from 'express';
|
import { NextFunction, Request, Response } from 'express';
|
||||||
import urljoin from 'url-join';
|
import urljoin from 'url-join';
|
||||||
|
@ -96,12 +102,13 @@ async function createBrowser (profilePath): Promise<any> {
|
||||||
Fs.writeFileSync(preferences, JSON.stringify(data));
|
Fs.writeFileSync(preferences, JSON.stringify(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
puppeteer.use(hidden())
|
||||||
const browser = await puppeteer.launch({
|
const browser = await puppeteer.launch({
|
||||||
args: args,
|
args: args,
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
headless: false,
|
headless: false,
|
||||||
defaultViewport: null,
|
defaultViewport: null,
|
||||||
|
executablePath:executablePath(),
|
||||||
ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=IdleDetection']
|
ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=IdleDetection']
|
||||||
});
|
});
|
||||||
return browser;
|
return browser;
|
||||||
|
|
|
@ -42,6 +42,7 @@ import { GBMinService } from '../packages/core.gbapp/services/GBMinService.js';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class RootData {
|
export class RootData {
|
||||||
|
public webSessions: {} // List of Web Automation sessions.
|
||||||
public processes: {}; // List of .gbdialog active executions.
|
public processes: {}; // List of .gbdialog active executions.
|
||||||
public files: {}; // List of uploaded files handled.
|
public files: {}; // List of uploaded files handled.
|
||||||
public publicAddress: string; // URI for BotServer.
|
public publicAddress: string; // URI for BotServer.
|
||||||
|
|
|
@ -84,6 +84,7 @@ export class GBServer {
|
||||||
const server = express();
|
const server = express();
|
||||||
|
|
||||||
GBServer.globals.server = server;
|
GBServer.globals.server = server;
|
||||||
|
GBServer.globals.webSessions = {};
|
||||||
GBServer.globals.processes = {};
|
GBServer.globals.processes = {};
|
||||||
GBServer.globals.files = {};
|
GBServer.globals.files = {};
|
||||||
GBServer.globals.appPackages = [];
|
GBServer.globals.appPackages = [];
|
||||||
|
|
Loading…
Add table
Reference in a new issue