new(all): Alpha Word Debugger for 3.0.

This commit is contained in:
rodrigorodriguez 2022-11-11 21:35:05 -03:00
parent f21c699b54
commit c954786efb
7 changed files with 456 additions and 223 deletions

View file

@ -136,3 +136,8 @@ CREATE TABLE [dbo].[GuaribasSchedule]
[updatedAt] [datetimeoffset](7) NULL [updatedAt] [datetimeoffset](7) NULL
GO GO
# 3.0.0
ALTER TABLE dbo.GuaribasInstance ADD botKey nvarchar(64) NULL;

View file

@ -0,0 +1,204 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model,this program can be used either |
| under the terms of the GNU Affero General Public License,version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY,without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of Pragmatismo.io. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights,title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { GBLog, GBMinInstance } from 'botlib';
import { GBServer } from '../../../src/app';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
import { GuaribasUser } from '../../security.gbapp/models';
import { DialogKeywords } from './DialogKeywords';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
const Swagger = require('swagger-client');
const fs = require('fs');
import { CollectionUtil } from 'pragmatismo-io-framework';
import * as request from 'request-promise-native';
const urlJoin = require('url-join');
const Path = require('path');
const Fs = require('fs');
const url = require('url');
/**
* Web Automation services of conversation to be called by BASIC.
*/
export class DebuggerService {
/**
* Reference to minimal bot instance.
*/
public min: GBMinInstance;
/**
* Reference to the base system keywords functions to be called.
*/
public dk: DialogKeywords;
/**
* Current user object to get BASIC properties read.
*/
public user;
/**
* HTML browser for conversation over page interaction.
*/
browser: any;
sys: any;
/**
* The number used in this execution for HEAR calls (useful for SET SCHEDULE).
*/
hrOn: string;
userId: GuaribasUser;
debugWeb: boolean;
lastDebugWeb: Date;
/**
* SYSTEM account maxLines,when used with impersonated contexts (eg. running in SET SCHEDULE).
*/
maxLines: number = 2000;
pageMap = {};
/**
* When creating this keyword facade,a bot instance is
* specified among the deployer service.
*/
constructor(min: GBMinInstance, user, dk) {
this.min = min;
this.user = user;
this.dk = dk;
this.debugWeb = this.min.core.getParam<boolean>(
this.min.instance,
'Debug Web Automation',
false
);
}
private client;
public async setBreakPoint({ botId, botApiKey, line }) {
const client = GBServer.globals.debuggers[botId];
async function mainScript({ Debugger }) {
return new Promise((fulfill, reject) => {
Debugger.scriptParsed((params) => {
const { scriptId, url } = params;
fulfill(scriptId);
});
});
}
const scriptId = await mainScript(client);
const { breakpointId } = await await client.Debugger.setBreakpoint({
location: {
scriptId,
lineNumber: 6 - 1 // (zero-based)
}
});
}
public async removeBreakPoint({ botId, botApiKey, line }) {
}
public async continue({ botId, botApiKey, force }) {
const client = GBServer.globals.debuggers[botId];
client.Debugger.resume();
}
public async stopDebug({ botId, botApiKey, force }) {
const client = GBServer.globals.debuggers[botId];
client.close();
}
public async stepOver({ botId, botApiKey, force }) {
const client = GBServer.globals.debuggers[botId];
client.stepOver();
}
public async startDebug({ botId, botApiKey, scriptName }) {
// TODO : Map this.
let webchatKey = null;
this.client = await new Swagger({
spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')), usePromise: true
});
this.client.clientAuthorizations.add(
'AuthorizationBotConnector',
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${webchatKey}`, 'header')
);
const response = await this.client.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;
GBServer.globals.debugConversationId = conversationId;
this.client.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: `/call ${scriptName}`,
type: 'message',
from: {
id: 'test',
name: 'test'
}
}
});
// Setup debugger.
const client = GBServer.globals.debuggers[botId];
client.Debugger.paused(({ callFrames, reason, hitBreakpoints }) => {
if (hitBreakpoints.length>1)
{
GBLog.info(`.gbdialog break at line ${callFrames[0].location.lineNumber + 1}`); // (zero-based)
}
else (reason === ''){
GBLog.info(`.gbdialog ${reason} at line ${callFrames[0].location.lineNumber + 1}`); // (zero-based)
}
});
await client.Runtime.runIfWaitingForDebugger();
await client.Debugger.enable();
await client.Debugger.setPauseOnExceptions('all');
}
}

View file

@ -223,7 +223,7 @@ export class GBVMService extends GBService {
if (fs.existsSync(jsfile)) { if (fs.existsSync(jsfile)) {
let code: string = fs.readFileSync(jsfile, 'utf8'); let code: string = fs.readFileSync(jsfile, 'utf8');
code = code.replace(/^.*exports.*$/gm, ''); code.replace(/^.*exports.*$/gm, '');
code = ` code = `
@ -313,6 +313,38 @@ export class GBVMService extends GBService {
}); });
} }
private getParams = (text, names) => {
let ret = {};
const splitParamsButIgnoreCommasInDoublequotes = (str) => {
return str.split(',').reduce((accum, curr) => {
if (accum.isConcatting) {
accum.soFar[accum.soFar.length - 1] += ',' + curr
} else {
accum.soFar.push(curr)
}
if (curr.split('"').length % 2 == 0) {
accum.isConcatting = !accum.isConcatting
}
return accum;
}, { soFar: [], isConcatting: false }).soFar
}
const items = splitParamsButIgnoreCommasInDoublequotes(text);
let i = 0;
let json = '{';
names.forEach(name => {
let value = items[i];
i++;
json = `${json} "${name}": ${value} ${names.length == i ? '' : ','}`;
});
json = `${json}}`
return json;
};
/** /**
* Converts General Bots BASIC * Converts General Bots BASIC
* *
@ -325,180 +357,170 @@ export class GBVMService extends GBService {
code = `<%\n code = `<%\n
${process.env.ENABLE_AUTH ? `hear gbLogin as login` : ``} ${process.env.ENABLE_AUTH ? `hear gbLogin as login` : ``}
${code} ${code}
`; `;
// Split all params by comma, not inside strings. var matchingLines = [];
var allLines = code.split("\n");
const getParams = (text, names) => { for (var i = 0; i < allLines.length; i++) {
if (allLines[i].match(pattern)) {
let ret = {}; matchingLines.push(i);
const splitParamsButIgnoreCommasInDoublequotes = (str) => {
return str.split(',').reduce((accum, curr) => {
if (accum.isConcatting) {
accum.soFar[accum.soFar.length - 1] += ',' + curr
} else {
accum.soFar.push(curr)
}
if (curr.split('"').length % 2 == 0) {
accum.isConcatting = !accum.isConcatting
}
return accum;
}, { soFar: [], isConcatting: false }).soFar
} }
}
const items = splitParamsButIgnoreCommasInDoublequotes(text); return matchingLines;
let i = 0;
let json = '{';
names.forEach(name => {
let value = items[i];
i++;
json = `${json} "${name}": ${value} ${names.length == i ? '' : ','}`;
});
json = `${json}}`
return json;
}; code = `${code}\n%>`;
return code;
}
private getKeywords() {
// Keywords from General Bots BASIC. // Keywords from General Bots BASIC.
code = code.replace(/^\s*(\w+)\s*\=\s*SELECT\s*(.*)/gim, ($0, $1, $2) => { let keywords = [];
let i = 0;
keywords[i++] = [/^\s*(\w+)\s*\=\s*SELECT\s*(.*)/gim, ($0, $1, $2) => {
let tableName = /\sFROM\s(\w+)/.exec($2)[1]; let tableName = /\sFROM\s(\w+)/.exec($2)[1];
let sql = `SELECT ${$2}`.replace(tableName, '?'); let sql = `SELECT ${$2}`.replace(tableName, '?');
return `${$1} = await sys.executeSQL({data:${$1}, sql:"${sql}", tableName:"${tableName}"})\n`; return `${$1} = await sys.executeSQL({data:${$1}, sql:"${sql}", tableName:"${tableName}"})\n`;
}); }];
code = code.replace(/^\s*open\s*(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*open\s*(.*)/gim, ($0, $1, $2) => {
if (!$1.startsWith("\"") && !$1.startsWith("\'")) { if (!$1.startsWith("\"") && !$1.startsWith("\'")) {
$1 = `"${$1}"`; $1 = `"${$1}"`;
} }
const params = getParams($1, ['url', 'username', 'password']); const params = this.getParams($1, ['url', 'username', 'password']);
return `page = await wa.getPage(${params})\n`; return `page = await wa.getPage(${params})\n`;
}); }];
code = code.replace(/^\s*(set hear on)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set hear on)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `hrOn = ${$3}\n`; return `hrOn = ${$3}\n`;
}); }];
code = code.replace(/^\shear (\w+) as login/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as login/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"login"})`; return `${$1} = await dk.getHear({kind:"login"})`;
}); }];
code = code.replace(/^\shear (\w+) as email/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as email/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"email"})`; return `${$1} = await dk.getHear({kind:"email"})`;
}); }];
code = code.replace(/^\shear (\w+) as integer/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as integer/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"integer"})`; return `${$1} = await dk.getHear({kind:"integer"})`;
}); }];
code = code.replace(/^\shear (\w+) as file/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as file/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"file"})`; return `${$1} = await dk.getHear({kind:"file"})`;
}); }];
code = code.replace(/^\shear (\w+) as boolean/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as boolean/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"boolean"})`; return `${$1} = await dk.getHear({kind:"boolean"})`;
}); }];
code = code.replace(/^\shear (\w+) as name/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as name/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"name"})`; return `${$1} = await dk.getHear({kind:"name"})`;
}); }];
code = code.replace(/^\shear (\w+) as date/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as date/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"date"})`; return `${$1} = await dk.getHear({kind:"date"})`;
}); }];
code = code.replace(/^\shear (\w+) as hour/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as hour/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"hour"})`; return `${$1} = await dk.getHear({kind:"hour"})`;
}); }];
code = code.replace(/^\shear (\w+) as phone/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as phone/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"phone"})`; return `${$1} = await dk.getHear({kind:"phone"})`;
}); }];
code = code.replace(/^\shear (\w+) as money/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as money/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"money")}`; return `${$1} = await dk.getHear({kind:"money")}`;
}); }];
code = code.replace(/^\shear (\w+) as language/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as language/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"language")}`; return `${$1} = await dk.getHear({kind:"language")}`;
}); }];
code = code.replace(/^\shear (\w+) as zipcode/gim, ($0, $1) => { keywords[i++] = [/^\shear (\w+) as zipcode/gim, ($0, $1) => {
return `${$1} = await dk.getHear({kind:"zipcode")}`; return `${$1} = await dk.getHear({kind:"zipcode")}`;
}); }];
code = code.replace(/^\shear (\w+) as (.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\shear (\w+) as (.*)/gim, ($0, $1, $2) => {
return `${$1} = await dk.getHear({kind:"menu", args: [${$2}])}`; return `${$1} = await dk.getHear({kind:"menu", args: [${$2}])}`;
}); }];
code = code.replace(/^\s*(hear)\s*(\w+)/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(hear)\s*(\w+)/gim, ($0, $1, $2) => {
return `${$2} = await dk.getHear({})`; return `${$2} = await dk.getHear({})`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*find contact\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*find contact\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await dk.fndContact({${$2})\n`; return `${$1} = await dk.fndContact({${$2})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*=\s*find\s*(.*)\s*or talk\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*=\s*find\s*(.*)\s*or talk\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.find({args:[${$2}])\n return `${$1} = await sys.find({args:[${$2}])\n
if (!${$1}) { if (!${$1}) {
await dk.talk ({${$3}})\n; await dk.talk ({${$3}})\n;
return -1; return -1;
} }
`; `;
}); }];
code = code.replace(/^\sCALL\s*(.*)/gim, ($0, $1) => { keywords[i++] = [/^\sCALL\s*(.*)/gim, ($0, $1) => {
return `await ${$1}\n`; return `await ${$1}\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*find\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*find\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.find({args: [${$2}]})\n`; return `
}); ${$1} = await sys.find({args: [${$2}]})\n`;
}];
code = code.replace(/^\s*(\w+)\s*\=\s*create deal(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*create deal(\s)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['dealName', 'contact', 'company', 'amount']); const params = this.getParams($3, ['dealName', 'contact', 'company', 'amount']);
return `${$1} = await dk.createDeal(${params})\n`; return `${$1} = await dk.createDeal(${params})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*active tasks/gim, ($0, $1) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*active tasks/gim, ($0, $1) => {
return `${$1} = await dk.getActiveTasks({})\n`; return `${$1} = await dk.getActiveTasks({})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*append\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*append\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.append({args:[${$2}]})\n`; return `${$1} = await sys.append({args:[${$2}]})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*sort\s*(\w+)\s*by(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*sort\s*(\w+)\s*by(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.sortBy({array: ${$2}, memberName: "${$3}"})\n`; return `${$1} = await sys.sortBy({array: ${$2}, memberName: "${$3}"})\n`;
}); }];
code = code.replace(/^\ssee\s*text\s*of\s*(\w+)\s*as\s*(\w+)\s*/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\ssee\s*text\s*of\s*(\w+)\s*as\s*(\w+)\s*/gim, ($0, $1, $2, $3) => {
return `${$2} = await sys.seeText({url: ${$1})\n`; return `${$2} = await sys.seeText({url: ${$1})\n`;
}); }];
code = code.replace(/^\ssee\s*caption\s*of\s*(\w+)\s*as(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\ssee\s*caption\s*of\s*(\w+)\s*as(.*)/gim, ($0, $1, $2, $3) => {
return `${$2} = await sys.seeCaption({url: ${$1})\n`; return `${$2} = await sys.seeCaption({url: ${$1})\n`;
}); }];
code = code.replace(/^\s*(wait)\s*(\d+)/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(wait)\s*(\d+)/gim, ($0, $1, $2) => {
return `await sys.wait({seconds:${$2})`; return `await sys.wait({seconds:${$2})`;
}); }];
code = code.replace(/^\s*(get stock for )(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(get stock for )(.*)/gim, ($0, $1, $2) => {
return `stock = await sys.getStock({symbol: ${$2})`; return `stock = await sys.getStock({symbol: ${$2})`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*get\s(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*get\s(.*)/gim, ($0, $1, $2, $3) => {
const count = ($2.match(/\,/g) || []).length; const count = ($2.match(/\,/g) || []).length;
const values = $2.split(','); const values = $2.split(',');
@ -523,225 +545,223 @@ export class GBVMService extends GBService {
return `${$1} = await sys.get ({file: ${$2}, addressOrHeaders: headers, httpUsername, httpPs})`; return `${$1} = await sys.get ({file: ${$2}, addressOrHeaders: headers, httpUsername, httpPs})`;
} }
}); }];
code = code.replace(/\= NEW OBJECT/gi, ($0, $1, $2, $3) => { keywords[i++] = [/\= NEW OBJECT/gi, ($0, $1, $2, $3) => {
return ` = {}`; return ` = {}`;
}); }];
code = code.replace(/\= NEW ARRAY/gi, ($0, $1, $2, $3) => { keywords[i++] = [/\= NEW ARRAY/gi, ($0, $1, $2, $3) => {
return ` = []`; return ` = []`;
}); }];
code = code.replace(/^\s*(go to)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(go to)(\s)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['fromOrDialogName', 'dialogName']); const params = this.getParams($3, ['fromOrDialogName', 'dialogName']);
return `await dk.gotoDialog(${params})\n`; return `await dk.gotoDialog(${params})\n`;
}); }];
code = code.replace(/^\s*(set language)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set language)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.setLanguage ({${$3}})\n`; return `await dk.setLanguage ({${$3}})\n`;
}); }];
code = code.replace(/^\s*set header\s*(.*)\sas\s(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*set header\s*(.*)\sas\s(.*)/gim, ($0, $1, $2) => {
return `headers[${$1}]=${$2})`; return `headers[${$1}]=${$2})`;
}); }];
code = code.replace(/^\s*set http username\s*\=\s*(.*)/gim, ($0, $1) => { keywords[i++] = [/^\s*set http username\s*\=\s*(.*)/gim, ($0, $1) => {
return `httpUsername = ${$1}`; return `httpUsername = ${$1}`;
}); }];
code = code.replace(/^\sset http password\s*\=\s*(.*)/gim, ($0, $1) => { keywords[i++] = [/^\sset http password\s*\=\s*(.*)/gim, ($0, $1) => {
return `httpPs = ${$1}`; return `httpPs = ${$1}`;
}); }];
code = code.replace(/^\s*(datediff)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(datediff)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['date1', 'date2', 'mode']); const params = this.getParams($3, ['date1', 'date2', 'mode']);
return `await dk.dateDiff (${params}})\n`; return `await dk.dateDiff (${params}})\n`;
}); }];
code = code.replace(/^\s*(dateadd)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(dateadd)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['date', 'mode', 'units']); const params = this.getParams($3, ['date', 'mode', 'units']);
return `await dk.dateAdd (${$3})\n`; return `await dk.dateAdd (${$3})\n`;
}); }];
code = code.replace(/^\s*(set max lines)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set max lines)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.setMaxLines ({count: ${$3}})\n`; return `await dk.setMaxLines ({count: ${$3}})\n`;
}); }];
code = code.replace(/^\s*(set max columns)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set max columns)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.setMaxColumns ({count: ${$3}})\n`; return `await dk.setMaxColumns ({count: ${$3}})\n`;
}); }];
code = code.replace(/^\s*(set translator)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set translator)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.setTranslatorOn ({on: "${$3.toLowerCase()}"})\n`; return `await dk.setTranslatorOn ({on: "${$3.toLowerCase()}"})\n`;
}); }];
code = code.replace(/^\s*(set theme)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set theme)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.setTheme ({theme: "${$3.toLowerCase()}"})\n`; return `await dk.setTheme ({theme: "${$3.toLowerCase()}"})\n`;
}); }];
code = code.replace(/^\s*(set whole word)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(set whole word)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.setWholeWord ({on: "${$3.toLowerCase()}"})\n`; return `await dk.setWholeWord ({on: "${$3.toLowerCase()}"})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*post\s*(.*),\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*post\s*(.*),\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.postByHttp ({url:${$2}, data:${$3}, headers})`; return `${$1} = await sys.postByHttp ({url:${$2}, data:${$3}, headers})`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*put\s*(.*),\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*put\s*(.*),\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.putByHttp ({url:${$2}, data:${$3}, headers})`; return `${$1} = await sys.putByHttp ({url:${$2}, data:${$3}, headers})`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*download\s*(.*),\s*(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*download\s*(.*),\s*(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.download ({handle:page, selector: ${$2}, folder:${$3}})`; return `${$1} = await sys.download ({handle:page, selector: ${$2}, folder:${$3}})`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*CREATE FOLDER\s*(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*CREATE FOLDER\s*(.*)/gim, ($0, $1, $2) => {
return `${$1} = await sys.createFolder ({name:${$2}})`; return `${$1} = await sys.createFolder ({name:${$2}})`;
}); }];
code = code.replace(/^\sSHARE FOLDER\s*(.*)/gim, ($0, $1) => { keywords[i++] = [/^\sSHARE FOLDER\s*(.*)/gim, ($0, $1) => {
return `await sys.shareFolder ({name: ${$1}})`; return `await sys.shareFolder ({name: ${$1}})`;
}); }];
code = code.replace(/^\s*(create a bot farm using)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(create a bot farm using)(\s)(.*)/gim, ($0, $1, $2, $3) => {
return `await sys.createABotFarmUsing ({${$3}})`; return `await sys.createABotFarmUsing ({${$3}})`;
}); }];
code = code.replace(/^\s*(transfer to)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(transfer to)(\s)(.*)/gim, ($0, $1, $2, $3) => {
return `await dk.transferTo ({to:${$3}})\n`; return `await dk.transferTo ({to:${$3}})\n`;
}); }];
code = code.replace(/^\s*(\btransfer\b)(?=(?:[^"]|"[^"]*")*$)/gim, () => { keywords[i++] = [/^\s*(\btransfer\b)(?=(?:[^"]|"[^"]*")*$)/gim, () => {
return `await dk.transferTo ({})\n`; return `await dk.transferTo ({})\n`;
}); }];
code = code.replace(/^\s*(exit)/gim, () => { keywords[i++] = [/^\s*(exit)/gim, () => {
return ``; return ``;
}); }];
code = code.replace(/^\s*(show menu)/gim, () => { keywords[i++] = [/^\s*(show menu)/gim, () => {
return `await dk.showMenu ({})\n`; return `await dk.showMenu ({})\n`;
}); }];
code = code.replace(/^\s*(talk to)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(talk to)(\s)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['mobile', 'message']); const params = this.getParams($3, ['mobile', 'message']);
return `await sys.talkTo(${params})\n`; return `await sys.talkTo(${params})\n`;
}); }];
code = code.replace(/^\s*(talk)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(talk)(\s)(.*)/gim, ($0, $1, $2, $3) => {
if ($3.substr(0, 1) !== "\"") { if ($3.substr(0, 1) !== "\"") {
$3 = `"${$3}"`; $3 = `"${$3}"`;
} }
return `await dk.talk ({text: ${$3}})\n`; return `await dk.talk ({text: ${$3}})\n`;
}); }];
code = code.replace(/^\s*(send sms to)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(send sms to)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['mobile', 'message']); const params = this.getParams($3, ['mobile', 'message']);
return `await sys.sendSmsTo(${params})\n`; return `await sys.sendSmsTo(${params})\n`;
}); }];
code = code.replace(/^\s*(send email)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(send email)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['to', 'subject', 'body']); const params = this.getParams($3, ['to', 'subject', 'body']);
return `await dk.sendEmail(${params})\n`; return `await dk.sendEmail(${params})\n`;
}); }];
code = code.replace(/^\s*(send mail)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(send mail)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['to', 'subject', 'body']); const params = this.getParams($3, ['to', 'subject', 'body']);
return `await dk.sendEmail(${params})\n`; return `await dk.sendEmail(${params})\n`;
}); }];
code = code.replace(/^\s*(send file to)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(send file to)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['mobile', 'filename', 'caption']); const params = this.getParams($3, ['mobile', 'filename', 'caption']);
return `await dk.sendFileTo(${params})\n`; return `await dk.sendFileTo(${params})\n`;
}); }];
code = code.replace(/^\s*(hover)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(hover)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['handle', 'selector']); const params = this.getParams($3, ['handle', 'selector']);
return `await wa.hover (${params})\n`; return `await wa.hover (${params})\n`;
}); }];
code = code.replace(/^\s*(click link text)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(click link text)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams('page,' + $3, ['handle', 'text', 'index']); const params = this.getParams('page,' + $3, ['handle', 'text', 'index']);
return `await wa.linkByText (${params})\n`; return `await wa.linkByText (${params})\n`;
}); }];
code = code.replace(/^\s*(click)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(click)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
// TODO: page is not string. // TODO: page is not string.
const params = getParams('page,' + $3, ['handle', 'frameOrSelector', 'selector']); const params = this.getParams('page,' + $3, ['handle', 'frameOrSelector', 'selector']);
return `await wa.click (${params})\n`; return `await wa.click (${params})\n`;
}); }];
code = code.replace(/^\s*(send file)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(send file)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['filename', 'caption']); const params = this.getParams($3, ['filename', 'caption']);
return `await dk.sendFile(${params})\n`; return `await dk.sendFile(${params})\n`;
}); }];
code = code.replace(/^\s*(copy)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(copy)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['src', 'dst']); const params = this.getParams($3, ['src', 'dst']);
return `await sys.copyFile (${params})\n`; return `await sys.copyFile (${params})\n`;
}); }];
code = code.replace(/^\s*(convert)(\s*)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(convert)(\s*)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['src', 'dst']); const params = this.getParams($3, ['src', 'dst']);
return `await sys.convert (${params})\n`; return `await sys.convert (${params})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*(.*)\s*as chart/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*(.*)\s*as chart/gim, ($0, $1, $2) => {
return `await dk.chart ({type:'bar', data: ${2}, legends:null, transpose: false})\n`; return `await dk.chart ({type:'bar', data: ${2}, legends:null, transpose: false})\n`;
}); }];
code = code.replace(/^\s*(chart)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(chart)(\s)(.*)/gim, ($0, $1, $2, $3) => {
const params = getParams($3, ['type', 'data', 'legends', 'transpose']); const params = this.getParams($3, ['type', 'data', 'legends', 'transpose']);
return `await dk.chart (${params})\n`; return `await dk.chart (${params})\n`;
}); }];
code = code.replace(/^\sMERGE\s(.*)\sWITH\s(.*)BY\s(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\sMERGE\s(.*)\sWITH\s(.*)BY\s(.*)/gim, ($0, $1, $2, $3) => {
return `await sys.merge({file: ${$1}, data: ${$2}, key1: ${$3}})\n`; return `await sys.merge({file: ${$1}, data: ${$2}, key1: ${$3}})\n`;
}); }];
code = code.replace(/^\sPRESS\s(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\sPRESS\s(.*)/gim, ($0, $1, $2) => {
return `await wa.pressKey({handle: page, char: ${$1})\n`; return `await wa.pressKey({handle: page, char: ${$1})\n`;
}); }];
code = code.replace(/^\sSCREENSHOT\s(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\sSCREENSHOT\s(.*)/gim, ($0, $1, $2) => {
return `await wa.screenshot({handle: page, selector: ${$1}})\n`; return `await wa.screenshot({handle: page, selector: ${$1}})\n`;
}); }];
code = code.replace(/^\sTWEET\s(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\sTWEET\s(.*)/gim, ($0, $1, $2) => {
return `await sys.tweet({text: ${$1})\n`; return `await sys.tweet({text: ${$1})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*(.*)\s*as image/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*(.*)\s*as image/gim, ($0, $1, $2) => {
return `${$1} = await sys.asImage({data: ${$2}})\n`; return `${$1} = await sys.asImage({data: ${$2}})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*(.*)\s*as pdf/gim, ($0, $1, $2) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*(.*)\s*as pdf/gim, ($0, $1, $2) => {
return `${$1} = await sys.asPdf({data: ${$2})\n`; return `${$1} = await sys.asPdf({data: ${$2})\n`;
}); }];
code = code.replace(/^\s*(\w+)\s*\=\s*FILL\s(.*)\sWITH\s(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(\w+)\s*\=\s*FILL\s(.*)\sWITH\s(.*)/gim, ($0, $1, $2, $3) => {
return `${$1} = await sys.fill({templateName: ${$2}, data: ${$3}})\n`; return `${$1} = await sys.fill({templateName: ${$2}, data: ${$3}})\n`;
}); }];
code = code.replace(/^\ssave\s(.*)\sas\s(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\ssave\s(.*)\sas\s(.*)/gim, ($0, $1, $2, $3) => {
return `await sys.saveFile({file: ${$2}, data: ${$1})\n`; return `await sys.saveFile({file: ${$2}, data: ${$1})\n`;
}); }];
code = code.replace(/^\s*(save)(\s)(.*)/gim, ($0, $1, $2, $3) => { keywords[i++] = [/^\s*(save)(\s)(.*)/gim, ($0, $1, $2, $3) => {
return `await sys.save({args: [${$3}]})\n`; return `await sys.save({args: [${$3}]})\n`;
}); }];
code = code.replace(/^\sset\s(.*)/gim, ($0, $1, $2) => { keywords[i++] = [/^\sset\s(.*)/gim, ($0, $1, $2) => {
const params = getParams($1, ['file', 'address', 'value']); const params = this.getParams($1, ['file', 'address', 'value']);
return `await sys.set (${params})`; return `await sys.set (${params})`;
}); }];
return keywords;
code = `${code}\n%>`;
return code;
} }
/** /**
* Executes the converted JavaScript from BASIC code inside execution context. * Executes the converted JavaScript from BASIC code inside execution context.
*/ */
@ -810,6 +830,7 @@ export class GBVMService extends GBService {
min: 1, min: 1,
max: 1, max: 1,
debuggerPort: 9222, debuggerPort: 9222,
botId: botId,
cpu: 100, cpu: 100,
memory: 50000, memory: 50000,
time: 60 * 60 * 24 * 14, time: 60 * 60 * 24 * 14,

View file

@ -7,7 +7,8 @@ const { } = require('child_process');
const { dirname } = require('path'); const { dirname } = require('path');
const { fileURLToPath } = require('url'); const { fileURLToPath } = require('url');
const net = require('net'); const net = require('net');
import { GBLog } from 'botlib';
import { GBServer } from '../../../../src/app';
const genericPool = require('generic-pool'); const genericPool = require('generic-pool');
const finalStream = require('final-stream'); const finalStream = require('final-stream');
@ -50,7 +51,7 @@ const createVm2Pool = ({ min, max, ...limits }) => {
const runner = spawn('cpulimit', [ const runner = spawn('cpulimit', [
'-ql', limits.cpu, '-ql', limits.cpu,
'--', '--',
'node', `--inspect-brk=${limits.debuggerPort} --experimental-fetch`, `--max-old-space-size=${limits.memory}`, 'node', `--inspect-brk=${limits.debuggerPort}`, `--experimental-fetch`, `--max-old-space-size=${limits.memory}`,
limits.script limits.script
, ref , ref
], { cwd: limits.cwd, shell: false }); ], { cwd: limits.cwd, shell: false });
@ -61,6 +62,10 @@ const createVm2Pool = ({ min, max, ...limits }) => {
runner.stderr.on('data', (data) => { runner.stderr.on('data', (data) => {
stderrCache = stderrCache + data.toString(); stderrCache = stderrCache + data.toString();
if (stderrCache.includes('failed: address already in use'))
{
limitError = stderrCache;
}
if (stderrCache.includes('FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory')) { if (stderrCache.includes('FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory')) {
limitError = 'code execution exceeed allowed memory'; limitError = 'code execution exceeed allowed memory';
} }
@ -79,17 +84,16 @@ const createVm2Pool = ({ min, max, ...limits }) => {
const run = async (code, scope) => { const run = async (code, scope) => {
const childProcess = await pool.acquire(); const childProcess = await pool.acquire();
await waitUntil(() => childProcess.socket);
const socket = net.createConnection(childProcess.socket);
CDP(async (client) => { CDP(async (client) => {
const { Debugger, Runtime } = client; const { Debugger, Runtime } = client;
try { try {
client.Debugger.paused(() => { GBServer.globals.debuggers[scope.botId] = client;
client.Debugger.resume();
client.close();
});
await client.Runtime.runIfWaitingForDebugger();
await client.Debugger.enable();
} catch (err) { } catch (err) {
console.error(err); GBLog.error(err);
} finally { } finally {
client.close(); client.close();
} }
@ -97,11 +101,6 @@ const createVm2Pool = ({ min, max, ...limits }) => {
console.error(err); console.error(err);
}); });
await waitUntil(() => childProcess.socket);
const socket = net.createConnection(childProcess.socket);
const timer = setTimeout(() => { const timer = setTimeout(() => {
limitError = 'code execution took too long and was killed'; limitError = 'code execution took too long and was killed';
kill(childProcess); kill(childProcess);

View file

@ -86,6 +86,9 @@ export class GuaribasInstance extends Model<GuaribasInstance>
public version: string; public version: string;
@Column(DataType.STRING(64))
public botKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public enabledAdmin: boolean; public enabledAdmin: boolean;

View file

@ -365,7 +365,7 @@ export class GBMinService {
} }
}); });
await sleep(15000); await sleep(5000);
}); });

View file

@ -70,6 +70,7 @@ export class RootData {
public wwwroot: string; // .gbui or a static webapp. public wwwroot: string; // .gbui or a static webapp.
public entryPointDialog: string; // To replace default welcome dialog. public entryPointDialog: string; // To replace default welcome dialog.
public debugConversationId: any; // Used to self-message during debug. public debugConversationId: any; // Used to self-message during debug.
public debuggers: any []; // Client of attached Debugger instances by botId.
} }
/** /**
* General Bots open-core entry point. * General Bots open-core entry point.