new(basic.gblib): GPT-3 automatically dialog generation.

This commit is contained in:
rodrigorodriguez 2023-04-09 19:20:15 -03:00
parent db7ed36707
commit a9ce03b353
13 changed files with 512 additions and 172 deletions

BIN
blank.docx Normal file

Binary file not shown.

BIN
blank.xlsx Normal file

Binary file not shown.

View file

@ -6,6 +6,7 @@ import { exec } from 'child_process';
import pjson from './package.json' assert { type: 'json' };
// Displays version of Node JS being used at runtime and others attributes.
console.log(``);
console.log(``);
console.log(` █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® `);

View file

@ -559,7 +559,7 @@ export class DialogKeywords {
// }
let { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const sec = new SecService();
await sec.setParam(user.userId, name, value);
await sec.setParam(user.userId, name , value);
GBLog.info(`BASIC: ${name} = ${value} (botId: ${min.botId})`);
return { min, user, params };
}

View file

@ -137,41 +137,13 @@ 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.
const fullFilename = urlJoin(folder, filename);
if (process.env.DEV_HOTSWAP) {
Fs.watchFile(fullFilename, async () => {
await this.translateBASIC(fullFilename, mainName, min);
await this.translateBASIC(fullFilename, min);
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
});
@ -184,17 +156,17 @@ export class GBVMService extends GBService {
const jsStat = Fs.statSync(jsfile);
const interval = 30000; // If compiled is older 30 seconds, then recompile.
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
await this.translateBASIC(fullFilename, mainName, min);
await this.translateBASIC(fullFilename, min);
}
} else {
await this.translateBASIC(fullFilename, mainName, min);
await this.translateBASIC(fullFilename, min);
}
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
return filename;
}
public async translateBASIC(filename: any, mainName: string, min: GBMinInstance) {
public async translateBASIC(filename: any, min: GBMinInstance) {
// Converts General Bots BASIC into regular VBS
let basicCode: string = Fs.readFileSync(filename, 'utf8');
@ -264,9 +236,11 @@ export class GBVMService extends GBService {
let list = this.list;
let httpUsername = this.httpUsername;
let httpPs = this.httpPs;
let page = null;
let today = this.today;
let now = this.now;
let page = null;
let files = [];
let col = 1;
// Transfers NLP auto variables into global object.
@ -351,18 +325,25 @@ export class GBVMService extends GBService {
var lines = code.split('\n');
const keywords = KeywordsExpressions.getKeywords();
let current = 41;
const map = {};
const map = {};
for (let i = 1; i <= lines.length; i++) {
let line = lines[i - 1];
// Remove lines before statments.
line = line.replace(/^\s*\d+\s*/gi,'');
for (let j = 0; j < keywords.length; j++) {
lines[i - 1] = lines[i - 1].replace(keywords[j][0], keywords[j][1]);
line = line.replace(keywords[j][0], keywords[j][1]);
}
// Add additional lines returned from replacement.
let add = lines[i - 1].split(/\r\n|\r|\n/).length;
let add = line.split(/\r\n|\r|\n/).length;
current = current + (add ? add : 0);
map[i] = current;
lines[i - 1] = line;
}
code = `${lines.join('\n')}\n`;
@ -419,7 +400,8 @@ export class GBVMService extends GBService {
};
const dk = new DialogKeywords();
const sys = new SystemKeywords();
await dk.setFilter ({pid: pid, value: null });
sandbox['variables'] = variables;
sandbox['id'] = sys.getRandomId();
sandbox['username'] = await dk.userName({ pid });

View file

@ -32,7 +32,9 @@
'use strict';
import { GBVMService } from "./GBVMService.js";
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBVMService } from './GBVMService.js';
import Path from 'path';
/**
* Image processing services of conversation to be called by BASIC.
@ -46,7 +48,7 @@ export class KeywordsExpressions {
if (accum.isConcatting) {
accum.soFar[accum.soFar.length - 1] += ',' + curr;
} else {
if(curr===""){
if (curr === '') {
curr = null;
}
accum.soFar.push(curr);
@ -67,15 +69,20 @@ export class KeywordsExpressions {
names.forEach(name => {
let value = items[i];
i++;
json = `${json} "${name}": ${value} ${names.length == i ? '' : ','}`;
json = `${json} "${name}": ${value === undefined ? null : value} ${names.length == i ? '' : ','}`;
});
json = `${json}`;
return json;
};
public static isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
/**
* Returns the list of BASIC keyword and their JS match.
* Based on https://github.com/uweg/vbscript-to-typescript.
*/
public static getKeywords() {
// Keywords from General Bots BASIC.
@ -91,14 +98,134 @@ export class KeywordsExpressions {
return result;
};
keywords[i++] = [
/^\s*INPUT(.*)/gim,
($0, $1, $2) => {
let separator;
if ($1.indexOf(',') > -1){
separator = ',';
}
else if ($1.indexOf(';') > -1){
separator = ';';
}
let parts;
if ( separator && (parts = $1.split(separator)) && parts.length > 1){
return `
TALK ${parts[0]}
HEAR ${parts[1]}`;
}
else
{
return `
HEAR ${$1}`;
}
}
];
keywords[i++] = [
/^\s*WRITE(.*)/gim,
($0, $1, $2) => {
return `PRINT${$1}`;
}
];
keywords[i++] = [/^\s*REM.*/gim, ''];
keywords[i++] = [/^\s*CLOSE.*/gim, ''];
// Always autoclose keyword.
keywords[i++] = [/^\s*CLOSE.*/gim, ''];
keywords[i++] = [/^\s*\'.*/gim, ''];
keywords[i++] = [
/^\s*PRINT ([\s\S]*)/gim,
($0, $1, $2) => {
let sessionName;
let kind = null;
let pos;
$1 = $1.trim();
if ($1.substr(0, 1) === '#') {
let sessionName = $1.substr(1, $1.indexOf(',') - 1);
$1 = $1.replace(/\; \"\,\"/gi, '');
$1 = $1.substr($1.indexOf(',') + 1);
let separator;
if ($1.indexOf(',') > -1){
separator = ',';
}
else if ($1.indexOf(';') > -1){
separator = ';';
}
let items;
if (separator && (items = $1.split(separator)) && items.length > 1) {
return `await sys.save({pid: pid, file: files[${sessionName}], args:[${items.join(',')}]})`;
} else {
return `await sys.set({pid: pid, file: files[${sessionName}], address: col++, name: "${items[0]}", value: ${items[0]}})`;
}
} else {
return `await dk.talk({pid: pid, text: ${$1}})`;
}
}
];
keywords[i++] = [
/^\s*open([\s\S]*)/gim,
($0, $1, $2) => {
let sessionName;
let kind = null;
let pos;
$1 = $1.replace('FOR APPEND', '');
$1 = $1.replace('FOR OUTPUT', '');
if ((pos = $1.match(/\s*AS\s*\#/gi))) {
kind = 'AS';
} else if ((pos = $1.match(/\s*WITH\s*\#/gi))) {
kind = 'WITH';
}
if (pos) {
let part = $1.substr($1.lastIndexOf(pos[0]));
sessionName = `${part.substr(part.indexOf('#') + 1)}`;
$1 = $1.substr(0, $1.lastIndexOf(pos[0]));
}
$1 = $1.trim();
if (!$1.startsWith('"') && !$1.startsWith("'")) {
$1 = `"${$1}"`;
}
const params = this.getParams($1, ['url', 'username', 'password']);
// Checks if it is opening a file or a webpage.
if (kind === 'AS' && KeywordsExpressions.isNumber(sessionName)) {
const jParams = JSON.parse(`{${params}}`);
const filename = `${jParams.url.substr(0, jParams.url.lastIndexOf("."))}.xlsx`;
let code =
`
col = 1
await sys.save({pid: pid,file: "${filename}", args: [id] })
await dk.setFilter ({pid: pid, value: "id=" + id })
files[${sessionName}] = "${filename}"
`;
return code;
} else {
sessionName = `"${sessionName}"`;
kind = `"${kind}"`;
return `page = await wa.openPage({pid: pid, handle: page, sessionKind: ${kind}, sessionName: ${sessionName}, ${params}})`;
}
}
];
keywords[i++] = [
/^\s*((?:[a-z]+.?)(?:(?:\w+).)(?:\w+)*)\s*=\s*SELECT\s*(.*)/gim,
($0, $1, $2) => {
let tableName = /\s*FROM\s*(\w+)/.exec($2)[1];
let tableName = /\s*FROM\s*(\w+\$*)/.exec($2)[1];
let sql = `SELECT ${$2}`.replace(tableName, '?');
return `${$1} = await sys.executeSQL({pid: pid, data:${$1}, sql:"${sql}", tableName:"${tableName}"})\n`;
}
@ -114,8 +241,6 @@ export class KeywordsExpressions {
}
];
// Based on https://github.com/uweg/vbscript-to-typescript.
keywords[i++] = [/^\s*else(?!{)/gim, '}\nelse {'];
keywords[i++] = [/^\s*select case +(.*)/gim, 'switch ($1) {'];
@ -141,30 +266,42 @@ export class KeywordsExpressions {
keywords[i++] = [/^\s*loop *$/gim, '}'];
keywords[i++] = [
/^\s*open\s*(.*)/gim,
/^\s*open([\s\S]*)/gim,
($0, $1, $2) => {
let sessionName;
let kind = null;
let pos;
if (pos = $1.match(/\s*AS\s*\#/)) {
kind = '"AS"';
} else if (pos = $1.match(/\s*WITH\s*\#/)) {
kind = '"WITH"';
$1 = $1.replace(' FOR APPEND', '');
if ((pos = $1.match(/\s*AS\s*\#/))) {
kind = 'AS';
} else if ((pos = $1.match(/\s*WITH\s*\#/))) {
kind = 'WITH';
}
if (pos) {
let part = $1.substr($1.lastIndexOf(pos[0]));
sessionName = `"${part.substr(part.indexOf('#') + 1)}"`;
sessionName = `${part.substr(part.indexOf('#') + 1)}`;
$1 = $1.substr(0, $1.lastIndexOf(pos[0]));
}
$1 = $1.trim();
if (!$1.startsWith('"') && !$1.startsWith("'")) {
$1 = `"${$1}"`;
}
const params = this.getParams($1, ['url', 'username', 'password']);
return `page = await wa.openPage({pid: pid, handle: page, sessionKind: ${kind}, sessionName: ${sessionName}, ${params}})`;
// Checks if it is opening a file or a webpage.
if (kind === 'AS' && KeywordsExpressions.isNumber(sessionName)) {
const jParams = JSON.parse(`{${params}}`);
const filename = `${Path.basename(jParams.url, 'txt')}xlsx`;
return `files[${sessionName}] = "${filename}"`;
} else {
sessionName = `"${sessionName}"`;
kind = `"${kind}"`;
return `page = await wa.openPage({pid: pid, handle: page, sessionKind: ${kind}, sessionName: ${sessionName}, ${params}})`;
}
}
];
@ -176,112 +313,112 @@ export class KeywordsExpressions {
];
keywords[i++] = [
/^\s*hear (\w+) as (\w+( \w+)*.xlsx)/gim,
/^\s*hear (\w+\$*) as (\w+( \w+)*.xlsx)/gim,
($0, $1, $2) => {
return `${$1} = await dk.hear({pid: pid, kind:"sheet", arg: "${$2}"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*login/gim,
/^\s*hear (\w+\$*) as\s*login/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"login"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*email/gim,
/^\s*hear (\w+\$*) as\s*email/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"email"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*integer/gim,
/^\s*hear (\w+\$*) as\s*integer/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"integer"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*file/gim,
/^\s*hear (\w+\$*) as\s*file/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"file"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*boolean/gim,
/^\s*hear (\w+\$*) as\s*boolean/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"boolean"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*name/gim,
/^\s*hear (\w+\$*) as\s*name/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"name"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*date/gim,
/^\s*hear (\w+\$*) as\s*date/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"date"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*hour/gim,
/^\s*hear (\w+\$*) as\s*hour/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"hour"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*phone/gim,
/^\s*hear (\w+\$*) as\s*phone/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"phone"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*money/gim,
/^\s*hear (\w+\$*) as\s*money/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"money")}`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*qrcode/gim,
/^\s*hear (\w+\$*) as\s*qrcode/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"qrcode")}`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*language/gim,
/^\s*hear (\w+\$*) as\s*language/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"language")}`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*zipcode/gim,
/^\s*hear (\w+\$*) as\s*zipcode/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"zipcode")}`;
}
];
keywords[i++] = [
/^\s*hear (\w+) as\s*(.*)/gim,
/^\s*hear (\w+\$*) as\s*(.*)/gim,
($0, $1, $2) => {
return `${$1} = await dk.hear({pid: pid, kind:"menu", args: [${$2}]})`;
}
];
keywords[i++] = [
/^\s*(hear)\s*(\w+)/gim,
/^\s*(hear)\s*(\w+\$*)/gim,
($0, $1, $2) => {
return `${$2} = await dk.hear({pid: pid})`;
}
@ -345,21 +482,21 @@ export class KeywordsExpressions {
];
keywords[i++] = [
/^\s*((?:[a-z]+.?)(?:(?:\w+).)(?:\w+)*)\s*=\s*sort\s*(\w+)\s*by(.*)/gim,
/^\s*((?:[a-z]+.?)(?:(?:\w+).)(?:\w+)*)\s*=\s*sort\s*(\w+\$*)\s*by(.*)/gim,
($0, $1, $2, $3) => {
return `${$1} = await sys.sortBy({pid: pid, array: ${$2}, memberName: "${$3}"})`;
}
];
keywords[i++] = [
/^\s*see\s*text\s*of\s*(\w+)\s*as\s*(\w+)\s*/gim,
/^\s*see\s*text\s*of\s*(\w+\$*)\s*as\s*(\w+\$*)\s*/gim,
($0, $1, $2, $3) => {
return `${$2} = await sys.seeText({pid: pid, url: ${$1})`;
}
];
keywords[i++] = [
/^\s*see\s*caption\s*of\s*(\w+)\s*as(.*)/gim,
/^\s*see\s*caption\s*of\s*(\w+\$*)\s*as(.*)/gim,
($0, $1, $2, $3) => {
return `${$2} = await sys.seeCaption({pid: pid, url: ${$1})`;
}
@ -618,7 +755,6 @@ export class KeywordsExpressions {
// Uses auto quote if this is a frase with more then one word.
if (/\s/.test($3) && $3.substr(0, 1) !== '"') {
$3 = `"${$3}"`;
}
return `await dk.talk ({pid: pid, text: ${$3}})`;
@ -772,19 +908,19 @@ export class KeywordsExpressions {
];
keywords[i++] = [
/^\s*save\s*(\w+)\s*as\s*(.*)/gim,
/^\s*save\s*(\w+\$*)\s*as\s*(.*)/gim,
($0, $1, $2, $3) => {
return `await sys.saveFile({pid: pid, file: ${$2}, data: ${$1}})`;
}
];
keywords[i++] = [
/^\s*(save)(\s*)(.*\.xlsx)(.*)/gim,
($0, $1, $2, $3, $4) => {
$3 = $3.replace (/\'/g, "")
$3 = $3.replace (/\"/g, "")
$4 = $4.substr(2)
return `await sys.save({pid: pid,file: "${$3}" , args: [${$4}]})`;
$3 = $3.replace(/\'/g, '');
$3 = $3.replace(/\"/g, '');
$4 = $4.substr(2);
return `await sys.save({pid: pid, file: "${$3}", args: [${$4}]})`;
}
];

View file

@ -58,6 +58,7 @@ import DynamicsWebApi from 'dynamics-web-api';
import * as MSAL from '@azure/msal-node';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { WebAutomationServices } from './WebAutomationServices.js';
import { KeywordsExpressions } from './KeywordsExpressions.js';
/**
* @fileoverview General Bots server core.
@ -471,7 +472,7 @@ export class SystemKeywords {
* @example SET page, "elementHTMLSelector", "text"
*
*/
public async set({ pid, handle, file, address, value }): Promise<any> {
public async set({ pid, handle, file, address, value, name = null }): Promise<any> {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
// Handles calls for HTML stuff
@ -485,14 +486,6 @@ export class SystemKeywords {
// TODO: Add a semaphore between FILTER and SET.
// Processes FILTER option to ensure parallel SET calls.
const filter = await DialogKeywords.getOption({ pid, name });
if (filter) {
const row = this.find({ pid, handle: null, args: [filter] });
address += row['line'];
}
// Handles calls for BASIC persistence on sheet files.
GBLog.info(`BASIC: Defining '${address}' in '${file}' to '${value}' (SET). `);
@ -501,16 +494,53 @@ export class SystemKeywords {
const botId = min.instance.botId;
const path = DialogKeywords.getGBAIPath(botId, 'gbdata');
let document = await this.internalGetDocument(client, baseUrl, path, file);
let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
let body = { values: [[]] };
// Processes FILTER option to ensure parallel SET calls.
const filter = await DialogKeywords.getOption({ pid, name: 'filter' });
let titleAddress;
if (filter) {
// Transforms address number (col index) to letter based.
// Eg.: REM This is A column and index automatically specified by filter.
// SET file.xlsx, 1, 4000
if (KeywordsExpressions.isNumber(address)) {
address = `${this.numberToLetters(address)}`;
titleAddress = `${address}1:${address}1`;
}
// Processes SET FILTER directive to calculate address.
body.values[0][0] = 'id';
const addressId = 'A1:A1';
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${addressId}')`
)
.patch(body);
const row = await this.find({ pid, handle: null, args: [file, filter] });
if (row) {
address += row['line']; // Eg.: "A" + 1 = "A1".
}
}
address = address.indexOf(':') !== -1 ? address : address + ':' + address;
let document = await this.internalGetDocument(client, baseUrl, path, file);
if (titleAddress) {
body.values[0][0] = name.trim().replace(/[^a-zA-Z]/gi, '');
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${titleAddress}')`
)
.patch(body);
}
let body = { values: [[]] };
body.values[0][0] = value;
let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`
@ -529,7 +559,10 @@ export class SystemKeywords {
});
if (!documents || documents.length === 0) {
throw `File '${file}' specified on GBasic command not found. Check the .gbdata or the .gbdialog associated.`;
throw new Error(
`File '${file}' specified on GBasic command not found. Check the .gbdata or the .gbdialog associated.`,
{ cause: 404 }
);
}
return documents[0];
@ -575,36 +608,84 @@ export class SystemKeywords {
*/
public async save({ pid, file, args }): Promise<any> {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
args.shift();
GBLog.info(`BASIC: Saving '${file}' (SAVE). Args: ${args.join(',')}.`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = min.instance.botId;
const path = DialogKeywords.getGBAIPath(botId, 'gbdata');
let document = await this.internalGetDocument(client, baseUrl, path, file);
let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
let sheets;
let document;
try {
document = await this.internalGetDocument(client, baseUrl, path, file);
sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
} catch (e) {
if (e.cause === 404) {
// Creates the file.
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A2:DX2')/insert`
)
.post({});
const blank = Path.join(process.env.PWD, 'blank.xlsx');
const data = Fs.readFileSync(blank);
await client.api(`${baseUrl}/drive/root:/${path}/${file}:/content`).put(data);
if (args.length > 128) {
throw `File '${file}' has a SAVE call with more than 128 arguments. Check the .gbdialog associated.`;
// Tries to open again.
document = await this.internalGetDocument(client, baseUrl, path, file);
sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
} else {
throw e;
}
}
let address;
let body = { values: [[]] };
const address = `A2:${this.numberToLetters(args.length - 1)}2`;
// Processes FILTER option to ensure parallel SET calls.
const filter = await DialogKeywords.getOption({ pid, name: 'filter' });
if (filter) {
// Creates id row.
body.values[0][0] = 'id';
const addressId = 'A1:A1';
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${addressId}')`
)
.patch(body);
body.values[0][0] = undefined ;
// FINDs the filtered row to be updated.
const row = await this.find({ pid, handle: null, args: [file, filter] });
if (row) {
address = `A${row['line']}:${this.numberToLetters(args.length)}${row['line']}`;
}
}
// Editing or saving detection.
if (!address) {
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A2:DX2')/insert`
)
.post({});
address = `A2:${this.numberToLetters(args.length - 1)}2`;
}
// Fills rows object to call sheet API.
for (let index = 0; index < args.length; index++) {
let value = args[index];
if (value && (await this.isValidDate({ pid, dt: value }))) {
value = `'${value}`;
}
body.values[0][index] = value;
}
// If filter is defined, skips id column.
body.values[0][filter ? index + 1 : index] = value;
}
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`
@ -675,11 +756,7 @@ export class SystemKeywords {
}
public async isValidNumber({ pid, number }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
if (number === '') {
return false;
}
return !isNaN(number);
return KeywordsExpressions.isNumber(number);
}
public isValidHour({ pid, value }) {
@ -1179,6 +1256,33 @@ export class SystemKeywords {
await client.api(`https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/invite`).post(body);
}
public async internalCreateDocument(min, path, content) {
GBLog.info(`BASIC: CREATE DOCUMENT '${path}...'`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const gbaiName = DialogKeywords.getGBAIPath(min.botId);
const tmpDocx = urlJoin(gbaiName, path);
// Templates a blank {content} tag inside the blank.docx.
const blank = Path.join(process.env.PWD, 'blank.docx');
let buf = Fs.readFileSync(blank);
let zip = new PizZip(buf);
let doc = new Docxtemplater();
doc.setOptions({ linebreaks: true });
doc.loadZip(zip);
doc.setData({ content: content }).render();
buf = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE' });
// Performs the upload.
await client.api(`${baseUrl}/drive/root:/${tmpDocx}:/content`).put(buf);
}
public async createDocument({ pid, path, content }) {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
this.internalCreateDocument(min, path, content);
}
/**
* Copies a drive file from a place to another .
*
@ -1224,8 +1328,9 @@ export class SystemKeywords {
parentReference: { driveId: folder.parentReference.driveId, id: folder.id },
name: `${Path.basename(dest)}`
};
return await client.api(`${baseUrl}/drive/items/${srcFile.id}/copy`).post(destFile);
const file = await client.api(`${baseUrl}/drive/items/${srcFile.id}/copy`).post(destFile);
GBLog.info(`BASIC: FINISHED COPY '${src}' to '${dest}'`);
return file;
} catch (error) {
if (error.code === 'itemNotFound') {
GBLog.info(`BASIC: COPY source file not found: ${srcPath}.`);
@ -1234,7 +1339,6 @@ export class SystemKeywords {
}
throw error;
}
GBLog.info(`BASIC: FINISHED COPY '${src}' to '${dest}'`);
}
/**
@ -1468,10 +1572,6 @@ export class SystemKeywords {
localName = Path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.docx`);
Fs.writeFileSync(localName, buf, { encoding: null });
// Loads the file as binary content.
let zip = new PizZip(buf);
// Replace image path on all elements of data.
const images = [];
@ -1537,6 +1637,9 @@ export class SystemKeywords {
}
};
// Loads the file as binary content.
let zip = new PizZip(buf);
let doc = new Docxtemplater();
doc.setOptions({ paragraphLoop: true, linebreaks: true });
doc.loadZip(zip);

View file

@ -166,7 +166,6 @@ export class GBMinService {
// Calls mountBot event to all bots.
let i = 1;
if (instances.length > 1) {
this.bar1 = new cliProgress.SingleBar(
{
@ -179,19 +178,20 @@ export class GBMinService {
this.bar1.start(instances.length, i, { botId: 'Boot' });
}
await CollectionUtil.asyncForEach(instances, (async instance => {
try {
await this['mountBot'](instance);
} catch (error) {
GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`);
}
finally {
if (this.bar1) {
this.bar1.update(i++, { botId: instance.botId });
await CollectionUtil.asyncForEach(
instances,
(async instance => {
try {
await this['mountBot'](instance);
} catch (error) {
GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`);
} finally {
if (this.bar1) {
this.bar1.update(i++, { botId: instance.botId });
}
}
}
}).bind(this));
}).bind(this)
);
if (this.bar1) {
this.bar1.stop();
@ -200,10 +200,10 @@ export class GBMinService {
pingSendTimeout: null,
keepAliveTimeout: null,
listeners: {
unsubscribed(subscriptions: number): void { },
subscribed(subscriptions: number): void { },
disconnected(remoteId: string, connections: number): void { },
connected(remoteId: string, connections: number): void { },
unsubscribed(subscriptions: number): void {},
subscribed(subscriptions: number): void {},
disconnected(remoteId: string, connections: number): void {},
connected(remoteId: string, connections: number): void {},
messageIn(...params): void {
GBLogEx.info(0, '[IN] ' + params);
},
@ -237,8 +237,6 @@ export class GBMinService {
GBLogEx.info(0, 'API RPC HTTP Server started.');
// // Loads schedules.
// GBLog.info(`Preparing SET SCHEDULE dialog calls...`);
@ -286,7 +284,7 @@ export class GBMinService {
/**
* Unmounts the bot web site (default.gbui) secure domain, if any.
*/
public async unloadDomain(instance: IGBInstance) { }
public async unloadDomain(instance: IGBInstance) {}
/**
* Mount the instance by creating an BOT Framework bot object,
@ -331,7 +329,7 @@ export class GBMinService {
const gbai = DialogKeywords.getGBAIPath(min.botId);
let dir = `work/${gbai}/cache`;
const botId = gbai.replace(/\.[^/.]+$/, "");
const botId = gbai.replace(/\.[^/.]+$/, '');
if (!Fs.existsSync(dir)) {
mkdirp.sync(dir);
@ -563,8 +561,9 @@ export class GBMinService {
min.instance.authenticatorTenant,
'/oauth2/authorize'
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.marketplaceId
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
GBLog.info(`HandleOAuthRequests: ${authorizationUrl}.`);
res.redirect(authorizationUrl);
});
@ -1065,8 +1064,9 @@ export class GBMinService {
await this.processEventActivity(min, user, context, step);
}
} catch (error) {
const msg = `ERROR: ${error.message} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
const msg = `ERROR: ${error.message} ${error.error ? error.error.body : ''} ${
error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
GBLog.error(msg);
await min.conversationalService.sendText(
@ -1085,8 +1085,7 @@ export class GBMinService {
if (error.code === 401) {
GBLog.error('Calling processActivity due to Signing Key could not be retrieved error.');
await adapter['processActivity'](req, res, handler);
}
else {
} else {
throw error;
}
}
@ -1275,7 +1274,9 @@ export class GBMinService {
// Files in .gbdialog can be called directly by typing its name normalized into JS .
const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
if (isVMCall) {
if (/create dialog|creative dialog|create a dialog|criar diálogo|criar diálogo/gi.test(context.activity.text)) {
await step.beginDialog('/dialog');
} else if (isVMCall) {
await GBVMService.callVM(context.activity.text, min, step, user, this.deployer, false);
} else if (context.activity.text.charAt(0) === '/') {
const text = context.activity.text;

View file

@ -326,7 +326,7 @@ class GBUIApp extends React.Component {
ref={chat => {
this.chat = chat;
}}
locale={'pt-br'}
locale={'en-us'}
directLine={this.state.line}
webSpeechPonyfillFactory={window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({
credentials: { authorizationToken: token, region: 'westus' }

View file

@ -50,6 +50,9 @@ import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import Fs from 'fs';
import urlJoin from 'url-join';
import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords.js';
/**
* Dialog arguments.
@ -78,6 +81,7 @@ export class AskDialog extends IGBDialog {
min.dialogs.add(new WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min)));
min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service)));
min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min)));
min.dialogs.add(new WaterfallDialog('/dialog', AskDialog.getLLVMDialog(min, service)));
}
private static getAskDialog(min: GBMinInstance) {
@ -234,7 +238,7 @@ export class AskDialog extends IGBDialog {
}
// TODO: https://github.com/GeneralBots/BotServer/issues/9
// else if (user.subjects && user.subjects.length > 0) {
// // ...second time running Search, now with no filter.
// // ..second time running Search, now with no filter.
// const resultsB = await service.ask(min.instance, text, searchScore, undefined);
@ -304,6 +308,7 @@ export class AskDialog extends IGBDialog {
}
const CHATGPT_TIMEOUT = 60 * 1000;
GBLog.info(`ChatGPT being used...`);
const response = await GBServer.globals.chatGPT.sendMessage(text, { timeoutMs: CHATGPT_TIMEOUT });
if (!response) {
@ -363,4 +368,77 @@ export class AskDialog extends IGBDialog {
}
];
}
private static getLLVMDialog(min: GBMinInstance, service: KBService) {
return [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
return await min.conversationalService.prompt(min, step, 'Please, describe the dialog scene.');
},
async step => {
step.options.dialog = step.result;
return await min.conversationalService.prompt(min, step, 'How would you call this?');
},
async step => {
if (GBServer.globals.chatGPT) {
let input = `Write a BASIC program that ${step.options.dialog.toLowerCase()}. And does not explain.`;
await min.conversationalService.sendText(min, step, 'Thank you. The dialog is being written right now...');
const CHATGPT_TIMEOUT = 3 * 60 * 1000;
GBLog.info(`ChatGPT Code: ${input}`);
let response = await GBServer.globals.chatGPT.sendMessage(input, {
timeoutMs: CHATGPT_TIMEOUT
});
// Removes instructions, just code.
response = response.replace(/Copy code/gim, '\n');
let lines = response.split('\n')
let filteredLines = lines.filter(line => /\s*\d+\s*.*/.test(line))
response = filteredLines.join('\n');
// Gets dialog name and file handling
let dialogName = step.result.replace('.', '');
const docx = urlJoin(`${min.botId}.gbdialog`, `${dialogName}.docx`);
const sys = new SystemKeywords();
const document = await sys.internalCreateDocument(min, docx, response);
await service.addQA(min, dialogName, dialogName);
let message = `Waiting for publishing...`;
await min.conversationalService.sendText(min, step, message);
await step.replaceDialog('/publish', { confirm: true });
message = `Dialog is ready! Let's run:`;
await min.conversationalService.sendText(min, step, message);
let sec = new SecService();
const member = step.context.activity.from;
const user = await sec.ensureUser(
min.instance.instanceId,
member.id,
member.name,
'',
'web',
member.name,
null
);
await step.endDialog();
await GBVMService.callVM(dialogName.toLowerCase(),
min, step, user, this.deployer, false);
}
}
];
}
}

View file

@ -529,7 +529,7 @@ export class KBService implements IGBKBService {
const isBasic = answer.toLowerCase().startsWith('/basic');
if (/TALK\s*\".*\"/gi.test(answer) || isBasic) {
const code = isBasic ? answer.substr(6) : answer;
const path = DialogKeywords.getGBAIPath(min.botId,`gbdialog`);
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
const scriptName = `tmp${GBAdminService.getRndReadableIdentifier()}.docx`;
const localName = Path.join('work', path, `${scriptName}`);
Fs.writeFileSync(localName, code, { encoding: null });
@ -604,17 +604,11 @@ export class KBService implements IGBKBService {
answer.content.endsWith('.xlsx')
) {
const path = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
const doc = urlJoin(
GBServer.globals.publicAddress,
'kb',
path,
'assets',
answer.content
);
const doc = urlJoin(GBServer.globals.publicAddress, 'kb', path, 'assets', answer.content);
const url = `http://view.officeapps.live.com/op/view.aspx?src=${doc}`;
await this.playUrl(min, min.conversationalService, step, url, channel);
} else if (answer.content.endsWith('.pdf')) {
const path = DialogKeywords.getGBAIPath(min.botId,`gbkb`);
const path = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
const url = urlJoin('kb', path, 'assets', answer.content);
await this.playUrl(min, min.conversationalService, step, url, channel);
} else if (answer.format === '.md') {
@ -627,6 +621,37 @@ export class KBService implements IGBKBService {
}
}
public async addQA(min, questionText, answerText) {
const pkg = await GuaribasPackage.findOne({
where: { instanceId: min.instance.instanceId }
});
const question = {
from: 'autodialog',
to: '',
subject1: '',
subject2: '',
subject3: '',
subject4: '',
content: questionText.replace(/["]+/g, ''),
instanceId: min.instance.instanceId,
skipIndex: false,
packageId: pkg.packageId
};
const answer = {
instanceId: min.instance.instanceId,
content: answerText,
format: '.txt',
media: null,
packageId: pkg.packageId,
prevId: 0
};
const a =await GuaribasAnswer.create(answer);
question['answerId'] = a.answerId;
const q = await GuaribasQuestion.create(question);
}
public async importKbPackage(
min: GBMinInstance,
localPath: string,
@ -692,9 +717,12 @@ export class KBService implements IGBKBService {
'cache',
`img-docx${GBAdminService.getRndReadableIdentifier()}.png`
);
const url = urlJoin(GBServer.globals.publicAddress,
DialogKeywords.getGBAIPath(instance.botId).replace(/\.[^/.]+$/, "")
, 'cache', Path.basename(localName));
const url = urlJoin(
GBServer.globals.publicAddress,
DialogKeywords.getGBAIPath(instance.botId).replace(/\.[^/.]+$/, ''),
'cache',
Path.basename(localName)
);
const buffer = await image.read();
Fs.writeFileSync(localName, buffer, { encoding: null });
return { src: url };
@ -965,7 +993,7 @@ export class KBService implements IGBKBService {
let category = categoryReg[1];
if (category === 'number') {
min['nerEngine'].addRegexEntity('number','pt', '/d+/gi');
min['nerEngine'].addRegexEntity('number', 'pt', '/d+/gi');
}
if (nameReg) {
let name = nameReg[1];
@ -996,13 +1024,8 @@ export class KBService implements IGBKBService {
GBLog.info(`[GBDeployer] Start Bot Server Side Rendering... ${localPath}`);
const html = await GBSSR.getHTML(min);
let path = DialogKeywords.getGBAIPath(min.botId,`gbui`);
path = Path.join(
process.env.PWD,
'work',
path,
'index.html'
);
let path = DialogKeywords.getGBAIPath(min.botId, `gbui`);
path = Path.join(process.env.PWD, 'work', path, 'index.html');
GBLogEx.info(min, `[GBDeployer] Saving SSR HTML in ${path}.`);
Fs.writeFileSync(path, html, 'utf8');

View file

@ -279,7 +279,7 @@ export class SecService extends GBService {
{
obj = {};
}
obj['name'] = value;
obj[name] = value;
user.params = JSON.stringify(obj);
return await user.save();
}

View file

@ -52,11 +52,13 @@ import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer.js';
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService.js';
import { GBMinService } from '../packages/core.gbapp/services/GBMinService.js';
import auth from 'basic-auth';
import { ChatGPTAPIBrowser } from 'chatgpt';
import child_process from 'child_process';
import * as winston from 'winston-logs-display';
import { RootData } from './RootData.js';
import { GBSSR } from '../packages/core.gbapp/services/GBSSR.js';
import { Mutex } from 'async-mutex';
import { GBVMService } from '../packages/basic.gblib/services/GBVMService.js';
/**
* General Bots open-core entry point.
@ -82,7 +84,7 @@ export class GBServer {
GBLog.error(`Running TEST_SHELL ERROR: ${error}...`);
}
}
const server = express();
GBServer.globals.server = server;
@ -215,6 +217,22 @@ export class GBServer {
GBServer.globals.minService = minService;
await minService.buildMin(instances);
if (process.env.OPENAI_EMAIL) {
if (!GBServer.globals.chatGPT) {
GBServer.globals.chatGPT = new ChatGPTAPIBrowser({
email: process.env.OPENAI_EMAIL,
password: process.env.OPENAI_PASSWORD,
markdown: false
});
await GBServer.globals.chatGPT.init();
}
}
// let s = new GBVMService();
// await s.translateBASIC('work/gptA.vbs', GBServer.globals.minBoot );
// await s.translateBASIC('work/gptB.vbs', GBServer.globals.minBoot );
// await s.translateBASIC('work/gptC.vbs', GBServer.globals.minBoot );
// process.exit(9);
if (process.env.ENABLE_WEBLOG) {
// If global log enabled, reorders transports adding web logging.
@ -257,8 +275,6 @@ export class GBServer {
})();
};
//
if (process.env.CERTIFICATE_PFX) {
const options1 = {
passphrase: process.env.CERTIFICATE_PASSPHRASE,