fix (templates): llm-server almost OK.
This commit is contained in:
parent
93ff7a418e
commit
25882854f8
6 changed files with 260 additions and 240 deletions
6
boot.mjs
6
boot.mjs
|
@ -11,7 +11,7 @@ import {GBUtil} from './dist/src/util.js'
|
||||||
|
|
||||||
// Displays version of Node JS being used at runtime and others attributes.
|
// Displays version of Node JS being used at runtime and others attributes.
|
||||||
|
|
||||||
console.log(`\nLoading virtual machine source code files...`);
|
console.log(`\nLoading General Bots VM...`);
|
||||||
|
|
||||||
var __dirname = process.env.PWD || process.cwd();
|
var __dirname = process.env.PWD || process.cwd();
|
||||||
try {
|
try {
|
||||||
|
@ -24,7 +24,7 @@ try {
|
||||||
var processDist = async () => {
|
var processDist = async () => {
|
||||||
if (!await GBUtil.exists('dist')) {
|
if (!await GBUtil.exists('dist')) {
|
||||||
console.log(`\n`);
|
console.log(`\n`);
|
||||||
console.log(`Generall Bots: Compiling...`);
|
console.log(`General Bots: Compiling...`);
|
||||||
exec(path.join(__dirname, 'node_modules/.bin/tsc'), async (err, stdout, stderr) => {
|
exec(path.join(__dirname, 'node_modules/.bin/tsc'), async (err, stdout, stderr) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -41,7 +41,7 @@ try {
|
||||||
|
|
||||||
if (!await GBUtil.exists('node_modules')) {
|
if (!await GBUtil.exists('node_modules')) {
|
||||||
console.log(`\n`);
|
console.log(`\n`);
|
||||||
console.log(`Generall Bots: Installing modules for the first time, please wait...`);
|
console.log(`General Bots: Installing modules for the first time, please wait...`);
|
||||||
exec('npm install', async (err, stdout, stderr) => {
|
exec('npm install', async (err, stdout, stderr) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
@ -121,7 +121,7 @@ export class GBVMService extends GBService {
|
||||||
const wordFile = filename;
|
const wordFile = filename;
|
||||||
const vbsFile = isWord ? filename.substr(0, filename.indexOf('docx')) + 'vbs' : filename;
|
const vbsFile = isWord ? filename.substr(0, filename.indexOf('docx')) + 'vbs' : filename;
|
||||||
const fullVbsFile = urlJoin(folder, vbsFile);
|
const fullVbsFile = urlJoin(folder, vbsFile);
|
||||||
const docxStat =await fs.stat(urlJoin(folder, wordFile));
|
const docxStat = await fs.stat(urlJoin(folder, wordFile));
|
||||||
const interval = 3000; // If compiled is older 30 seconds, then recompile.
|
const interval = 3000; // If compiled is older 30 seconds, then recompile.
|
||||||
let writeVBS = true;
|
let writeVBS = true;
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ export class GBVMService extends GBService {
|
||||||
// .post(subscription);
|
// .post(subscription);
|
||||||
|
|
||||||
if (await GBUtil.exists(fullVbsFile)) {
|
if (await GBUtil.exists(fullVbsFile)) {
|
||||||
const vbsStat =await fs.stat(fullVbsFile);
|
const vbsStat = await fs.stat(fullVbsFile);
|
||||||
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
|
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
|
||||||
writeVBS = false;
|
writeVBS = false;
|
||||||
}
|
}
|
||||||
|
@ -173,11 +173,11 @@ export class GBVMService extends GBService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const compiledAt =await fs.stat(fullFilename);
|
const compiledAt = await fs.stat(fullFilename);
|
||||||
const jsfile = urlJoin(folder, `${filename}.js`);
|
const jsfile = urlJoin(folder, `${filename}.js`);
|
||||||
|
|
||||||
if (await GBUtil.exists(jsfile)) {
|
if (await GBUtil.exists(jsfile)) {
|
||||||
const jsStat =await fs.stat(jsfile);
|
const jsStat = await fs.stat(jsfile);
|
||||||
const interval = 1000; // If compiled is older 1 seconds, then recompile.
|
const interval = 1000; // If compiled is older 1 seconds, then recompile.
|
||||||
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
|
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
|
||||||
await this.translateBASIC(mainName, fullFilename, min);
|
await this.translateBASIC(mainName, fullFilename, min);
|
||||||
|
@ -196,7 +196,7 @@ export class GBVMService extends GBService {
|
||||||
}
|
}
|
||||||
private async processNodeModules(folder: string, min: GBMinInstance) {
|
private async processNodeModules(folder: string, min: GBMinInstance) {
|
||||||
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
|
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
|
||||||
if (!await GBUtil.exists(node_modules)) {
|
if (!(await GBUtil.exists(node_modules))) {
|
||||||
const packageJson = `
|
const packageJson = `
|
||||||
{
|
{
|
||||||
"name": "${min.botId}.gbdialog",
|
"name": "${min.botId}.gbdialog",
|
||||||
|
@ -498,10 +498,10 @@ export class GBVMService extends GBService {
|
||||||
// Generates function JSON metadata to be used later.
|
// Generates function JSON metadata to be used later.
|
||||||
|
|
||||||
const jsonFile = `${filename}.json`;
|
const jsonFile = `${filename}.json`;
|
||||||
await fs.writeFile(jsonFile, JSON.stringify(metadata));
|
await fs.writeFile(jsonFile, JSON.stringify(metadata));
|
||||||
|
|
||||||
const mapFile = `${filename}.map`;
|
const mapFile = `${filename}.map`;
|
||||||
await fs.writeFile(mapFile, JSON.stringify(map));
|
await fs.writeFile(mapFile, JSON.stringify(map));
|
||||||
|
|
||||||
// Execute off-line code tasks
|
// Execute off-line code tasks
|
||||||
|
|
||||||
|
@ -511,207 +511,11 @@ await fs.writeFile(mapFile, JSON.stringify(map));
|
||||||
|
|
||||||
const jsfile: string = `${filename}.js`;
|
const jsfile: string = `${filename}.js`;
|
||||||
|
|
||||||
code = `
|
code = (await fs.readFile('./vm-inject.js')).toString();
|
||||||
module.exports = (async () => {
|
|
||||||
|
|
||||||
// Imports npm packages for this .gbdialog conversational application.
|
|
||||||
|
|
||||||
require('isomorphic-fetch');
|
|
||||||
const YAML = require('yaml');
|
|
||||||
const http = require('node:http');
|
|
||||||
const retry = require('async-retry');
|
|
||||||
const createRpcClient = require("@push-rpc/core").createRpcClient;
|
|
||||||
const createHttpClient = require("@push-rpc/http").createHttpClient;
|
|
||||||
|
|
||||||
// Unmarshalls Local variables from server VM.
|
|
||||||
|
|
||||||
const pid = this.pid;
|
|
||||||
let id = this.id;
|
|
||||||
let username = this.username;
|
|
||||||
let mobile = this.mobile;
|
|
||||||
let from = this.from;
|
|
||||||
const channel = this.channel;
|
|
||||||
const ENTER = this.ENTER;
|
|
||||||
const headers = this.headers;
|
|
||||||
let httpUsername = this.httpUsername;
|
|
||||||
let httpPs = this.httpPs;
|
|
||||||
let today = this.today;
|
|
||||||
let now = this.now;
|
|
||||||
let date = new Date();
|
|
||||||
let page = null;
|
|
||||||
const files = [];
|
|
||||||
let col = 1;
|
|
||||||
let index = 1;
|
|
||||||
|
|
||||||
const mid = (arr, start, length) => {
|
|
||||||
|
|
||||||
if (length === undefined) {
|
|
||||||
return arr.slice(start);
|
|
||||||
}
|
|
||||||
return arr.slice(start, start + length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes objects in BASIC insensitive.
|
|
||||||
|
|
||||||
const caseInsensitive = (listOrRow) => {
|
|
||||||
|
|
||||||
if (!listOrRow) {
|
|
||||||
|
|
||||||
return listOrRow;
|
|
||||||
};
|
|
||||||
|
|
||||||
const lowercase = (oldKey) => typeof oldKey === 'string' ? oldKey.toLowerCase() : oldKey;
|
|
||||||
|
|
||||||
const createCaseInsensitiveProxy = (obj) => {
|
|
||||||
const propertiesMap = new Map(Object.keys(obj).map(propKey => [lowercase(propKey), obj[propKey]]));
|
|
||||||
const caseInsensitiveGetHandler = {
|
|
||||||
get: (target, property) => propertiesMap.get(lowercase(property))
|
|
||||||
};
|
|
||||||
return new Proxy(obj, caseInsensitiveGetHandler);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (listOrRow.length) {
|
|
||||||
return listOrRow.map(row => createCaseInsensitiveProxy(row));
|
|
||||||
} else {
|
|
||||||
return createCaseInsensitiveProxy(listOrRow);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Transfers auto variables into global object.
|
|
||||||
|
|
||||||
for (const key of Object.keys(this.variables)) {
|
|
||||||
global[key] = this.variables[key];
|
|
||||||
console.log('Defining global variable: ' + key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines local utility BASIC functions.
|
|
||||||
|
|
||||||
const ubound = (gbarray) => {
|
|
||||||
let length = 0;
|
|
||||||
if (gbarray){
|
|
||||||
length = gbarray.length;
|
|
||||||
if (length > 0){
|
|
||||||
if(gbarray[0].gbarray){
|
|
||||||
return length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isarray = (gbarray) => {return Array.isArray(gbarray) };
|
|
||||||
|
|
||||||
// Proxies remote functions as BASIC functions.
|
|
||||||
|
|
||||||
const weekday = (v) => { return (async () => { return await dk.getWeekFromDate({v}) })(); };
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
const random = () => { return Number.parseInt((Math.random() * 8) % 8 * 100000000)};
|
|
||||||
|
|
||||||
|
|
||||||
// Setups interprocess communication from .gbdialog run-time to the BotServer API.
|
|
||||||
|
|
||||||
const optsRPC = {callTimeout: this.callTimeout, messageParser: data => {return JSON.parse(data)}};
|
|
||||||
let url;
|
|
||||||
const agent = http.Agent({ keepAlive: true });
|
|
||||||
|
|
||||||
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/dk';
|
|
||||||
const dk = (await createRpcClient(() => createHttpClient(url, {agent: agent}), optsRPC)).remote;
|
|
||||||
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/sys';
|
|
||||||
const sys = (await createRpcClient(() => createHttpClient(url, {agent: agent}), optsRPC)).remote;
|
|
||||||
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/wa';
|
|
||||||
const wa = (await createRpcClient(() => createHttpClient(url, {agent: agent}), optsRPC)).remote;
|
|
||||||
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/img';
|
|
||||||
const img = (await createRpcClient(() => createHttpClient(url, {agent: agent}), optsRPC)).remote;
|
|
||||||
|
|
||||||
const timeout = (ms)=> {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
const ensureTokens = async (firstTime) => {
|
|
||||||
const REFRESH_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes in milliseconds
|
|
||||||
const tokens = this.tokens ? this.tokens.split(',') : [];
|
|
||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
|
||||||
const tokenName = tokens[i];
|
|
||||||
|
|
||||||
// Auto update Bearer authentication for the first token.
|
|
||||||
const expiresOn = new Date(global[tokenName + '_expiresOn']);
|
|
||||||
const expiration = expiresOn.getTime() - REFRESH_THRESHOLD_MS;
|
|
||||||
|
|
||||||
// Expires token 10min. before or if it the first time, load it.
|
|
||||||
if (expiration < Date.now() || firstTime) {
|
|
||||||
console.log('Expired. Refreshing token...' + expiration);
|
|
||||||
try {
|
|
||||||
const result = await sys.getCustomToken({pid: this.pid, tokenName: tokenName});
|
|
||||||
global[tokenName] = result.token;
|
|
||||||
global[tokenName + '_expiresOn'] = result.expiresOn;
|
|
||||||
console.log('DONE:' + new Date(global[tokenName + '_expiresOn']));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to refresh token for ' + tokenName + ':', error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
headers['Authorization'] = 'Bearer ' + global[tokenName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const sleep = async (ms) => {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
setTimeout(resolve, ms);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const TOYAML = (json) => {
|
|
||||||
const doc = new YAML.Document();
|
|
||||||
doc.contents = json;
|
|
||||||
return doc.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Line of Business logic.
|
|
||||||
|
|
||||||
let __reportMerge = {adds: 0, updates: 0, skipped: 0};
|
|
||||||
let __report = () => {
|
|
||||||
return __reportMerge.title + ' adds: ' + __reportMerge.adds + ', updates: ' + __reportMerge.updates + ' and skipped: ' + __reportMerge.skipped + '.';
|
|
||||||
};
|
|
||||||
let REPORT = 'No report yet';
|
|
||||||
|
|
||||||
try{
|
|
||||||
await ensureTokens(true);
|
|
||||||
${code}
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
console.log(e);
|
|
||||||
|
|
||||||
reject ({message: e.message, name: e.name});
|
|
||||||
}
|
|
||||||
finally{
|
|
||||||
|
|
||||||
// Closes handles if any.
|
|
||||||
|
|
||||||
await wa.closeHandles({pid: pid});
|
|
||||||
await sys.closeHandles({pid: pid});
|
|
||||||
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
`;
|
|
||||||
|
|
||||||
code = ji.default(code, ' ');
|
code = ji.default(code, ' ');
|
||||||
|
|
||||||
await fs.writeFile(jsfile, code);
|
await fs.writeFile(jsfile, code);
|
||||||
GBLogEx.info(min, `Code reloaded: ${path.basename(filename)}.`);
|
GBLogEx.info(min, `Code reloaded: ${path.basename(filename)}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,7 +545,6 @@ await fs.writeFile(jsfile, code);
|
||||||
|
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
if (line.trim()) {
|
if (line.trim()) {
|
||||||
console.log(line);
|
|
||||||
const keyword = /^\s*SET SCHEDULE (.*)/gi;
|
const keyword = /^\s*SET SCHEDULE (.*)/gi;
|
||||||
let result: any = keyword.exec(line);
|
let result: any = keyword.exec(line);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -813,16 +616,15 @@ await fs.writeFile(jsfile, code);
|
||||||
const list = propertiesExp[2] as any;
|
const list = propertiesExp[2] as any;
|
||||||
element = z.enum(list.split(','));
|
element = z.enum(list.split(','));
|
||||||
} else if (t === 'string') {
|
} else if (t === 'string') {
|
||||||
element = z.string({description:description});
|
element = z.string({ description: description });
|
||||||
} else if (t === 'object') {
|
} else if (t === 'object') {
|
||||||
element = z.string({description:description}); // Assuming 'object' is represented as a string here
|
element = z.string({ description: description }); // Assuming 'object' is represented as a string here
|
||||||
} else if (t === 'number') {
|
} else if (t === 'number') {
|
||||||
element = z.number({description:description});
|
element = z.number({ description: description });
|
||||||
} else {
|
} else {
|
||||||
GBLog.warn(`Element type invalid specified on .docx: ${propertiesExp[0]}`);
|
GBLog.warn(`Element type invalid specified on .docx: ${propertiesExp[0]}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
element['type'] = t;
|
element['type'] = t;
|
||||||
properties[propertiesExp[1].trim()] = element;
|
properties[propertiesExp[1].trim()] = element;
|
||||||
}
|
}
|
||||||
|
@ -1177,10 +979,9 @@ await fs.writeFile(jsfile, code);
|
||||||
time: 60 * 60 * 24 * 14,
|
time: 60 * 60 * 24 * 14,
|
||||||
cwd: gbdialogPath,
|
cwd: gbdialogPath,
|
||||||
script: runnerPath
|
script: runnerPath
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
result = await run(code, Object.assign( sandbox, { filename: scriptFilePath}));
|
result = await run(code, Object.assign(sandbox, { filename: scriptFilePath }));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
|
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
|
||||||
|
|
|
@ -574,8 +574,8 @@ export class GBConversationalService {
|
||||||
GBLog.verbose(`Translated text(playMarkdown): ${text}.`);
|
GBLog.verbose(`Translated text(playMarkdown): ${text}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
var renderer = new marked.marked.Renderer();
|
var renderer = new marked.Renderer();
|
||||||
renderer.oldImage = renderer.image;
|
renderer['oldImage'] = renderer.image;
|
||||||
renderer.image = function (href, title, text) {
|
renderer.image = function (href, title, text) {
|
||||||
var videos = ['webm', 'mp4', 'mov'];
|
var videos = ['webm', 'mp4', 'mov'];
|
||||||
var filetype = href.split('.').pop();
|
var filetype = href.split('.').pop();
|
||||||
|
@ -592,7 +592,7 @@ export class GBConversationalService {
|
||||||
'</video>';
|
'</video>';
|
||||||
return out;
|
return out;
|
||||||
} else {
|
} else {
|
||||||
return renderer.oldImage(href, title, text);
|
return renderer['oldImage'](href, title, text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -601,13 +601,8 @@ export class GBConversationalService {
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
gfm: true,
|
gfm: true,
|
||||||
tables: true,
|
|
||||||
breaks: false,
|
breaks: false,
|
||||||
pedantic: false,
|
pedantic: false,
|
||||||
sanitize: false,
|
|
||||||
smartLists: true,
|
|
||||||
smartypants: false,
|
|
||||||
xhtml: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// MSFT Translator breaks markdown, so we need to manually fix it:
|
// MSFT Translator breaks markdown, so we need to manually fix it:
|
||||||
|
@ -619,10 +614,10 @@ export class GBConversationalService {
|
||||||
if (mobile) {
|
if (mobile) {
|
||||||
await this.sendMarkdownToMobile(min, step, mobile, text);
|
await this.sendMarkdownToMobile(min, step, mobile, text);
|
||||||
} else if (GBConfigService.get('DISABLE_WEB') !== 'true') {
|
} else if (GBConfigService.get('DISABLE_WEB') !== 'true') {
|
||||||
const html = marked(text);
|
const html = await marked.marked(text);
|
||||||
await this.sendHTMLToWeb(min, step, html, answer);
|
await this.sendHTMLToWeb(min, step, html, answer);
|
||||||
} else {
|
} else {
|
||||||
const html = marked(text);
|
const html = await marked.marked(text);
|
||||||
await min.conversationalService.sendText(min, step, html);
|
await min.conversationalService.sendText(min, step, html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1230,6 +1230,7 @@ export class GBMinService {
|
||||||
GBLog.error('Calling processActivity due to Signing Key could not be retrieved error.');
|
GBLog.error('Calling processActivity due to Signing Key could not be retrieved error.');
|
||||||
await adapter['processActivity'](req, res, handler);
|
await adapter['processActivity'](req, res, handler);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ BEGIN SYSTEM PROMPT
|
||||||
You must act as a chatbot that will assist a store attendant by following these rules:
|
You must act as a chatbot that will assist a store attendant by following these rules:
|
||||||
Whenever the attendant places an order, it must include the table and the customer's name. Example: A 400ml Pineapple Caipirinha for Rafael at table 10.
|
Whenever the attendant places an order, it must include the table and the customer's name. Example: A 400ml Pineapple Caipirinha for Rafael at table 10.
|
||||||
Orders are based on the products and sides from this product menu:
|
Orders are based on the products and sides from this product menu:
|
||||||
${JSON.stringify(products)}.
|
${TOYAML(products)}.
|
||||||
|
|
||||||
For each order placed, return a JSON containing the product name, the table, and a list of sides with their respective ids.
|
For each order placed, return a JSON containing the product name, the table, and a list of sides with their respective ids.
|
||||||
Keep orderedItems with only one item and keep sideItems only with the sides that were specified.
|
Keep orderedItems with only one item and keep sideItems only with the sides that were specified.
|
||||||
|
|
223
vm-inject.js
Normal file
223
vm-inject.js
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
module.exports = (async () => {
|
||||||
|
// Imports npm packages for this .gbdialog conversational application.
|
||||||
|
|
||||||
|
require('isomorphic-fetch');
|
||||||
|
const YAML = require('yaml');
|
||||||
|
const http = require('node:http');
|
||||||
|
const retry = require('async-retry');
|
||||||
|
const createRpcClient = require('@push-rpc/core').createRpcClient;
|
||||||
|
const createHttpClient = require('@push-rpc/http').createHttpClient;
|
||||||
|
|
||||||
|
// Unmarshalls Local variables from server VM.
|
||||||
|
|
||||||
|
const pid = this.pid;
|
||||||
|
let id = this.id;
|
||||||
|
let username = this.username;
|
||||||
|
let mobile = this.mobile;
|
||||||
|
let from = this.from;
|
||||||
|
const channel = this.channel;
|
||||||
|
const ENTER = this.ENTER;
|
||||||
|
const headers = this.headers;
|
||||||
|
let httpUsername = this.httpUsername;
|
||||||
|
let httpPs = this.httpPs;
|
||||||
|
let today = this.today;
|
||||||
|
let now = this.now;
|
||||||
|
let date = new Date();
|
||||||
|
let page = null;
|
||||||
|
const files = [];
|
||||||
|
let col = 1;
|
||||||
|
let index = 1;
|
||||||
|
|
||||||
|
const mid = (arr, start, length) => {
|
||||||
|
if (length === undefined) {
|
||||||
|
return arr.slice(start);
|
||||||
|
}
|
||||||
|
return arr.slice(start, start + length);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Makes objects in BASIC insensitive.
|
||||||
|
|
||||||
|
const caseInsensitive = listOrRow => {
|
||||||
|
if (!listOrRow) {
|
||||||
|
return listOrRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lowercase = oldKey => (typeof oldKey === 'string' ? oldKey.toLowerCase() : oldKey);
|
||||||
|
|
||||||
|
const createCaseInsensitiveProxy = obj => {
|
||||||
|
const propertiesMap = new Map(Object.keys(obj).map(propKey => [lowercase(propKey), obj[propKey]]));
|
||||||
|
const caseInsensitiveGetHandler = {
|
||||||
|
get: (target, property) => propertiesMap.get(lowercase(property))
|
||||||
|
};
|
||||||
|
return new Proxy(obj, caseInsensitiveGetHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (listOrRow.length) {
|
||||||
|
return listOrRow.map(row => createCaseInsensitiveProxy(row));
|
||||||
|
} else {
|
||||||
|
return createCaseInsensitiveProxy(listOrRow);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transfers auto variables into global object.
|
||||||
|
|
||||||
|
for (const key of Object.keys(this.variables)) {
|
||||||
|
global[key] = this.variables[key];
|
||||||
|
console.log('Defining global variable: ' + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines local utility BASIC functions.
|
||||||
|
|
||||||
|
const ubound = gbarray => {
|
||||||
|
let length = 0;
|
||||||
|
if (gbarray) {
|
||||||
|
length = gbarray.length;
|
||||||
|
if (length > 0) {
|
||||||
|
if (gbarray[0].gbarray) {
|
||||||
|
return length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isarray = gbarray => {
|
||||||
|
return Array.isArray(gbarray);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Proxies remote functions as BASIC functions.
|
||||||
|
|
||||||
|
const weekday = v => {
|
||||||
|
return (async () => {
|
||||||
|
return await dk.getWeekFromDate({ v });
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
const random = () => {
|
||||||
|
return Number.parseInt(((Math.random() * 8) % 8) * 100000000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setups interprocess communication from .gbdialog run-time to the BotServer API.
|
||||||
|
|
||||||
|
const optsRPC = {
|
||||||
|
callTimeout: this.callTimeout,
|
||||||
|
messageParser: data => {
|
||||||
|
return JSON.parse(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let url;
|
||||||
|
const agent = http.Agent({ keepAlive: true });
|
||||||
|
|
||||||
|
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/dk';
|
||||||
|
const dk = (await createRpcClient(() => createHttpClient(url, { agent: agent }), optsRPC)).remote;
|
||||||
|
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/sys';
|
||||||
|
const sys = (await createRpcClient(() => createHttpClient(url, { agent: agent }), optsRPC)).remote;
|
||||||
|
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/wa';
|
||||||
|
const wa = (await createRpcClient(() => createHttpClient(url, { agent: agent }), optsRPC)).remote;
|
||||||
|
url = 'http://localhost:${GBVMService.API_PORT}/${min.botId}/img';
|
||||||
|
const img = (await createRpcClient(() => createHttpClient(url, { agent: agent }), optsRPC)).remote;
|
||||||
|
|
||||||
|
const timeout = ms => {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
};
|
||||||
|
|
||||||
|
const ensureTokens = async firstTime => {
|
||||||
|
const REFRESH_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes in milliseconds
|
||||||
|
const tokens = this.tokens ? this.tokens.split(',') : [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
|
const tokenName = tokens[i];
|
||||||
|
|
||||||
|
// Auto update Bearer authentication for the first token.
|
||||||
|
const expiresOn = new Date(global[tokenName + '_expiresOn']);
|
||||||
|
const expiration = expiresOn.getTime() - REFRESH_THRESHOLD_MS;
|
||||||
|
|
||||||
|
// Expires token 10min. before or if it the first time, load it.
|
||||||
|
if (expiration < Date.now() || firstTime) {
|
||||||
|
console.log('Expired. Refreshing token...' + expiration);
|
||||||
|
try {
|
||||||
|
const result = await sys.getCustomToken({ pid: this.pid, tokenName: tokenName });
|
||||||
|
global[tokenName] = result.token;
|
||||||
|
global[tokenName + '_expiresOn'] = result.expiresOn;
|
||||||
|
console.log('DONE:' + new Date(global[tokenName + '_expiresOn']));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to refresh token for ' + tokenName + ':', error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
headers['Authorization'] = 'Bearer ' + global[tokenName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const sleep = async ms => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const TOYAML = json => {
|
||||||
|
const doc = new YAML.Document();
|
||||||
|
doc.contents = json;
|
||||||
|
return doc.toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Line of Business logic.
|
||||||
|
|
||||||
|
let __reportMerge = { adds: 0, updates: 0, skipped: 0 };
|
||||||
|
let __report = () => {
|
||||||
|
return (
|
||||||
|
__reportMerge.title +
|
||||||
|
' adds: ' +
|
||||||
|
__reportMerge.adds +
|
||||||
|
', updates: ' +
|
||||||
|
__reportMerge.updates +
|
||||||
|
' and skipped: ' +
|
||||||
|
__reportMerge.skipped +
|
||||||
|
'.'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let REPORT = 'No report yet';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ensureTokens(true);
|
||||||
|
|
||||||
|
${code}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
reject({ message: e.message, name: e.name });
|
||||||
|
} finally {
|
||||||
|
// Closes handles if any.
|
||||||
|
|
||||||
|
await wa.closeHandles({ pid: pid });
|
||||||
|
await sys.closeHandles({ pid: pid });
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
})();
|
Loading…
Add table
Reference in a new issue