This commit is contained in:
rodrigorodriguez 2023-03-04 16:27:25 -03:00
parent 4314a37916
commit f3b7c1d77e
11 changed files with 235 additions and 340 deletions

View file

@ -67,6 +67,9 @@
"@microsoft/microsoft-graph-client": "3.0.4", "@microsoft/microsoft-graph-client": "3.0.4",
"@nlpjs/basic": "4.26.1", "@nlpjs/basic": "4.26.1",
"@nosferatu500/textract": "3.1.2", "@nosferatu500/textract": "3.1.2",
"@push-rpc/core": "^1.5.7",
"@push-rpc/http": "^1.5.7",
"@push-rpc/websocket": "^1.5.7",
"@semantic-release/changelog": "5.0.1", "@semantic-release/changelog": "5.0.1",
"@semantic-release/exec": "5.0.0", "@semantic-release/exec": "5.0.0",
"@semantic-release/git": "9.0.0", "@semantic-release/git": "9.0.0",
@ -171,6 +174,7 @@
"whatsapp-web.js": "github:pedroslopez/whatsapp-web.js#fix-buttons-list", "whatsapp-web.js": "github:pedroslopez/whatsapp-web.js#fix-buttons-list",
"winston": "3.8.2", "winston": "3.8.2",
"winston-logs-display": "1.0.0", "winston-logs-display": "1.0.0",
"ws": "^8.12.1",
"yarn": "1.22.19" "yarn": "1.22.19"
}, },
"devDependencies": { "devDependencies": {

View file

@ -39,16 +39,43 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { GuaribasSchedule } from '../core.gbapp/models/GBModel.js'; import { GuaribasSchedule } from '../core.gbapp/models/GBModel.js';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { createServerRouter } from 'typescript-rest-rpc/lib/server.js';
import { DialogKeywords } from './services/DialogKeywords.js'; import { DialogKeywords } from './services/DialogKeywords.js';
import { SystemKeywords } from './services/SystemKeywords.js'; import { SystemKeywords } from './services/SystemKeywords.js';
import { WebAutomationServices } from './services/WebAutomationServices.js'; import { WebAutomationServices } from './services/WebAutomationServices.js';
import { ImageProcessingServices } from './services/ImageProcessingServices.js'; import { ImageProcessingServices } from './services/ImageProcessingServices.js';
import { DebuggerService } from './services/DebuggerService.js'; import { DebuggerService } from './services/DebuggerService.js';
import * as koaBody from 'koa-body';
import Koa from 'koa'; import Koa from 'koa';
import {createRpcServer, createRpcClient} from "@push-rpc/core"
import {createKoaHttpMiddleware, createExpressHttpMiddleware, createHttpClient} from "@push-rpc/http"
import { GBServer } from '../../src/app.js';
const app = new Koa(); const app = new Koa();
import {SocketServer} from "@push-rpc/core"
import * as koaBody from "koa-body"
export function createKoaHttpServer(
port: number,
getRemoteId: (ctx: Koa.Context) => string
): SocketServer {
const {onError, onConnection, middleware} = createKoaHttpMiddleware(getRemoteId)
const app = new Koa()
app.use(koaBody.koaBody({multipart: true}))
app.use(middleware)
const server = app.listen(port)
return {
onError,
onConnection,
close(cb) {
server.close(cb)
},
}
}
/** /**
* Package for core.gbapp. * Package for core.gbapp.
@ -60,9 +87,49 @@ export class GBBasicPackage implements IGBPackage {
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> { public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasSchedule]); core.sequelize.addModels([GuaribasSchedule]);
//app.use(koaBody.koaBody({ multipart: true, }));
app.listen(1111); const dk = new DialogKeywords();
const wa = new WebAutomationServices();
const sys = new SystemKeywords();
const dbg = new DebuggerService();
const img = new ImageProcessingServices();
// remote id is required for assigning separate HTTP requests to a single session
function getRemoteId(ctx: Koa.Context) {
return "1" // share a single session for now, real impl could use cookies or some other meaning for HTTP sessions
}
GBServer.globals.server.dk = createRpcServer(dk, createKoaHttpServer(5555, getRemoteId),{
listeners: {
unsubscribed(subscriptions: number): void {},
subscribed(subscriptions: number): void {},
disconnected(remoteId: string, connections: number): void {
console.log(`Client ${remoteId} disconnected`)
},
connected(remoteId: string, connections: number): void {
console.log(`New client ${remoteId} connected`)
},
messageIn(...params): void {
console.log("IN ", params)
},
messageOut(...params): void {
console.log("OUT ", params)
},
},
pingSendTimeout: null,
keepAliveTimeout: null,
})
console.log("RPC Server started at ws://localhost:5555")
// GBServer.globals.wa = createRpcServer(wa, createWebsocketServer({port: 1112}));
// GBServer.globals.sys = createRpcServer(sys, createWebsocketServer({port: 1113}));
// GBServer.globals.dbg = createRpcServer(dbg, createWebsocketServer({port: 1114}));
// GBServer.globals.img = createRpcServer(img, createWebsocketServer({port: 1115}));
} }
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
@ -81,28 +148,16 @@ export class GBBasicPackage implements IGBPackage {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
public async loadBot (min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {
const dk = new DialogKeywords(min, null, null);
const wa = new WebAutomationServices(min, null, dk); const botId = min.botId;
const sys = new SystemKeywords(min, null, dk, wa); GBServer.globals.debuggers[botId] = {};
const dbg = new DebuggerService(min, null, dk); GBServer.globals.debuggers[botId].state = 0;
const img = new ImageProcessingServices(min, null, dk); GBServer.globals.debuggers[botId].breaks = [];
dk.wa = wa; GBServer.globals.debuggers[botId].stateInfo = 'Stopped';
wa.sys = sys; GBServer.globals.debuggers[botId].childProcess = null;
const dialogRouter = createServerRouter(`/api/v2/${min.botId}/dialog`, dk); GBServer.globals.debuggers[botId].client = null;
const waRouter = createServerRouter(`/api/v2/${min.botId}/webautomation`, wa); GBServer.globals.debuggers[botId].conversationsMap = {};
const sysRouter = createServerRouter(`/api/v2/${min.botId}/system`, sys); GBServer.globals.debuggers[botId].watermarkMap = {};
const dbgRouter = createServerRouter(`/api/v2/${min.botId}/debugger`, dbg);
const imgRouter = createServerRouter(`/api/v2/${min.botId}/imageprocessing`, dbg);
app.use(dialogRouter.routes());
app.use(sysRouter.routes());
app.use(waRouter.routes());
app.use(dbgRouter.routes());
app.use(imgRouter.routes());
app.use(async (ctx, next) => {
if(ctx['status'] === 404){
ctx.status = 404
ctx.body = {msg:'emmmmmmm, seems 404'};
}
});
} }
} }

View file

@ -44,44 +44,6 @@ import { spawn } from 'child_process';
* Web Automation services of conversation to be called by BASIC. * Web Automation services of conversation to be called by BASIC.
*/ */
export class DebuggerService { 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;
conversationsMap = {};
watermarkMap = {};
static systemVariables = [ static systemVariables = [
'AggregateError', 'AggregateError',
'Array', 'Array',
@ -173,28 +135,6 @@ export class DebuggerService {
'valueOf' 'valueOf'
]; ];
/**
* 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);
const botId = min.botId;
GBServer.globals.debuggers[botId] = {};
GBServer.globals.debuggers[botId].state = 0;
GBServer.globals.debuggers[botId].breaks = [];
GBServer.globals.debuggers[botId].stateInfo = 'Stopped';
GBServer.globals.debuggers[botId].childProcess = null;
}
private client;
public async breakpoint ({ botId, line }) { public async breakpoint ({ botId, line }) {
GBLog.info(`BASIC: Enabled breakpoint for ${botId} on ${line}.`); GBLog.info(`BASIC: Enabled breakpoint for ${botId} on ${line}.`);
GBServer.globals.debuggers[botId].breaks.push(Number.parseInt(line)); GBServer.globals.debuggers[botId].breaks.push(Number.parseInt(line));
@ -239,14 +179,18 @@ export class DebuggerService {
} }
public async context ({ botId }) { public async context ({ botId }) {
const conversationId = this.conversationsMap[botId]; const conversationsMap = GBServer.globals.debuggers[botId].conversationsMap;
const watermarkMap = GBServer.globals.debuggers[botId].watermarkMap;
const conversationId = conversationsMap[botId];
let messages = []; let messages = [];
if (this.client) { const client = GBServer.globals.debuggers[botId].client;
const response = await this.client.Conversations.Conversations_GetActivities({ if (client) {
const response = await client.Conversations.Conversations_GetActivities({
conversationId: conversationId, conversationId: conversationId,
watermark: this.watermarkMap[botId] watermark: watermarkMap[botId]
}); });
this.watermarkMap[botId] = response.obj.watermark; watermarkMap[botId] = response.obj.watermark;
let activities = response.obj.activites; let activities = response.obj.activites;
if (activities && activities.length) { if (activities && activities.length) {
@ -272,6 +216,8 @@ export class DebuggerService {
} }
public async getRunning ({ botId, botApiKey, scriptName }) { public async getRunning ({ botId, botApiKey, scriptName }) {
const conversationsMap = GBServer.globals.debuggers[botId].conversationsMap;
let error; let error;
botId = botId[0]; botId = botId[0];
if (!GBServer.globals.debuggers[botId]) { if (!GBServer.globals.debuggers[botId]) {
@ -296,20 +242,21 @@ export class DebuggerService {
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0]; let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
this.client = await new Swagger({ GBServer.globals.debuggers[botId].client = await new Swagger({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')), spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
usePromise: true usePromise: true
}); });
this.client.clientAuthorizations.add( const client = GBServer.globals.debuggers[botId].client;
client.clientAuthorizations.add(
'AuthorizationBotConnector', 'AuthorizationBotConnector',
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${min.instance.webchatKey}`, 'header') new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${min.instance.webchatKey}`, 'header')
); );
const response = await this.client.Conversations.Conversations_StartConversation(); const response = await client.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId; const conversationId = response.obj.conversationId;
this.conversationsMap[botId] = conversationId; conversationsMap[botId] = conversationId;
GBServer.globals.debugConversationId = conversationId; GBServer.globals.debugConversationId = conversationId;
this.client.Conversations.Conversations_PostActivity({ client.Conversations.Conversations_PostActivity({
conversationId: conversationId, conversationId: conversationId,
activity: { activity: {
textFormat: 'plain', textFormat: 'plain',

View file

@ -69,67 +69,6 @@ const DEFAULT_HEAR_POLL_INTERVAL = 500;
* Base services of conversation to be called by BASIC. * Base services of conversation to be called by BASIC.
*/ */
export class DialogKeywords { export class DialogKeywords {
/**
* Reference to minimal bot instance.
*/
public min: GBMinInstance;
/**
* Reference to the base system keywords functions to be called.
*/
public internalSys: SystemKeywords;
/**
* Current user object to get BASIC properties read.
*/
public user;
/**
* HTML browser for conversation over page interaction.
*/
browser: any;
/**
* The number used in this execution for HEAR calls (useful for SET SCHEDULE).
*/
hrOn: string;
userId: GuaribasUser;
debugWeb: boolean;
lastDebugWeb: Date;
public wa;
/**
* SYSTEM account maxLines,when used with impersonated contexts (eg. running in SET SCHEDULE).
*/
maxLines: number = 2000;
public async getDeployer() {
return this.min.deployService;
}
public async getMin() {
return this.min;
}
/**
* When creating this keyword facade,a bot instance is
* specified among the deployer service.
*/
constructor(min: GBMinInstance, deployer: GBDeployer, user) {
this.min = min;
this.user = user;
this.internalSys = new SystemKeywords(min, deployer, this, null);
this.debugWeb = this.min.core.getParam<boolean>(this.min.instance, 'Debug Web Automation', false);
}
/**
* Base reference of system keyword facade,called directly
* by the script.
*/
public sys(): SystemKeywords {
return this.internalSys;
}
/** /**
* *
@ -144,7 +83,8 @@ export class DialogKeywords {
* @param legends * @param legends
* @see https://www.npmjs.com/package/plot * @see https://www.npmjs.com/package/plot
*/ */
public async chart({ type, data, legends, transpose }) { public async chart({pid, type, data, legends, transpose }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
let table = [[]]; let table = [[]];
if (legends) { if (legends) {
@ -195,12 +135,12 @@ export class DialogKeywords {
}; };
} }
const gbaiName = `${this.min.botId}.gbai`; const gbaiName = `${min.botId}.gbai`;
const localName = Path.join('work', gbaiName, 'cache', `img${GBAdminService.getRndReadableIdentifier()}.jpg`); const localName = Path.join('work', gbaiName, 'cache', `img${GBAdminService.getRndReadableIdentifier()}.jpg`);
await ChartServices.screenshot(definition, localName); await ChartServices.screenshot(definition, localName);
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName)); const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', Path.basename(localName));
GBLog.info(`BASIC: Visualization: Chart generated at ${url}.`); GBLog.info(`BASIC: Visualization: Chart generated at ${url}.`);
@ -228,7 +168,8 @@ export class DialogKeywords {
* *
* @example x = TODAY * @example x = TODAY
*/ */
public async getToday({}) { public async getToday({pid}) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
let d = new Date(), let d = new Date(),
month = '' + (d.getMonth() + 1), month = '' + (d.getMonth() + 1),
day = '' + d.getDate(), day = '' + d.getDate(),
@ -241,8 +182,8 @@ export class DialogKeywords {
day = '0' + day; day = '0' + day;
} }
const contentLocale = this.min.core.getParam<string>( const contentLocale = min.core.getParam(
this.min.instance, min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
@ -316,9 +257,10 @@ export class DialogKeywords {
* @example day = WEEKDAY (date) * @example day = WEEKDAY (date)
* *
*/ */
public getWeekFromDate(pid, date) { public async getWeekFromDate({pid, date}) {
const contentLocale = this.min.core.getParam<string>( const { min, user } = await DialogKeywords.getProcessInfo(pid);
this.min.instance, const contentLocale = min.core.getParam(
min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
@ -465,7 +407,7 @@ export class DialogKeywords {
return i; return i;
} }
const contentLocale = this.min.core.getParam<string>( const contentLocale = min.core.getParam(
min.instance, min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
@ -488,9 +430,10 @@ export class DialogKeywords {
* @example SAVE "contacts.xlsx", name, email, NOW * @example SAVE "contacts.xlsx", name, email, NOW
* *
*/ */
public async getNow({}) { public async getNow({pid}) {
const contentLocale = this.min.core.getParam<string>( const { min, user } = await DialogKeywords.getProcessInfo(pid);
this.min.instance, const contentLocale = min.core.getParam(
min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
@ -574,9 +517,10 @@ export class DialogKeywords {
* @example SET LANGUAGE "pt" * @example SET LANGUAGE "pt"
* *
*/ */
public async setLanguage({ language }) { public async setLanguage({ pid, language }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const sec = new SecService(); const sec = new SecService();
await sec.updateUserLocale(this.user.userId, language); await sec.updateUserLocale(user.userId, language);
} }
/** /**
@ -587,7 +531,7 @@ export class DialogKeywords {
*/ */
public async setIdGeneration({ mode }) { public async setIdGeneration({ mode }) {
this['idGeneration'] = mode; this['idGeneration'] = mode;
this['id'] = this.sys().getRandomId(); this['id'] = new SystemKeywords().getRandomId();
} }
private isUserSystemParam(name: string): Boolean { private isUserSystemParam(name: string): Boolean {
@ -751,8 +695,7 @@ export class DialogKeywords {
* *
*/ */
public async getHear({ pid, kind, arg }) { public async getHear({ pid, kind, arg }) {
const process = GBServer.globals.processes[pid]; let { min, user, params } = await DialogKeywords.getProcessInfo(pid);
let { min, user } = await DialogKeywords.getProcessInfo(pid);
// Handles first arg as an array of args. // Handles first arg as an array of args.
@ -772,8 +715,8 @@ export class DialogKeywords {
// containing the specified user other than the actual user // containing the specified user other than the actual user
// TODO: Store hrOn in processInfo. // TODO: Store hrOn in processInfo.
if (this.hrOn) { if (params.hrOn) {
user = await sec.getUserFromAgentSystemId(this.hrOn); user = await sec.getUserFromAgentSystemId(params.hrOn);
} }
const userId = user.userId; const userId = user.userId;
@ -831,7 +774,7 @@ export class DialogKeywords {
// Retrieves the .xlsx file associated with the HEAR var AS file.xlsx. // Retrieves the .xlsx file associated with the HEAR var AS file.xlsx.
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = min.instance.botId; const botId = min.instance.botId;
const path = urljoin(`${botId}.gbai`, `${botId}.gbdata`); const path = urljoin(`${botId}.gbai`, `${botId}.gbdata`);
let url = `${baseUrl}/drive/root:/${path}:/children`; let url = `${baseUrl}/drive/root:/${path}:/children`;
@ -884,13 +827,13 @@ export class DialogKeywords {
// In case of unmatch, asks the person to try again. // In case of unmatch, asks the person to try again.
if (result === null) { if (result === null) {
await this.getTalk({ pid, text: `Escolha por favor um dos itens sugeridos.` }); await this.talk({ pid, text: `Escolha por favor um dos itens sugeridos.` });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
} else if (kind === 'file') { } else if (kind === 'file') {
GBLog.info(`BASIC (${min.botId}): Upload done for ${answer.filename}.`); GBLog.info(`BASIC (${min.botId}): Upload done for ${answer.filename}.`);
const handle = WebAutomationServices.cyrb53(this.min.botId + answer.filename); const handle = WebAutomationServices.cyrb53(min.botId + answer.filename);
GBServer.globals.files[handle] = answer; GBServer.globals.files[handle] = answer;
result = handle; result = handle;
} else if (kind === 'boolean') { } else if (kind === 'boolean') {
@ -907,7 +850,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null) { if (value === null) {
await this.getTalk({ pid, text: 'Por favor, digite um e-mail válido.' }); await this.talk({ pid, text: 'Por favor, digite um e-mail válido.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -920,7 +863,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null || value.length != 1) { if (value === null || value.length != 1) {
await this.getTalk({ pid, text: 'Por favor, digite um nome válido.' }); await this.talk({ pid, text: 'Por favor, digite um nome válido.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -933,7 +876,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null || value.length != 1) { if (value === null || value.length != 1) {
await this.getTalk({ pid, text: 'Por favor, digite um número válido.' }); await this.talk({ pid, text: 'Por favor, digite um número válido.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -948,7 +891,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null || value.length != 1) { if (value === null || value.length != 1) {
await this.getTalk({ pid, text: 'Por favor, digite uma data no formato 12/12/2020.' }); await this.talk({ pid, text: 'Por favor, digite uma data no formato 12/12/2020.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -961,7 +904,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null || value.length != 1) { if (value === null || value.length != 1) {
await this.getTalk({ pid, text: 'Por favor, digite um horário no formato hh:ss.' }); await this.talk({ pid, text: 'Por favor, digite um horário no formato hh:ss.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -980,7 +923,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null || value.length != 1) { if (value === null || value.length != 1) {
await this.getTalk({ pid, text: 'Por favor, digite um valor monetário.' }); await this.talk({ pid, text: 'Por favor, digite um valor monetário.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -992,12 +935,12 @@ export class DialogKeywords {
phoneNumber = phone(answer, { country: 'BRA' })[0]; phoneNumber = phone(answer, { country: 'BRA' })[0];
phoneNumber = phoneUtil.parse(phoneNumber); phoneNumber = phoneUtil.parse(phoneNumber);
} catch (error) { } catch (error) {
await this.getTalk({ pid, text: Messages[locale].validation_enter_valid_mobile }); await this.talk({ pid, text: Messages[locale].validation_enter_valid_mobile });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
if (!phoneUtil.isPossibleNumber(phoneNumber)) { if (!phoneUtil.isPossibleNumber(phoneNumber)) {
await this.getTalk({ pid, text: 'Por favor, digite um número de telefone válido.' }); await this.talk({ pid, text: 'Por favor, digite um número de telefone válido.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -1017,7 +960,7 @@ export class DialogKeywords {
const value = extractEntity(answer); const value = extractEntity(answer);
if (value === null || value.length != 1) { if (value === null || value.length != 1) {
await this.getTalk({ pid, text: 'Por favor, digite um CEP válido.' }); await this.talk({ pid, text: 'Por favor, digite um CEP válido.' });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
@ -1032,7 +975,7 @@ export class DialogKeywords {
}); });
if (result === null) { if (result === null) {
await this.getTalk({ pid, text: `Escolha por favor um dos itens sugeridos.` }); await this.talk({ pid, text: `Escolha por favor um dos itens sugeridos.` });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
} else if (kind === 'language') { } else if (kind === 'language') {
@ -1064,7 +1007,7 @@ export class DialogKeywords {
}); });
if (result === null) { if (result === null) {
await this.getTalk({ pid, text: `Escolha por favor um dos itens sugeridos.` }); await this.talk({ pid, text: `Escolha por favor um dos itens sugeridos.` });
return await this.getHear({ pid, kind, arg }); return await this.getHear({ pid, kind, arg });
} }
} }
@ -1077,7 +1020,8 @@ export class DialogKeywords {
/** /**
* Prepares the next dialog to be shown to the specified user. * Prepares the next dialog to be shown to the specified user.
*/ */
public async gotoDialog({ fromOrDialogName, dialogName }) { public async gotoDialog({ pid, fromOrDialogName, dialogName }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
if (dialogName) { if (dialogName) {
if (dialogName.charAt(0) === '/') { if (dialogName.charAt(0) === '/') {
// https://github.com/GeneralBots/BotServer/issues/308 // https://github.com/GeneralBots/BotServer/issues/308
@ -1087,7 +1031,7 @@ export class DialogKeywords {
let user = await sec.getUserFromSystemId(fromOrDialogName); let user = await sec.getUserFromSystemId(fromOrDialogName);
if (!user) { if (!user) {
user = await sec.ensureUser( user = await sec.ensureUser(
this.min.instance.instanceId, min.instance.instanceId,
fromOrDialogName, fromOrDialogName,
fromOrDialogName, fromOrDialogName,
null, null,
@ -1121,7 +1065,7 @@ export class DialogKeywords {
/** /**
* Talks to the user by using the specified text. * Talks to the user by using the specified text.
*/ */
public async getTalk({ pid, text }) { public async talk({ pid, text }) {
GBLog.info(`BASIC: TALK '${text}'.`); GBLog.info(`BASIC: TALK '${text}'.`);
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);

View file

@ -51,6 +51,7 @@ import { DialogKeywords } from './DialogKeywords.js';
import { KeywordsExpressions } from './KeywordsExpressions.js'; import { KeywordsExpressions } from './KeywordsExpressions.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js'; import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js'; import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { SystemKeywords } from './SystemKeywords.js';
/** /**
* @fileoverview Decision was to priorize security(isolation) and debugging, * @fileoverview Decision was to priorize security(isolation) and debugging,
@ -340,7 +341,6 @@ export class GBVMService extends GBService {
// Creates a class DialogKeywords which is the *this* pointer // Creates a class DialogKeywords which is the *this* pointer
// in BASIC. // in BASIC.
const dk = new DialogKeywords(min, deployer, null);
const sandbox = {}; const sandbox = {};
const contentLocale = min.core.getParam<string>( const contentLocale = min.core.getParam<string>(
min.instance, min.instance,
@ -373,9 +373,11 @@ export class GBVMService extends GBService {
userId: user.userId, userId: user.userId,
instanceId: min.instance.instanceId instanceId: min.instance.instanceId
}; };
const dk = new DialogKeywords();
const sys = new SystemKeywords();
sandbox['variables'] = variables; sandbox['variables'] = variables;
sandbox['id'] = dk.sys().getRandomId(); sandbox['id'] = 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 });
@ -398,7 +400,7 @@ export class GBVMService extends GBService {
console: 'inherit', console: 'inherit',
wrapper: 'commonjs', wrapper: 'commonjs',
require: { require: {
builtin: ['stream', 'http', 'https', 'url', 'zlib'], builtin: ['stream', 'http', 'https', 'url', 'zlib', 'net', 'tls', 'crypto', ],
root: ['./'], root: ['./'],
external: true, external: true,
context: 'sandbox' context: 'sandbox'

View file

@ -46,33 +46,6 @@ import { GBServer } from '../../../src/app.js';
* Image processing services of conversation to be called by BASIC. * Image processing services of conversation to be called by BASIC.
*/ */
export class ImageProcessingServices { export class ImageProcessingServices {
/**
* 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;
sys: any;
/**
* 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;
}
/** /**
* Sharpen the image. * Sharpen the image.
* *
@ -116,7 +89,7 @@ export class ImageProcessingServices {
paths.push(gbfile.path); paths.push(gbfile.path);
}); });
const botId = this.min.instance.botId; const botId = min.instance.botId;
const gbaiName = `${botId}.gbai`; const gbaiName = `${botId}.gbai`;
const img = await joinImages(paths); const img = await joinImages(paths);
const localName = Path.join('work', gbaiName, 'cache', `img-mrg${GBAdminService.getRndReadableIdentifier()}.png`); const localName = Path.join('work', gbaiName, 'cache', `img-mrg${GBAdminService.getRndReadableIdentifier()}.png`);

View file

@ -57,6 +57,7 @@ import ImageModule from 'open-docxtemplater-image-module';
import DynamicsWebApi from 'dynamics-web-api'; import DynamicsWebApi from 'dynamics-web-api';
import * as MSAL from '@azure/msal-node'; import * as MSAL from '@azure/msal-node';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { WebAutomationServices } from './WebAutomationServices.js';
/** /**
@ -67,23 +68,6 @@ import { GBConversationalService } from '../../core.gbapp/services/GBConversatio
* BASIC system class for extra manipulation of bot behaviour. * BASIC system class for extra manipulation of bot behaviour.
*/ */
export class SystemKeywords { export class SystemKeywords {
/**
* Reference to minimal bot instance.
*/
public min: GBMinInstance;
dk: DialogKeywords;
wa;
/**
* When creating this keyword facade, a bot instance is
* specified among the deployer service.
*/
constructor(min: GBMinInstance, deployer: GBDeployer, dk: DialogKeywords, wa) {
this.min = min;
this.wa = wa;
this.dk = dk;
}
public async callVM({ pid, text }) { public async callVM({ pid, text }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
@ -114,7 +98,7 @@ export class SystemKeywords {
let caption = (await computerVisionClient.describeImage(url)).captions[0]; let caption = (await computerVisionClient.describeImage(url)).captions[0];
const contentLocale = this.min.core.getParam<string>( const contentLocale = min.core.getParam(
min.instance, min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
@ -158,7 +142,7 @@ export class SystemKeywords {
public async sortBy({ pid, array, memberName }) { public async sortBy({ pid, array, memberName }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
memberName = memberName.trim(); memberName = memberName.trim();
const contentLocale = this.min.core.getParam<string>( const contentLocale = min.core.getParam(
min.instance, min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
@ -257,7 +241,7 @@ export class SystemKeywords {
// Includes the associated CSS related to current theme. // Includes the associated CSS related to current theme.
const theme = this.dk.user.basicOptions.theme; const theme: string = 'white'; // TODO: params.theme;
switch (theme) { switch (theme) {
case 'white': case 'white':
await page.addStyleTag({ path: 'node_modules/tabulator-tables/dist/css/tabulator_simple.min.css' }); await page.addStyleTag({ path: 'node_modules/tabulator-tables/dist/css/tabulator_simple.min.css' });
@ -342,9 +326,9 @@ export class SystemKeywords {
if (data.data) { if (data.data) {
const gbfile = data.data; const gbfile = data.data;
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
const gbaiName = `${this.min.botId}.gbai`; const gbaiName = `${min.botId}.gbai`;
const tmpDocx = urlJoin(gbaiName, `${botId}.gbdrive`, `tmp${GBAdminService.getRndReadableIdentifier()}.docx`); const tmpDocx = urlJoin(gbaiName, `${botId}.gbdrive`, `tmp${GBAdminService.getRndReadableIdentifier()}.docx`);
// Performs the conversion operation. // Performs the conversion operation.
@ -425,7 +409,7 @@ export class SystemKeywords {
* Retrives a random id with a length of five, every time it is called. * Retrives a random id with a length of five, every time it is called.
*/ */
public getRandomId() { public getRandomId() {
const idGeneration = this.dk['idGeneration']; const idGeneration = '1v'; // TODO: this.dk['idGeneration'];
if (idGeneration && idGeneration.trim().toLowerCase() === 'number') { if (idGeneration && idGeneration.trim().toLowerCase() === 'number') {
return GBAdminService.getNumberIdentifier(); return GBAdminService.getNumberIdentifier();
} else { } else {
@ -488,15 +472,15 @@ export class SystemKeywords {
* @example SET page, "elementHTMLSelector", "text" * @example SET page, "elementHTMLSelector", "text"
* *
*/ */
public async set({ pid, file, address, value }): Promise<any> { public async set({ pid, handle, file, address, value }): Promise<any> {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
// Handles calls for HTML stuff // Handles calls for HTML stuff
if (file._javascriptEnabled) { if (file._javascriptEnabled) {
const page = file;
GBLog.info(`BASIC: Web automation setting ${page}' to '${value}' (SET). `); GBLog.info(`BASIC: Web automation setting ${file}' to '${value}' (SET). `);
await this.wa.setElementText({ page, selector: address, text: value }); await new WebAutomationServices().setElementText({ pid, handle, selector: address, text: value });
return; return;
} }
@ -550,9 +534,10 @@ export class SystemKeywords {
* *
*/ */
public async saveFile({ pid, file, data }): Promise<any> { public async saveFile({ pid, file, data }): Promise<any> {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
GBLog.info(`BASIC: Saving '${file}' (SAVE file).`); GBLog.info(`BASIC: Saving '${file}' (SAVE file).`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
const path = `/${botId}.gbai/${botId}.gbdrive`; const path = `/${botId}.gbai/${botId}.gbdrive`;
// Checks if it is a GB FILE object. // Checks if it is a GB FILE object.
@ -581,11 +566,12 @@ export class SystemKeywords {
* *
*/ */
public async save({ pid, args }): Promise<any> { public async save({ pid, args }): Promise<any> {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const file = args[0]; const file = args[0];
args.shift(); args.shift();
GBLog.info(`BASIC: Saving '${file}' (SAVE). Args: ${args.join(',')}.`); GBLog.info(`BASIC: Saving '${file}' (SAVE). Args: ${args.join(',')}.`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
const path = `/${botId}.gbai/${botId}.gbdata`; const path = `/${botId}.gbai/${botId}.gbdata`;
let document = await this.internalGetDocument(client, baseUrl, path, file); let document = await this.internalGetDocument(client, baseUrl, path, file);
@ -626,6 +612,7 @@ export class SystemKeywords {
* *
*/ */
public async get({ pid, file, addressOrHeaders, httpUsername, httpPs, qs, streaming }): Promise<any> { public async get({ pid, file, addressOrHeaders, httpUsername, httpPs, qs, streaming }): Promise<any> {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
if (file.startsWith('http')) { if (file.startsWith('http')) {
return await this.getByHttp({ return await this.getByHttp({
pid, pid,
@ -637,8 +624,8 @@ export class SystemKeywords {
}); });
} else { } else {
GBLog.info(`BASIC: GET '${addressOrHeaders}' in '${file}'.`); GBLog.info(`BASIC: GET '${addressOrHeaders}' in '${file}'.`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
const path = `/${botId}.gbai/${botId}.gbdata`; const path = `/${botId}.gbai/${botId}.gbdata`;
let document = await this.internalGetDocument(client, baseUrl, path, file); let document = await this.internalGetDocument(client, baseUrl, path, file);
@ -659,9 +646,10 @@ export class SystemKeywords {
} }
} }
public isValidDate({ pid, dt }) { public async isValidDate({ pid, dt }) {
const contentLocale = this.min.core.getParam<string>( const { min, user } = await DialogKeywords.getProcessInfo(pid);
this.min.instance, const contentLocale = min.core.getParam(
min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
@ -678,7 +666,8 @@ export class SystemKeywords {
return !isNaN(date.valueOf()); return !isNaN(date.valueOf());
} }
public isValidNumber({ pid, number }) { public async isValidNumber({ pid, number }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
if (number === '') { if (number === '') {
return false; return false;
} }
@ -704,21 +693,22 @@ export class SystemKeywords {
* *
*/ */
public async getFind({ pid, args }): Promise<any> { public async getFind({ pid, args }): Promise<any> {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const file = args[0]; const file = args[0];
args.shift(); args.shift();
const botId = this.min.instance.botId; const botId = min.instance.botId;
const path = `/${botId}.gbai/${botId}.gbdata`; const path = `/${botId}.gbai/${botId}.gbdata`;
// MAX LINES property. // MAX LINES property.
let maxLines; let maxLines;
if (this.dk.user && this.dk.user.basicOptions && this.dk.user.basicOptions.maxLines) { if (user && params && params.maxLines) {
if (this.dk.user.basicOptions.maxLines.toString().toLowerCase() !== 'default') { if (params.maxLines.toString().toLowerCase() !== 'default') {
maxLines = Number.parseInt(this.dk.user.basicOptions.maxLines).valueOf(); maxLines = Number.parseInt(params.maxLines).valueOf();
} }
} else { } else {
maxLines = this.dk.maxLines; maxLines = maxLines;
} }
GBLog.info(`BASIC: FIND running on ${file} (maxLines: ${maxLines}) and args: ${JSON.stringify(args)}...`); GBLog.info(`BASIC: FIND running on ${file} (maxLines: ${maxLines}) and args: ${JSON.stringify(args)}...`);
@ -760,7 +750,7 @@ export class SystemKeywords {
rows[i] = result[i]; rows[i] = result[i];
} }
} else if (file['cTag']) { } else if (file['cTag']) {
const gbaiName = `${this.min.botId}.gbai`; const gbaiName = `${min.botId}.gbai`;
const localName = Path.join('work', gbaiName, 'cache', `csv${GBAdminService.getRndReadableIdentifier()}.csv`); const localName = Path.join('work', gbaiName, 'cache', `csv${GBAdminService.getRndReadableIdentifier()}.csv`);
const url = file['@microsoft.graph.downloadUrl']; const url = file['@microsoft.graph.downloadUrl'];
const response = await fetch(url); const response = await fetch(url);
@ -785,7 +775,7 @@ export class SystemKeywords {
} }
} }
} else { } else {
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
let document; let document;
document = await this.internalGetDocument(client, baseUrl, path, file); document = await this.internalGetDocument(client, baseUrl, path, file);
@ -834,8 +824,8 @@ export class SystemKeywords {
return filter; return filter;
}; };
const contentLocale = this.min.core.getParam<string>( const contentLocale = min.core.getParam(
this.min.instance, min.instance,
'Default Content Language', 'Default Content Language',
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
@ -888,8 +878,8 @@ export class SystemKeywords {
await CollectionUtil.asyncForEach(filters, async filter => { await CollectionUtil.asyncForEach(filters, async filter => {
let result = rows[foundIndex][filter.columnIndex]; let result = rows[foundIndex][filter.columnIndex];
let wholeWord = true; let wholeWord = true;
if (this.dk.user && this.dk.user.basicOptions && this.dk.user.basicOptions.wholeWord) { if (user && params && params.wholeWord) {
wholeWord = this.dk.user.basicOptions.wholeWord; wholeWord = params.wholeWord;
} }
switch (filter.dataType) { switch (filter.dataType) {
@ -1101,8 +1091,9 @@ export class SystemKeywords {
* *
*/ */
public async createFolder({ pid, name }) { public async createFolder({ pid, name }) {
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const botId = this.min.instance.botId; let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = min.instance.botId;
let path = `/${botId}.gbai/${botId}.gbdrive`; let path = `/${botId}.gbai/${botId}.gbdrive`;
// Extracts each part of path to call create folder to each // Extracts each part of path to call create folder to each
@ -1150,8 +1141,9 @@ export class SystemKeywords {
* *
*/ */
public async shareFolder({ pid, folder, email, message }) { public async shareFolder({ pid, folder, email, message }) {
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const root = urlJoin(`/${this.min.botId}.gbai/${this.min.botId}.gbdrive`, folder); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const root = urlJoin(`/${min.botId}.gbai/${min.botId}.gbdrive`, folder);
const src = await client.api(`${baseUrl}/drive/root:/${root}`).get(); const src = await client.api(`${baseUrl}/drive/root:/${root}`).get();
@ -1177,9 +1169,10 @@ export class SystemKeywords {
* *
*/ */
public async copyFile({ pid, src, dest }) { public async copyFile({ pid, src, dest }) {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
GBLog.info(`BASIC: BEGINING COPY '${src}' to '${dest}'`); GBLog.info(`BASIC: BEGINING COPY '${src}' to '${dest}'`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
// Normalizes all slashes. // Normalizes all slashes.
@ -1237,9 +1230,10 @@ export class SystemKeywords {
* *
*/ */
public async convert({ pid, src, dest }) { public async convert({ pid, src, dest }) {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
GBLog.info(`BASIC: CONVERT '${src}' to '${dest}'`); GBLog.info(`BASIC: CONVERT '${src}' to '${dest}'`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
// Normalizes all slashes. // Normalizes all slashes.
@ -1339,7 +1333,7 @@ export class SystemKeywords {
* @example * @example
* *
* user = put "http://server/path", "data" * user = put "http://server/path", "data"
* talk "The updated user area is" + user.area * talk "The updated user area is" + area
* *
*/ */
public async putByHttp({ pid, url, data, headers }) { public async putByHttp({ pid, url, data, headers }) {
@ -1359,7 +1353,7 @@ export class SystemKeywords {
* @example * @example
* *
* user = post "http://server/path", "data" * user = post "http://server/path", "data"
* talk "The updated user area is" + user.area * talk "The updated user area is" + area
* *
*/ */
public async postByHttp({ pid, url, data, headers }) { public async postByHttp({ pid, url, data, headers }) {
@ -1435,13 +1429,13 @@ export class SystemKeywords {
*/ */
public async fill({ pid, templateName, data }) { public async fill({ pid, templateName, data }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
const botId = this.min.instance.botId; const botId = min.instance.botId;
const gbaiName = `${botId}.gbai`; const gbaiName = `${botId}.gbai`;
let localName; let localName;
// Downloads template from .gbdrive. // Downloads template from .gbdrive.
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
let path = '/' + urlJoin(gbaiName, `${botId}.gbdrive`); let path = '/' + urlJoin(gbaiName, `${botId}.gbdrive`);
let template = await this.internalGetDocument(client, baseUrl, path, templateName); let template = await this.internalGetDocument(client, baseUrl, path, templateName);
let url = template['@microsoft.graph.downloadUrl']; let url = template['@microsoft.graph.downloadUrl'];
@ -1473,7 +1467,7 @@ export class SystemKeywords {
for (const kind of ['png', 'jpg', 'jpeg']) { for (const kind of ['png', 'jpg', 'jpeg']) {
if (value.endsWith && value.endsWith(`.${kind}`)) { if (value.endsWith && value.endsWith(`.${kind}`)) {
const { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
path = urlJoin(gbaiName, `${botId}.gbdrive`); path = urlJoin(gbaiName, `${botId}.gbdrive`);
if (value.indexOf('/') !== -1) { if (value.indexOf('/') !== -1) {
@ -1596,16 +1590,16 @@ export class SystemKeywords {
public async merge({ pid, file, data, key1, key2 }): Promise<any> { public async merge({ pid, file, data, key1, key2 }): Promise<any> {
GBLog.info(`BASIC: MERGE running on ${file} and key1: ${key1}, key2: ${key2}...`); GBLog.info(`BASIC: MERGE running on ${file} and key1: ${key1}, key2: ${key2}...`);
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const botId = min.instance.botId; const botId = min.instance.botId;
const path = `/${botId}.gbai/${botId}.gbdata`; const path = `/${botId}.gbai/${botId}.gbdata`;
// MAX LINES property. // MAX LINES property.
let maxLines = 1000; let maxLines = 1000;
if (this.dk.user && this.dk.user.basicOptions && this.dk.user.basicOptions.maxLines) { if (user && params && params.maxLines) {
if (this.dk.user.basicOptions.maxLines.toString().toLowerCase() !== 'default') { if (params.maxLines.toString().toLowerCase() !== 'default') {
maxLines = Number.parseInt(this.dk.user.basicOptions.maxLines).valueOf(); maxLines = Number.parseInt(params.maxLines).valueOf();
} }
} }
@ -1694,7 +1688,7 @@ export class SystemKeywords {
const address = `${cell}:${cell}`; const address = `${cell}:${cell}`;
if (value !== found[columnName]) { if (value !== found[columnName]) {
await this.set({ pid, file, address, value }); await this.set({ pid, handle:null, file, address, value });
merges++; merges++;
} }
} }

View file

@ -46,39 +46,18 @@ import { DialogKeywords } from './DialogKeywords.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js'; import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { SystemKeywords } from './SystemKeywords.js';
/** /**
* 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 {
/**
* 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). * The number used in this execution for HEAR calls (useful for SET SCHEDULE).
*/ */
hrOn: string; hrOn: string;
userId: GuaribasUser;
debugWeb: boolean; debugWeb: boolean;
lastDebugWeb: Date; lastDebugWeb: Date;
@ -102,17 +81,6 @@ export class WebAutomationServices {
return 4294967296 * (2097151 & h2) + (h1 >>> 0); return 4294967296 * (2097151 & h2) + (h1 >>> 0);
}; };
/**
* 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);
}
public async getCloseHandles({ pid }) { public async getCloseHandles({ pid }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
@ -185,7 +153,7 @@ export class WebAutomationServices {
// A new web session is being created. // A new web session is being created.
handle = WebAutomationServices.cyrb53(this.min.botId + url); handle = WebAutomationServices.cyrb53(min.botId + url);
GBServer.globals.webSessions[handle] = session = {}; GBServer.globals.webSessions[handle] = session = {};
session.sessionName = sessionName; session.sessionName = sessionName;
@ -294,16 +262,17 @@ export class WebAutomationServices {
} }
private async debugStepWeb(pid, page) { private async debugStepWeb(pid, page) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
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;
} }
if (this.debugWeb && refresh) { if (this.debugWeb && refresh) {
const mobile = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null); const mobile = min.core.getParam(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 new DialogKeywords().sendFileTo({ pid: pid, mobile, filename, caption: 'General Bots Debugger' });
} }
this.lastDebugWeb = new Date(); this.lastDebugWeb = new Date();
} }
@ -346,16 +315,17 @@ export class WebAutomationServices {
* *
* @example file = SCREENSHOT page * @example file = SCREENSHOT page
*/ */
public async screenshot({ handle, selector }) { public async screenshot({pid, handle, selector }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = this.getPageByHandle(handle); const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation SCREENSHOT ${selector}.`); GBLog.info(`BASIC: Web Automation SCREENSHOT ${selector}.`);
const gbaiName = `${this.min.botId}.gbai`; const gbaiName = `${min.botId}.gbai`;
const localName = Path.join('work', gbaiName, 'cache', `screen-${GBAdminService.getRndReadableIdentifier()}.jpg`); const localName = Path.join('work', gbaiName, 'cache', `screen-${GBAdminService.getRndReadableIdentifier()}.jpg`);
await page.screenshot({ path: localName }); await page.screenshot({ path: localName });
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName)); const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', Path.basename(localName));
GBLog.info(`BASIC: WebAutomation: Screenshot captured at ${url}.`); GBLog.info(`BASIC: WebAutomation: Screenshot captured at ${url}.`);
return url; return url;
@ -381,7 +351,8 @@ export class WebAutomationServices {
* *
* @example file = DOWNLOAD element, folder * @example file = DOWNLOAD element, folder
*/ */
public async download({ handle, selector, folder }) { public async download({ pid, handle, selector, folder }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = this.getPageByHandle(handle); const page = this.getPageByHandle(handle);
const element = await this.getBySelector({ handle, selector }); const element = await this.getBySelector({ handle, selector });
@ -429,8 +400,8 @@ export class WebAutomationServices {
const res = await fetch(options.uri, options); const res = await fetch(options.uri, options);
result = Buffer.from(await res.arrayBuffer()); result = Buffer.from(await res.arrayBuffer());
} }
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = this.min.instance.botId; const botId = min.instance.botId;
// Normalizes all slashes. // Normalizes all slashes.
@ -444,7 +415,7 @@ export class WebAutomationServices {
// Checks if the destination contains subfolders that // Checks if the destination contains subfolders that
// need to be created. // need to be created.
folder = await this.sys.createFolder(folder); folder = await new SystemKeywords().createFolder(folder);
// Performs the conversion operation getting a reference // Performs the conversion operation getting a reference
// to the source and calling /content on drive API. // to the source and calling /content on drive API.

View file

@ -9,7 +9,7 @@ const evaluate = async (script, scope) => {
console: 'inherit', console: 'inherit',
wrapper: 'none', wrapper: 'none',
require: { require: {
builtin: ['stream', 'http', 'https', 'url', 'buffer', 'zlib', 'isomorphic-fetch', 'punycode', 'encoding'], builtin: ['stream', 'http', 'https', 'url', 'buffer', 'zlib', 'isomorphic-fetch', 'punycode', 'encoding', 'net'],
root: ['./'], root: ['./'],
external: true, external: true,
context: 'sandbox' context: 'sandbox'

View file

@ -911,7 +911,7 @@ export class GBMinService {
); );
const botToken = await credentials.getToken(); const botToken = await credentials.getToken();
const headers = { Authorization: `Bearer ${botToken}` }; const headers = { Authorization: `Bearer ${botToken}` };
const t = new SystemKeywords(null, null, null, null); const t = new SystemKeywords( );
const data = await t.getByHttp({ const data = await t.getByHttp({
pid: 0, pid: 0,
url: file.contentUrl, url: file.contentUrl,

View file

@ -42,7 +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 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.
@ -59,5 +59,10 @@ export class RootData {
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. public debuggers: any[]; // Client of attached Debugger instances by botId.
public chatGPT: any; // ChatGPT API handle (shared Browser). public chatGPT: any; // ChatGPT API handle (shared Browser).
public dk;
public wa;
public sys;
public dbg;
public img;
indexSemaphore: any; indexSemaphore: any;
} }