fix(core.gbapp): #336 timeout in API fixed.

This commit is contained in:
rodrigorodriguez 2023-03-26 19:33:58 -03:00
parent 7bdc57a4a1
commit 0c443618a6
10 changed files with 122 additions and 74 deletions

1
.vscode/launch.json vendored
View file

@ -15,7 +15,6 @@
"args": [
"--no-deprecation",
"--loader ts-node/esm",
"--openssl-legacy-provider",
"--require ${workspaceRoot}/suppress-node-warnings.cjs",
],
"skipFiles": [

View file

@ -13,7 +13,7 @@ console.log(`██ █ ███ █ █ ██ ██ ██
console.log(`██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ `);
console.log(`██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ `);
console.log(` █████ █████ █ ███ █████ ██ ██ ██ ██ ██████ ████ █████ █ ███ 3.0`);
process.stdout.write(` botserver@${pjson.version}, botlib@${pjson.dependencies.botlib}, botbuilder@${pjson.dependencies.botbuilder}, node@${process.version.replace('v', '')}, ${process.platform} ${process.arch}`);
process.stdout.write(` botserver@${pjson.version}, botlib@${pjson.dependencies.botlib}, botbuilder@${pjson.dependencies.botbuilder}, node@${process.version.replace('v', '')}, ${process.platform} ${process.arch} `);
var now = () => {
return new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '') + ' UTC';
@ -28,7 +28,8 @@ try {
};
var processDist = () => {
if (!Fs.existsSync('dist')) {
console.log(`${now()} - Compiling...`);
console.log(`\n`);
console.log(`Generall Bots: Compiling...`);
exec(Path.join(__dirname, 'node_modules/.bin/tsc'), (err, stdout, stderr) => {
if (err) {
console.error(err);
@ -44,7 +45,8 @@ try {
// Installing modules if it has not been done yet.
if (!Fs.existsSync('node_modules')) {
console.log(`${now()} - Installing modules for the first time, please wait...`);
console.log(`\n`);
console.log(`Generall Bots: Installing modules for the first time, please wait...`);
exec('npm install', (err, stdout, stderr) => {
if (err) {
console.error(err);

View file

@ -3,13 +3,13 @@
ECHO General Bots Command Line
IF EXIST node_modules goto COMPILE
ECHO Installing Packages for the first time use...
ECHO Installing Packages for the first time use (it may take several minutes)...
CALL npm install --silent
:COMPILE
IF EXIST dist goto ALLSET
ECHO Compiling...
CALL node_modules\.bin\tsc
npm run build
:ALLSET
node boot.mjs
npm run start

View file

@ -35,7 +35,7 @@
"build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build",
"build-docs": "typedoc --options typedoc.json src/",
"test": "node test.js",
"start": "NODE_NO_WARNINGS=1 node ./boot.mjs --loader ts-node/esm --require ./suppress-node-warnings.cjs ",
"start": "NODE_NO_WARNINGS=1 node ./boot.mjs --loader ts-node/esm --require ./suppress-node-warnings.cjs",
"reverse-proxy": "node_modules/.bin/ngrok http 4242",
"watch:build": "tsc --watch",
"posttypedoc": "shx cp .nojekyll docs/reference/.nojekyll",
@ -119,9 +119,11 @@
"koa": "2.13.4",
"koa-body": "6.0.1",
"koa-router": "12.0.0",
"line-replace": "2.0.1",
"lodash": "4.17.21",
"luxon": "3.1.0",
"mammoth": "1.5.1",
"mime-types": "^2.1.35",
"moment": "1.3.0",
"ms-rest-azure": "3.0.0",
"nexmo": "2.9.1",
@ -182,6 +184,7 @@
"yarn": "1.22.19"
},
"devDependencies": {
"@types/qrcode": "1.5.0",
"@types/url-join": "4.0.1",
"ban-sensitive-files": "1.9.18",
"commitizen": "4.2.2",

View file

@ -36,39 +36,31 @@
'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { GuaribasSchedule } from '../core.gbapp/models/GBModel.js';
import { Sequelize } from 'sequelize-typescript';
import { DialogKeywords } from './services/DialogKeywords.js';
import { SystemKeywords } from './services/SystemKeywords.js';
import { WebAutomationServices } from './services/WebAutomationServices.js';
import { ImageProcessingServices } from './services/ImageProcessingServices.js';
import { DebuggerService } from './services/DebuggerService.js';
import Koa from 'koa';
import cors from '@koa/cors';
import { createRpcServer } from '@push-rpc/core';
import { createHttpKoaMiddleware } from '@push-rpc/http';
import { HttpServerOptions } from '@push-rpc/http/dist/server.js';
import { GBServer } from '../../src/app.js';
import { } from '@push-rpc/core';
import { SocketServer } from '@push-rpc/core';
import * as koaBody from 'koa-body';
import { GBVMService } from './services/GBVMService.js';
import { GBLogEx } from '../core.gbapp/services/GBLogEx.js';
import { CollectionUtil } from 'pragmatismo-io-framework';
export function createKoaHttpServer(
port: number,
getRemoteId: (ctx: Koa.Context) => string,
opts: Partial<HttpServerOptions> = {}
): SocketServer {
const { onError, onConnection, middleware } = createHttpKoaMiddleware(getRemoteId, opts);
const { onError, onConnection, middleware } =
createHttpKoaMiddleware(getRemoteId, opts);
const app = new Koa();
app.use(cors({ origin: '*' }));
app.use(koaBody.koaBody({ multipart: true }));
app.use(middleware);
const server = app.listen(port);
const SERVER_TIMEOUT = 60 * 1000;
const SERVER_TIMEOUT = 60 * 60 * 24 * 1000; // Equals to client RPC set .
server.timeout = SERVER_TIMEOUT;
return {

View file

@ -46,7 +46,7 @@ import { Messages } from '../strings.js';
import * as Fs from 'fs';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import phoneUtil from 'google-libphonenumber';
import phoneUtil from 'google-libphonenumber';
import phone from 'phone';
import DateDiff from 'date-diff';
import tesseract from 'node-tesseract-ocr';
@ -58,7 +58,9 @@ import { WebAutomationServices } from './WebAutomationServices.js';
import urljoin from 'url-join';
import QrScanner from 'qr-scanner';
import pkg from 'whatsapp-web.js';
import { ActivityTypes } from 'botbuilder';
const { List, Buttons } = pkg;
import mime from 'mime-types';
/**
* Default check interval for user replay
@ -204,28 +206,28 @@ export class DialogKeywords {
*
* @example EXIT
*/
public async exit({ }) { }
public async exit({}) {}
/**
* Get active tasks.
*
* @example list = ACTIVE TASKS
*/
public async getActiveTasks({ pid }) { }
public async getActiveTasks({ pid }) {}
/**
* Creates a new deal.
*
* @example CREATE DEAL dealname,contato,empresa,amount
*/
public async createDeal({ pid, dealName, contact, company, amount }) { }
public async createDeal({ pid, dealName, contact, company, amount }) {}
/**
* Finds contacts in XRM.
*
* @example list = FIND CONTACT "Sandra"
*/
public async fndContact({ pid, name }) { }
public async fndContact({ pid, name }) {}
public getContentLocaleWithCulture(contentLocale) {
switch (contentLocale) {
@ -495,7 +497,7 @@ export class DialogKeywords {
*/
public async sendFileTo({ pid, mobile, filename, caption }) {
GBLog.info(`BASIC: SEND FILE TO '${mobile}',filename '${filename}'.`);
return await this.internalSendFile({ pid, mobile, filename, caption });
return await this.internalSendFile({ pid, mobile, channel: null, filename, caption });
}
/**
@ -505,9 +507,10 @@ export class DialogKeywords {
*
*/
public async sendFile({ pid, filename, caption }) {
const { min, user, proc } = await DialogKeywords.getProcessInfo(pid);
GBLog.info(`BASIC: SEND FILE (to: ${user.userSystemId},filename '${filename}'.`);
const mobile = await this.userMobile({ pid });
GBLog.info(`BASIC: SEND FILE (current: ${mobile},filename '${filename}'.`);
return await this.internalSendFile({ pid, mobile, filename, caption });
return await this.internalSendFile({ pid, channel: proc.channel, mobile, filename, caption });
}
/**
@ -676,7 +679,7 @@ export class DialogKeywords {
* @example MENU
*
*/
public async showMenu({ }) {
public async showMenu({}) {
// https://github.com/GeneralBots/BotServer/issues/237
// return await beginDialog('/menu');
}
@ -1031,25 +1034,15 @@ export class DialogKeywords {
static getGBAIPath(botId, packageType = null, packageName = null) {
let gbai = `${botId}.gbai`;
if (!packageType && !packageName) {
return GBConfigService.get('DEV_GBAI') ?
GBConfigService.get('DEV_GBAI') :
gbai;
return GBConfigService.get('DEV_GBAI') ? GBConfigService.get('DEV_GBAI') : gbai;
}
if (GBConfigService.get('DEV_GBAI')) {
gbai = GBConfigService.get('DEV_GBAI');
botId = gbai.replace(/\.[^/.]+$/, "");
return urljoin(GBConfigService.get('DEV_GBAI'),
packageName ?
packageName :
`${botId}.${packageType}`);
}
else {
return urljoin(gbai,
packageName ?
packageName :
`${botId}.${packageType}`);
botId = gbai.replace(/\.[^/.]+$/, '');
return urljoin(GBConfigService.get('DEV_GBAI'), packageName ? packageName : `${botId}.${packageType}`);
} else {
return urljoin(gbai, packageName ? packageName : `${botId}.${packageType}`);
}
}
@ -1094,7 +1087,8 @@ export class DialogKeywords {
return {
min,
user,
params
params,
proc
};
}
@ -1121,21 +1115,22 @@ export class DialogKeywords {
/**
* Processes the sending of the file.
*/
private async internalSendFile({ pid, mobile, filename, caption }) {
private async internalSendFile({ pid, channel, mobile, filename, caption }) {
// Handles SEND FILE TO mobile,element in Web Automation.
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const element = filename._page ? filename._page : filename.screenshot ? filename : null;
let url;
if (element) {
const gbaiName = DialogKeywords.getGBAIPath(min.botId);
const localName = Path.join('work', gbaiName, 'cache', `img${GBAdminService.getRndReadableIdentifier()}.jpg`);
await element.screenshot({ path: localName, fullPage: true });
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', Path.basename(localName));
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', Path.basename(localName));
GBLog.info(`BASIC: WebAutomation: Sending the file ${url} to mobile ${mobile}.`);
await min.conversationalService.sendFile(min, null, mobile, url, caption);
}
// Handles Markdown.
@ -1148,25 +1143,37 @@ export class DialogKeywords {
await min.conversationalService['playMarkdown'](min, md, DialogKeywords.getChannel(), mobile);
} else {
const gbaiName = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
GBLog.info(`BASIC: Sending the file ${filename} to mobile ${mobile}.`);
let url: string;
if (!filename.startsWith('https://')) {
url = urlJoin(
GBServer.globals.publicAddress,
'kb',
gbaiName,
'assets',
filename
);
url = urlJoin(GBServer.globals.publicAddress, 'kb', gbaiName, 'assets', filename);
} else {
url = filename;
}
await min.conversationalService.sendFile(min, null, mobile, url, caption);
}
if (url){
const reply = { type: ActivityTypes.Message, text: caption };
const imageData = await (await fetch(url)).arrayBuffer();
const base64Image = Buffer.from(imageData).toString('base64');
const contentType = mime.lookup(url);
reply['attachments'] = [];
reply['attachments'].push({
name: filename,
contentType: contentType,
contentUrl: `data:${contentType};base64,${base64Image}`
});
if (channel === 'omnichannel') {
await min.conversationalService.sendFile(min, null, mobile, url, caption);
} else {
await min.conversationalService['sendOnConversation'](min, user, reply);
}
}
}
/**
* Generates a new QRCode.

View file

@ -52,6 +52,7 @@ import { KeywordsExpressions } from './KeywordsExpressions.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { SystemKeywords } from './SystemKeywords.js';
import lineReplace from 'line-replace';
/**
* @fileoverview Decision was to priorize security(isolation) and debugging,
@ -113,7 +114,7 @@ export class GBVMService extends GBService {
}
// Process node_modules install.
const node_modules = urlJoin(folder, 'node_modules');
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
if (!Fs.existsSync(node_modules)) {
const packageJson = `
{
@ -136,6 +137,34 @@ export class GBVMService extends GBService {
GBLogEx.info(min, `BASIC: Installing .gbdialog node_modules for ${min.botId}...`);
const npmPath = urlJoin(process.env.PWD, 'node_modules', '.bin', 'npm');
child_process.execSync(`${npmPath} install`, { cwd: folder });
// // Hacks push-rpc to put timeout.
// const inject1 = `
// const { AbortController } = require("node-abort-controller");
// var controller_1 = new AbortController();
// var signal = controller_1.signal;
// setTimeout(function () { controller_1.abort(); }, 24 * 60 * 60 * 1000);`;
// const inject2 = `signal: signal,`;
// const js = Path.join(process.env.PWD, folder, 'node_modules/@push-rpc/http/dist/client.js');
// lineReplace({
// file: js,
// line: 75,
// text: inject1,
// addNewLine: true,
// callback: ({ file, line, text, replacedText, error }) => {
// lineReplace({
// file: js,
// line: 82,
// text: inject2,
// addNewLine: true,
// callback: ({ file, line, text, replacedText, error }) => {
// GBLogEx.info(min, `BASIC: Patching node_modules for ${min.botId} done.`);
// }
// });
// }
// });
}
// Hot swap for .vbs files.
@ -209,16 +238,17 @@ export class GBVMService extends GBService {
const createHttpClient = require("@push-rpc/http").createHttpClient;
// Setups interprocess communication from .gbdialog run-time to the BotServer API.
const optsRPC = {callTimeout: this.callTimeout};
let url;
url = 'http://localhost:${GBVMService.API_PORT}/api/v3/${min.botId}/dk';
const dk = (await createRpcClient(0, () => createHttpClient(url))).remote;
const dk = (await createRpcClient(0, () => createHttpClient(url), optsRPC)).remote;
url = 'http://localhost:${GBVMService.API_PORT}/api/v3/${min.botId}/sys';
const sys = (await createRpcClient(0, () => createHttpClient(url))).remote;
const sys = (await createRpcClient(0, () => createHttpClient(url), optsRPC)).remote;
url = 'http://localhost:${GBVMService.API_PORT}/api/v3/${min.botId}/wa';
const wa = (await createRpcClient(0, () => createHttpClient(url))).remote;
const wa = (await createRpcClient(0, () => createHttpClient(url), optsRPC)).remote;
url = 'http://localhost:${GBVMService.API_PORT}/api/v3/${min.botId}/img';
const img = (await createRpcClient(0, () => createHttpClient(url))).remote;
const img = (await createRpcClient(0, () => createHttpClient(url), optsRPC)).remote;
// Unmarshalls Local variables from server VM.
@ -227,6 +257,7 @@ export class GBVMService extends GBService {
let username = this.username;
let mobile = this.mobile;
let from = this.from;
let channel = this.channel;
let ENTER = this.ENTER;
let headers = this.headers;
let data = this.data;
@ -302,7 +333,7 @@ export class GBVMService extends GBService {
text = text.replace(/\”/gm, '"');
text = text.replace(/\/gm, "'");
text = text.replace(/\/gm, "'");
return text;
}
@ -372,17 +403,18 @@ export class GBVMService extends GBService {
}
const botId = min.botId;
const path = DialogKeywords.getGBAIPath(min.botId,`gbdialog`);
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
const gbdialogPath = urlJoin(process.cwd(), 'work', path);
const scriptPath = urlJoin(gbdialogPath, `${text}.js`);
let code = min.sandBoxMap[text];
const channel = step? step.context.activity.channelId : 'web';
const pid = GBAdminService.getNumberIdentifier();
GBServer.globals.processes[pid] = {
pid: pid,
userId: user.userId,
instanceId: min.instance.instanceId
instanceId: min.instance.instanceId,
channel: channel
};
const dk = new DialogKeywords();
const sys = new SystemKeywords();
@ -400,7 +432,9 @@ export class GBVMService extends GBService {
sandbox['httpPs'] = '';
sandbox['pid'] = pid;
sandbox['contentLocale'] = contentLocale;
sandbox['callTimeout'] = 60 * 60 * 24 * 1000;
sandbox['channel'] = channel;
let result;
try {
@ -411,7 +445,7 @@ export class GBVMService extends GBService {
console: 'inherit',
wrapper: 'commonjs',
require: {
builtin: ['stream', 'http', 'https', 'url', 'zlib', 'net', 'tls', 'crypto', ],
builtin: ['stream', 'http', 'https', 'url', 'zlib', 'net', 'tls', 'crypto'],
root: ['./'],
external: true,
context: 'sandbox'

View file

@ -304,6 +304,17 @@ export class GBConversationalService {
return GBMinService.userMobile(step);
}
public async sendFile2(
min: GBMinInstance,
step: GBDialogStep,
mobile: string,
url: string,
caption: string,
channel: string
): Promise<any> {
return await this.sendFile(min, step, mobile, url , caption);
}
public async sendFile(
min: GBMinInstance,
step: GBDialogStep,

View file

@ -462,7 +462,7 @@ export class GBMinService {
this.createCheckHealthAddress(GBServer.globals.server, min, min.instance);
GBDeployer.mountGBKBAssets(`${instance.botId}.gbkb`, instance.botId, `${instance.botId}.gbkb`);
GBDeployer.mountGBKBAssets(`${botId}.gbkb`, botId, `${botId}.gbkb`);
}
public static isChatAPI(req: any, res: any) {

View file

@ -39,7 +39,7 @@
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { Messages } from '../strings.js';
import * as phone from 'google-libphonenumber';
import * as phone from 'google-libphonenumber';
/**
* Dialogs for handling Menu control.