diff --git a/packages/basic.gblib/services/DialogKeywords.ts b/packages/basic.gblib/services/DialogKeywords.ts index 21aa4bc3..f46bf89b 100644 --- a/packages/basic.gblib/services/DialogKeywords.ts +++ b/packages/basic.gblib/services/DialogKeywords.ts @@ -644,6 +644,19 @@ export class DialogKeywords { await DialogKeywords.setOption({ pid, name, value }); } + + /** + * Returns current if any continuation token for paginated HTTP requests. + * + * @example CONTINUATION TOKEN + * + */ + public async getContinuationToken({ pid }) { + let { min, user, params, proc } = await DialogKeywords.getProcessInfo(pid); + + return DialogKeywords.getOption({ pid, name: `${proc.executable}-continuationToken` }); + } + /** * Returns bot param persisted on storage. * diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index 9b4c762b..6625bdb8 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -171,19 +171,42 @@ export class GBVMService extends GBService { } else { await this.translateBASIC(mainName, fullFilename, min); } - + // Syncronizes Database Objects with the ones returned from "Word". - const tablesFile = urlJoin(folder, 'tables.json'); + const tablesFile = urlJoin(folder, `${filename}.tables.json`); if (Fs.existsSync(tablesFile)) { const minBoot = GBServer.globals.minBoot; GBLogEx.info(min, `BASIC: Reading tables and sync storage for ${min.botId}...`); - - const tables = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')); - tables.forEach(t => { - minBoot.core.sequelize.define(t.name, t.fields); + + const t = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')); + + const getTypeBasedOnCondition = (t) => { + switch (t) { + case 'string': + return { key: 'STRING' }; + case 'key': + return { key: 'STRING' }; // Assuming key is a string data type + case 'integer': + return { key: 'INTEGER' }; + case 'double': + return { key: 'FLOAT' }; + case 'date': + return { key: 'DATE' }; + case 'boolean': + return { key: 'BOOLEAN' }; + default: + return { key: 'STRING' }; // Default to string if the type is unknown + } + }; + + Object.keys(t.fields).forEach(key => { + let obj = t.fields[key]; + obj.type = getTypeBasedOnCondition(obj.type); }); + minBoot.core.sequelize.define(t.name, t.fields); + await minBoot.core.sequelize.sync(); } @@ -194,6 +217,7 @@ export class GBVMService extends GBService { } public async translateBASIC(mainName, filename: any, min: GBMinInstance) { + // Converts General Bots BASIC into regular VBS let basicCode: string = Fs.readFileSync(filename, 'utf8'); @@ -218,7 +242,7 @@ export class GBVMService extends GBService { } } while (include); - let { code, map, metadata, tasks } = await this.convert(mainName, basicCode); + let { code, map, metadata, tasks } = await this.convert(filename, mainName, basicCode); // Generates function JSON metadata to be used later. @@ -231,7 +255,7 @@ export class GBVMService extends GBService { // Execute off-line code tasks - await this.executeTasks(tasks); + await this.executeTasks(min, tasks); // Run JS into the GB context. @@ -310,6 +334,15 @@ export class GBVMService extends GBService { const hour = (v) => { return (async () => { return await dk.getHourFromDate({v}) })(); }; const base64 = (v) => { return (async () => { return await dk.getCoded({v}) })(); }; const tolist = (v) => { return (async () => { return await dk.getToLst({v}) })(); }; + const uuid = () => { + var dt = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + var r = (dt + Math.random()*16)%16 | 0; + dt = Math.floor(dt/16); + return (c=='x' ? r :(r&0x3|0x8)).toString(16); + }); + return uuid; + }; // Setups interprocess communication from .gbdialog run-time to the BotServer API. @@ -338,29 +371,20 @@ export class GBVMService extends GBService { } - private async executeTasks(tasks) { + private async executeTasks(min, tasks) { for (let i = 0; i < tasks.length; i++) { const task = tasks[i]; if (task.kind === 'writeTableDefinition') { // Creates an empty object that will receive Sequelize fields. - - let obj = {name: task.name}; - obj['fields'] = []; - const fields = task.fields; - fields.forEach(f => { + let obj = { name: task.name }; + obj['fields'] = task.fields; + const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`); + const tablesFile = `${task.file}.tables.json`; - let field = {}; - field[f.name] = f; - - obj['fields'].push(field); - - }); - - const jsonFile = `${task.name}.table.json`; - Fs.writeFileSync(jsonFile, JSON.stringify(obj)); + Fs.writeFileSync(tablesFile, JSON.stringify(obj)); } @@ -454,42 +478,20 @@ export class GBVMService extends GBService { let required = line.indexOf('*') !== -1; line = line.replace('*', ''); - const fieldRegExp = /^\s*(\w+)\s*(\w+)(\((\d+)\))?/gim; + const fieldRegExp = /^\s*(\w+)\s*(\w+)(?:\((\d+)\))?/gim; let reg = fieldRegExp.exec(line); const t = reg[2]; - const n = reg[1]; - - let obj = {}; - let field = {allowNull: !required }; - obj[n] = field; + const name = reg[1]; - const getTypeBasedOnCondition = (t) => { - switch (t) { - case 'string': - return DataTypes.STRING; - case 'key': - return DataTypes.STRING; // Assuming key is a string data type - case 'integer': - return DataTypes.INTEGER; - case 'double': - return DataTypes.FLOAT; - case 'date': - return DataTypes.DATE; - case 'boolean': - return DataTypes.BOOLEAN; - default: - return DataTypes.STRING; // Default to string if the type is unknown - } - }; - - field['type'] = getTypeBasedOnCondition(t); - - if (reg[3]){ - field['size'] = getTypeBasedOnCondition(t); + let definition = { allowNull: !required }; + definition['type'] = t; + + if (reg[3]) { + definition['size'] = Number.parseInt(reg[3]); } - return obj; + return { name, definition }; } /** @@ -498,7 +500,7 @@ export class GBVMService extends GBService { * * @param code General Bots BASIC */ - public async convert(mainName: string, code: string) { + public async convert(filename: string, mainName: string, code: string) { // Start and End of VB2TS tags of processing. @@ -511,9 +513,10 @@ export class GBVMService extends GBService { let description; let table = null; // Used for TABLE keyword. const tasks = []; - let fields = []; + let fields = {}; for (let i = 1; i <= lines.length; i++) { + let line = lines[i - 1]; // Remove lines before statments. @@ -541,27 +544,12 @@ export class GBVMService extends GBService { emmit = false; } - // Inside BEGIN/END table pair containing FIELDS. - - if (table) { - const field = this.parseField(line); - fields.push(field); - } - - const tableKeyword = /^\s*TABLE\s*\"(.*)\"/gim; - let tableReg = tableKeyword.exec(line); - if (tableReg) { - table = tableReg[1]; - emmit = false; - } - - const endTableKeyword = /^\s*END TABLE"/gim; + const endTableKeyword = /^\s*END TABLE\s*/gim; let endTableReg = endTableKeyword.exec(line); - if (endTableReg) { - + if (endTableReg && table) { tasks.push({ - kind: 'writeTableDefinition', name: table, fields: fields + kind: 'writeTableDefinition', file: filename, name: table, fields: fields }); fields = []; @@ -569,16 +557,27 @@ export class GBVMService extends GBService { emmit = false; } + // Inside BEGIN/END table pair containing FIELDS. - if (emmit) { - - // Add additional lines returned from replacement. - - let add = line.split(/\r\n|\r|\n/).length; - current = current + (add ? add : 0); - map[i] = current; - lines[i - 1] = line; + if (table && line.trim() !== '') { + const field = await this.parseField(line); + fields[field.name] = field.definition; + emmit = false; } + + const tableKeyword = /^\s*TABLE\s*(.*)/gim; + let tableReg = tableKeyword.exec(line); + if (tableReg && !table) { + table = tableReg[1]; + emmit = false; + } + + // Add additional lines returned from replacement. + + let add = emmit ? line.split(/\r\n|\r|\n/).length : 0; + current = current + (add ? add : 0); + map[i] = current; + lines[i - 1] = emmit ? line : ''; } code = `${lines.join('\n')}\n`; @@ -617,7 +616,7 @@ export class GBVMService extends GBService { variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, false); // Adds all .gbot params as variables. - + const gbotConfig = JSON.parse(min.instance.params); let keys = Object.keys(gbotConfig); for (let j = 0; j < keys.length; j++) { @@ -650,7 +649,7 @@ export class GBVMService extends GBService { let code = min.sandBoxMap[text]; const channel = step ? step.context.activity.channelId : 'web'; - const pid = GBVMService.createProcessInfo(user, min, channel); + const pid = GBVMService.createProcessInfo(user, min, channel, text); const dk = new DialogKeywords(); const sys = new SystemKeywords(); await dk.setFilter({ pid: pid, value: null }); @@ -723,14 +722,15 @@ export class GBVMService extends GBService { return result; } - public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any) { + public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any, executable: string) { const pid = GBAdminService.getNumberIdentifier(); GBServer.globals.processes[pid] = { pid: pid, userId: user ? user.userId : 0, instanceId: min.instance.instanceId, channel: channel, - roles: 'everyone' + roles: 'everyone', + executable: executable }; return pid; } diff --git a/packages/basic.gblib/services/KeywordsExpressions.ts b/packages/basic.gblib/services/KeywordsExpressions.ts index b621a693..4105a56e 100644 --- a/packages/basic.gblib/services/KeywordsExpressions.ts +++ b/packages/basic.gblib/services/KeywordsExpressions.ts @@ -93,6 +93,7 @@ export class KeywordsExpressions { const convertConditions = input => { var result = input.replace(/ +and +/gim, ' && '); result = result.replace(/ +or +/gim, ' || '); + result = result.replace(/ +not +/gim, ' !'); result = result.replace(/ +<> +/gim, ' !== '); result = result.replace(/ += +/gim, ' === '); return result; @@ -319,8 +320,14 @@ export class KeywordsExpressions { return `${$1} = await dk.getConfig ({pid: pid, name: ${$2}})`; } ]; - + keywords[i++] = [ + /\s*CONTINUATION TOKEN\s*/gim, + () => { + return `await dk.getContinuationToken ({pid: pid})`; + } + ]; + keywords[i++] = [ /^\s*(set hear on)(\s*)(.*)/gim, ($0, $1, $2, $3) => { @@ -340,14 +347,15 @@ export class KeywordsExpressions { __next = true; __calls = 0; __index = 0; + __data = ${$2}; - __url = $2.links?.next?.uri; - __seekToken = $2.links?.self?.headers["MS-ContinuationToken"] - __totalCount = $2["totalCount"]; + __url = __data.links?.next?.uri; + __seekToken = __data.links?.self?.headers["MS-ContinuationToken"] + __totalCount = __data["totalCount"]; while (__next) { - let $1 = $2[__index]; + let ${$1} = __data[__index]; `; } ]; @@ -357,37 +365,37 @@ export class KeywordsExpressions { ($0, $1, $2) => { return ` - - // TRUE if all items are processed. - - if (__index >= __totalCount) { - - // Check if HTTP call limit has reached. - - if (__calls < __totalCalls) { - - // Perform GET request using the constructed URL - - $2 = await sys.get ({pid: pid, file: __url, addressOrHeaders: headers, httpUsername, httpPs}); - // Update current variable handlers. + // TRUE if all items are processed. + + if (__index >= __totalCount) { + + // Check if HTTP call limit has reached. + + if (__calls < __totalCalls) { + + // Perform GET request using the constructed URL + + __data = await sys.get ({pid: pid, file: __url, addressOrHeaders: headers, httpUsername, httpPs}); + + // Update current variable handlers. + + __url = __data.links?.next?.uri; + __seekToken = __data.links?.self?.headers["MS-ContinuationToken"] + __totalCount = __data["totalCount"]; + + index = 0; + __calls++; + + } else { + + next = false; + + } - __url = $2.links?.next?.uri; - __seekToken = $2.links?.self?.headers["MS-ContinuationToken"] - __totalCount = $2["totalCount"]; - - index = 0; - __calls++; - - } else { - - next = false; - - } - - index = index + 1; - } -`; + index = index + 1; + } + }`; } ]; @@ -690,7 +698,7 @@ export class KeywordsExpressions { keywords[i++] = [ /^\s*(set page mode)(\s*)(.*)/gim, ($0, $1, $2, $3) => { - return `await dk.setPageMode ({pid: pid, ${$3}})`; + return `await dk.setPageMode ({pid: pid, value: ${$3}})`; } ]; @@ -704,7 +712,7 @@ export class KeywordsExpressions { keywords[i++] = [ /^\s*set header\s*(.*)\s*as\s*(.*)/gim, ($0, $1, $2) => { - return `headers[${$1}]=${$2})`; + return `headers[${$1.trim()}] = ${$2}`; } ]; @@ -845,14 +853,14 @@ export class KeywordsExpressions { ]; keywords[i++] = [ - /^\s*(\bexit\b)\s*/gim, + /^\sEXIT\s*$/gim, () => { return `return;`; } ]; keywords[i++] = [ - /^\s*(\bEND\b)\s*/gim, + /^\s*END\s*$/gim, () => { return `return;`; } @@ -1072,10 +1080,29 @@ export class KeywordsExpressions { ($0, $1, $2, $3, $4) => { $3 = $3.replace(/\'/g, ''); $3 = $3.replace(/\"/g, ''); - $4 = $4.substr(2); - const fields = $4.split(','); + let fields = $3.split(','); + const table = fields[0].trim(); + fields.shift(); - return `await sys.saveToStorage({pid: pid, file: "${$3}", args: [${$4}]}, fields)`; + const fieldsAsText = fields.join(','); + + let fieldsNamesOnly = []; + let index = 0; + + + fields.forEach(field => { + + // Extracts only the last part of the variable like 'column' + // from 'row.column'. + + const fieldRegExp = /(?:.*\.)(.*)/gim; + let name = fieldRegExp.exec(field)[1] + + fieldsNamesOnly.push (name); + }); + let fieldsNames = fieldsNamesOnly.join(','); + + return `await sys.saveToStorage({pid: pid, table: "${table}", fields: [${fieldsAsText}], fieldsNames: [${fieldsNames}] })`; } ]; diff --git a/packages/basic.gblib/services/SystemKeywords.ts b/packages/basic.gblib/services/SystemKeywords.ts index 240d65f3..318f763c 100644 --- a/packages/basic.gblib/services/SystemKeywords.ts +++ b/packages/basic.gblib/services/SystemKeywords.ts @@ -656,24 +656,18 @@ export class SystemKeywords { */ public async saveToStorage({ pid, table, fields, fieldsNames }) { - const fieldRegExp = /(?:.*\.)(.*)/gim; + GBLog.info(`BASIC: Saving '${table}' (SAVE). Values: ${fields.join(',')}.`); const minBoot = GBServer.globals.minBoot as any; const definition = minBoot.core.sequelize.models[table]; - + let data = {}; let index = 0; - fields.forEach(field => { - - // Extracts only the last part of the variable like 'column' - // from 'row.column'. - - let name = fieldsNames[index]; - name = fieldRegExp.exec(name)[2]; - - data[name] = field; + fieldsNames.forEach(field => { + data[fieldsNames] = fields[index++]; }); + return await definition.create(data); } @@ -1509,7 +1503,7 @@ export class SystemKeywords { public generatePassword(pid) { return GBAdminService.getRndPassword(); } - +static aa; /** * Calls any REST API by using GET HTTP method. * @@ -1519,11 +1513,12 @@ export class SystemKeywords { public async getByHttp({ pid, url, headers, username, ps, qs }) { let options = {}; - const { min, user, params } = await DialogKeywords.getProcessInfo(pid); + const { min, user, params, proc } = await DialogKeywords.getProcessInfo(pid); GBLogEx.info(min, `GET: ${url}`); const pageMode = await DialogKeywords.getOption({ pid, name: 'pageMode' }); - let continuationToken = await DialogKeywords.getOption({ pid, name: 'continuationToken' }); + let continuationToken = await + DialogKeywords.getOption({ pid, name: `${proc.executable}-continuationToken` }); if (pageMode === "auto" && continuationToken) { headers = headers ? headers : {}; @@ -1775,7 +1770,17 @@ export class SystemKeywords { } }; - const result = await fetch(url, options); + + let result; + if (!SystemKeywords.aa){ + SystemKeywords.aa = 1; + return r1; + } else { + SystemKeywords.aa = null; + return r2; + } + + //const result = await fetch(url, options); try { @@ -1783,7 +1788,7 @@ export class SystemKeywords { // Token expired. GBLog.info(`Expired Token for ${url}.`); - await DialogKeywords.setOption({ pid, name: 'continuationToken', value: null }); + await DialogKeywords.setOption({ pid, name: `${proc.executable}-continuationToken`, value: null }); return null; } diff --git a/packages/core.gbapp/services/GBLogEx.ts b/packages/core.gbapp/services/GBLogEx.ts index a57db04f..75b38891 100644 --- a/packages/core.gbapp/services/GBLogEx.ts +++ b/packages/core.gbapp/services/GBLogEx.ts @@ -76,11 +76,14 @@ export class GBLogEx { * Finds and update user agent information to a next available person. */ public static async log(instance: IGBInstance, kind: string, message: string): Promise { - message = message ? message.substring(0, 1023) : null; - return await GuaribasLog.create({ - instanceId: instance ? instance.instanceId : 1, - message: message, - kind: kind - }); + if (process.env.LOG_ON_STORAGE) { + message = message ? message.substring(0, 1023) : null; + + return await GuaribasLog.create({ + instanceId: instance ? instance.instanceId : 1, + message: message, + kind: kind + }); + } } } diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index bc27440b..efd8ad85 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -1031,8 +1031,10 @@ export class GBMinService { return; } } else if (context.activity.type === 'message') { + // Processes messages activities. - const pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId); + + const pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId, null); step.context.activity['pid'] = pid; await this.processMessageActivity(context, min, step, pid);