fix(all): TRUE multicloud.
This commit is contained in:
parent
d5e47de73e
commit
a98323dfd1
9 changed files with 437 additions and 430 deletions
|
@ -32,7 +32,7 @@
|
|||
|
||||
import { GBMinInstance, GBService, IGBCoreService, GBLog } from 'botlib';
|
||||
import * as Fs from 'fs';
|
||||
import * as ji from 'just-indent'
|
||||
import * as ji from 'just-indent';
|
||||
import { GBServer } from '../../../src/app.js';
|
||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
|
||||
import { CollectionUtil } from 'pragmatismo-io-framework';
|
||||
|
@ -52,8 +52,8 @@ import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
|
|||
import { GuaribasUser } from '../../security.gbapp/models/index.js';
|
||||
import { SystemKeywords } from './SystemKeywords.js';
|
||||
import { Sequelize, QueryTypes } from '@sequelize/core';
|
||||
import { z } from "zod";
|
||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
||||
import { z } from 'zod';
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema';
|
||||
|
||||
/**
|
||||
* @fileoverview Decision was to priorize security(isolation) and debugging,
|
||||
|
@ -68,7 +68,8 @@ export class GBVMService extends GBService {
|
|||
public static API_PORT = 1111;
|
||||
|
||||
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
||||
const files = await walkPromise(folder);
|
||||
const ignore = Path.join('work', DialogKeywords.getGBAIPath(min.botId, 'gbdialog'), 'node_modules');
|
||||
const files = await walkPromise(folder, { ignore: [ignore] });
|
||||
|
||||
await CollectionUtil.asyncForEach(files, async file => {
|
||||
if (!file) {
|
||||
|
@ -77,9 +78,7 @@ export class GBVMService extends GBService {
|
|||
|
||||
let filename: string = file.name;
|
||||
|
||||
if (filename.endsWith('.docx')) {
|
||||
filename = await this.loadDialog(filename, folder, min);
|
||||
}
|
||||
filename = await this.loadDialog(filename, folder, min);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -91,14 +90,11 @@ export class GBVMService extends GBService {
|
|||
|
||||
//check every key for being same
|
||||
return Object.keys(obj1).every(function (key) {
|
||||
|
||||
//if object
|
||||
if ((typeof obj1[key] == "object") && (typeof obj2[key] == "object")) {
|
||||
|
||||
if (typeof obj1[key] == 'object' && typeof obj2[key] == 'object') {
|
||||
//recursively check
|
||||
return GBVMService.compare(obj1[key], obj2[key]);
|
||||
} else {
|
||||
|
||||
//do the normal compare
|
||||
return obj1[key] === obj2[key];
|
||||
}
|
||||
|
@ -106,9 +102,22 @@ export class GBVMService extends GBService {
|
|||
}
|
||||
|
||||
public async loadDialog(filename: string, folder: string, min: GBMinInstance) {
|
||||
const isWord = filename.endsWith('.docx');
|
||||
if (
|
||||
!(
|
||||
isWord ||
|
||||
filename.endsWith('.vbs') ||
|
||||
filename.endsWith('.vb') ||
|
||||
filename.endsWith('.vba') ||
|
||||
filename.endsWith('.bas') ||
|
||||
filename.endsWith('.basic')
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wordFile = filename;
|
||||
const vbsFile = filename.substr(0, filename.indexOf('docx')) + 'vbs';
|
||||
const vbsFile = isWord ? filename.substr(0, filename.indexOf('docx')) + 'vbs' : filename;
|
||||
const fullVbsFile = urlJoin(folder, vbsFile);
|
||||
const docxStat = Fs.statSync(urlJoin(folder, wordFile));
|
||||
const interval = 3000; // If compiled is older 30 seconds, then recompile.
|
||||
|
@ -125,11 +134,10 @@ export class GBVMService extends GBService {
|
|||
// };
|
||||
|
||||
// let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
|
||||
|
||||
|
||||
// await client.api('/subscriptions')
|
||||
// .post(subscription);
|
||||
|
||||
|
||||
if (Fs.existsSync(fullVbsFile)) {
|
||||
const vbsStat = Fs.statSync(fullVbsFile);
|
||||
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
|
||||
|
@ -184,7 +192,6 @@ export class GBVMService extends GBService {
|
|||
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
|
||||
return filename;
|
||||
}
|
||||
|
||||
private processNodeModules(folder: string, min: GBMinInstance) {
|
||||
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
|
||||
if (!Fs.existsSync(node_modules)) {
|
||||
|
@ -215,7 +222,6 @@ export class GBVMService extends GBService {
|
|||
}
|
||||
|
||||
private syncStorageFromTABLE(folder: string, filename: string, min: GBMinInstance, mainName: string) {
|
||||
|
||||
const tablesFile = urlJoin(folder, `${filename}.tables.json`);
|
||||
let sync = false;
|
||||
|
||||
|
@ -225,7 +231,6 @@ export class GBVMService extends GBService {
|
|||
const tableDef = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')) as any;
|
||||
|
||||
const getTypeBasedOnCondition = (t, size) => {
|
||||
|
||||
if (1) {
|
||||
switch (t) {
|
||||
case 'string':
|
||||
|
@ -249,7 +254,6 @@ export class GBVMService extends GBService {
|
|||
default:
|
||||
return { type: 'TABLE', name: t };
|
||||
}
|
||||
|
||||
} else {
|
||||
switch (t) {
|
||||
case 'string':
|
||||
|
@ -273,7 +277,6 @@ export class GBVMService extends GBService {
|
|||
default:
|
||||
return { key: 'TABLE', name: t };
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -286,22 +289,17 @@ export class GBVMService extends GBService {
|
|||
if (Fs.existsSync(filePath)) {
|
||||
connections = JSON.parse(Fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
const shouldSync = min.core.getParam<boolean>(
|
||||
min.instance,
|
||||
'Synchronize Database',
|
||||
false
|
||||
);
|
||||
|
||||
tableDef.forEach(async (t) => {
|
||||
const shouldSync = min.core.getParam<boolean>(min.instance, 'Synchronize Database', false);
|
||||
|
||||
tableDef.forEach(async t => {
|
||||
const tableName = t.name.trim();
|
||||
|
||||
// Determines autorelationship.
|
||||
Object.keys(t.fields).forEach(key => {
|
||||
let obj = t.fields[key];
|
||||
obj.type = getTypeBasedOnCondition(obj.type, obj.size);
|
||||
if (obj.type.key === "TABLE") {
|
||||
obj.type.key = "BIGINT";
|
||||
if (obj.type.key === 'TABLE') {
|
||||
obj.type.key = 'BIGINT';
|
||||
associations.push({ from: tableName, to: obj.type.name });
|
||||
}
|
||||
});
|
||||
|
@ -320,11 +318,12 @@ export class GBVMService extends GBService {
|
|||
const username = con['storageUsername'];
|
||||
const password = con['storagePassword'];
|
||||
|
||||
const logging: boolean | Function = GBConfigService.get('STORAGE_LOGGING') === 'true'
|
||||
? (str: string): void => {
|
||||
GBLogEx.info(min, str);
|
||||
}
|
||||
: false;
|
||||
const logging: boolean | Function =
|
||||
GBConfigService.get('STORAGE_LOGGING') === 'true'
|
||||
? (str: string): void => {
|
||||
GBLogEx.info(min, str);
|
||||
}
|
||||
: false;
|
||||
|
||||
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
|
||||
const acquire = parseInt(GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT'));
|
||||
|
@ -362,9 +361,9 @@ export class GBVMService extends GBService {
|
|||
min[`llmconnection`] = {
|
||||
type: dialect,
|
||||
username,
|
||||
database: storageName, password
|
||||
database: storageName,
|
||||
password
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,11 +372,9 @@ export class GBVMService extends GBService {
|
|||
}
|
||||
|
||||
// Field checking, syncs if there is any difference.
|
||||
const seq = min[connectionName] ? min[connectionName]
|
||||
: minBoot.core.sequelize;
|
||||
const seq = min[connectionName] ? min[connectionName] : minBoot.core.sequelize;
|
||||
|
||||
if (seq) {
|
||||
|
||||
const model = seq.models[tableName];
|
||||
if (model) {
|
||||
// Except Id, checks if has same number of fields.
|
||||
|
@ -386,12 +383,11 @@ export class GBVMService extends GBService {
|
|||
let obj1 = t.fields[key];
|
||||
let obj2 = model['fieldRawAttributesMap'][key];
|
||||
|
||||
if (key !== "id") {
|
||||
if (key !== 'id') {
|
||||
if (obj1 && obj2) {
|
||||
equals++;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (equals != Object.keys(t.fields).length) {
|
||||
|
@ -401,23 +397,25 @@ export class GBVMService extends GBService {
|
|||
|
||||
seq.define(tableName, t.fields);
|
||||
|
||||
// New table checking, if needs sync.
|
||||
// New table checking, if needs sync.
|
||||
let tables;
|
||||
|
||||
if (con.storageDriver === 'mssql') {
|
||||
tables = await seq.query(`SELECT table_name, table_schema
|
||||
tables = await seq.query(
|
||||
`SELECT table_name, table_schema
|
||||
FROM information_schema.tables
|
||||
WHERE table_type = 'BASE TABLE'
|
||||
ORDER BY table_name ASC`, {
|
||||
type: QueryTypes.RAW
|
||||
})[0];
|
||||
}
|
||||
else if (con.storageDriver === 'mariadb') {
|
||||
ORDER BY table_name ASC`,
|
||||
{
|
||||
type: QueryTypes.RAW
|
||||
}
|
||||
)[0];
|
||||
} else if (con.storageDriver === 'mariadb') {
|
||||
tables = await seq.getQueryInterface().showAllTables();
|
||||
}
|
||||
|
||||
let found = false;
|
||||
tables.forEach((storageTable) => {
|
||||
tables.forEach(storageTable => {
|
||||
if (storageTable['table_name'] === tableName) {
|
||||
found = true;
|
||||
}
|
||||
|
@ -432,15 +430,17 @@ export class GBVMService extends GBService {
|
|||
try {
|
||||
to.hasMany(from);
|
||||
} catch (error) {
|
||||
throw new Error(`BASIC: Invalid relationship in ${mainName}: from ${e.from} to ${e.to} (${min.botId})... ${error.message}`);
|
||||
throw new Error(
|
||||
`BASIC: Invalid relationship in ${mainName}: from ${e.from} to ${e.to} (${min.botId})... ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
if (sync && shouldSync) {
|
||||
|
||||
GBLogEx.info(min, `BASIC: Syncing changes for TABLE ${connectionName} ${tableName} keyword (${min.botId})...`);
|
||||
GBLogEx.info(
|
||||
min,
|
||||
`BASIC: Syncing changes for TABLE ${connectionName} ${tableName} keyword (${min.botId})...`
|
||||
);
|
||||
|
||||
await seq.sync({
|
||||
alter: true,
|
||||
|
@ -454,7 +454,6 @@ 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');
|
||||
|
@ -467,17 +466,14 @@ export class GBVMService extends GBService {
|
|||
await s.deleteScheduleIfAny(min, mainName);
|
||||
|
||||
let i = 1;
|
||||
await CollectionUtil.asyncForEach(schedules, async (syntax) => {
|
||||
|
||||
await CollectionUtil.asyncForEach(schedules, async syntax => {
|
||||
if (s) {
|
||||
await s.createOrUpdateSchedule(min, syntax, `${mainName};${i++}`);
|
||||
await s.createOrUpdateSchedule(min, syntax, `${mainName};${i++}`);
|
||||
}
|
||||
});
|
||||
|
||||
basicCode = basicCode.replace(/^\s*SET SCHEDULE (.*)/gim, '');
|
||||
|
||||
|
||||
|
||||
// Process INCLUDE keyword to include another
|
||||
// dialog inside the dialog.
|
||||
|
||||
|
@ -718,7 +714,6 @@ export class GBVMService extends GBService {
|
|||
|
||||
Fs.writeFileSync(jsfile, code);
|
||||
GBLogEx.info(min, `[GBVMService] Finished loading of ${filename}, JavaScript from Word: \n ${code}`);
|
||||
|
||||
}
|
||||
|
||||
private async executeTasks(min, tasks) {
|
||||
|
@ -726,12 +721,10 @@ export class GBVMService extends GBService {
|
|||
const task = tasks[i];
|
||||
|
||||
if (task.kind === 'writeTableDefinition') {
|
||||
|
||||
// Creates an empty object that will receive Sequelize fields.
|
||||
|
||||
const tablesFile = `${task.file}.tables.json`;
|
||||
Fs.writeFileSync(tablesFile, JSON.stringify(task.tables));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -750,10 +743,10 @@ export class GBVMService extends GBService {
|
|||
lines.forEach(line => {
|
||||
if (line.trim()) {
|
||||
console.log(line);
|
||||
const keyword = /\s*SET SCHEDULE (.*)/gi;
|
||||
const keyword = /^\s*SET SCHEDULE (.*)/gi;
|
||||
let result: any = keyword.exec(line);
|
||||
if (result) {
|
||||
result = result[1].replace(/\`|\"|\'/, '')
|
||||
result = result[1].replace(/\`|\"|\'/, '');
|
||||
result = result.trim();
|
||||
results.push(result);
|
||||
}
|
||||
|
@ -762,7 +755,7 @@ export class GBVMService extends GBService {
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
private async getTextFromWord(folder: string, filename: string) {
|
||||
return new Promise<string>(async (resolve, reject) => {
|
||||
const path = urlJoin(folder, filename);
|
||||
|
@ -784,7 +777,6 @@ export class GBVMService extends GBService {
|
|||
}
|
||||
|
||||
public static normalizeQuotes(text: any) {
|
||||
|
||||
text = text.replace(/\"/gm, '`');
|
||||
text = text.replace(/\¨/gm, '`');
|
||||
text = text.replace(/\“/gm, '`');
|
||||
|
@ -798,27 +790,22 @@ export class GBVMService extends GBService {
|
|||
public static getMetadata(mainName: string, propertiesText, description) {
|
||||
let properties = {};
|
||||
if (!propertiesText || !description) {
|
||||
|
||||
return {}
|
||||
return {};
|
||||
}
|
||||
const getType = asClause => {
|
||||
|
||||
asClause = asClause.trim().toUpperCase();
|
||||
|
||||
if (asClause.indexOf('STRING') !== -1) {
|
||||
return 'string';
|
||||
}
|
||||
else if (asClause.indexOf('OBJECT') !== -1) {
|
||||
} else if (asClause.indexOf('OBJECT') !== -1) {
|
||||
return 'object';
|
||||
}
|
||||
else if (asClause.indexOf('INTEGER') !== -1 || asClause.indexOf('NUMBER') !== -1) {
|
||||
} else if (asClause.indexOf('INTEGER') !== -1 || asClause.indexOf('NUMBER') !== -1) {
|
||||
return 'number';
|
||||
} else {
|
||||
return 'enum';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (let i = 0; i < propertiesText.length; i++) {
|
||||
const propertiesExp = propertiesText[i];
|
||||
const t = getType(propertiesExp[2]);
|
||||
|
@ -841,21 +828,19 @@ export class GBVMService extends GBService {
|
|||
properties[propertiesExp[1].trim()] = element;
|
||||
}
|
||||
|
||||
|
||||
let json = {
|
||||
type: "function",
|
||||
type: 'function',
|
||||
function: {
|
||||
name: `${mainName}`,
|
||||
description: description ? description : '',
|
||||
parameters: zodToJsonSchema(z.object(properties))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public async parseField(line) {
|
||||
|
||||
let required = line.indexOf('*') !== -1;
|
||||
let unique = /\bunique\b/gi.test(line);
|
||||
let primaryKey = /\bkey\b/gi.test(line);
|
||||
|
@ -877,7 +862,8 @@ export class GBVMService extends GBService {
|
|||
|
||||
let definition = {
|
||||
allowNull: !required,
|
||||
unique: unique, primaryKey: primaryKey,
|
||||
unique: unique,
|
||||
primaryKey: primaryKey,
|
||||
autoIncrement: autoIncrement
|
||||
};
|
||||
definition['type'] = t;
|
||||
|
@ -896,7 +882,6 @@ export class GBVMService extends GBService {
|
|||
* @param code General Bots BASIC
|
||||
*/
|
||||
public async convert(filename: string, mainName: string, code: string) {
|
||||
|
||||
// Start and End of VB2TS tags of processing.
|
||||
|
||||
code = process.env.ENABLE_AUTH ? `hear GBLogExin as login\n${code}` : code;
|
||||
|
@ -917,7 +902,6 @@ export class GBVMService extends GBService {
|
|||
const outputLines = [];
|
||||
let emmitIndex = 1;
|
||||
for (let i = 1; i <= lines.length; i++) {
|
||||
|
||||
let line = lines[i - 1];
|
||||
|
||||
// Remove lines before statements.
|
||||
|
@ -968,12 +952,12 @@ export class GBVMService extends GBService {
|
|||
const endTableKeyword = /^\s*END TABLE\s*/gim;
|
||||
let endTableReg = endTableKeyword.exec(line);
|
||||
if (endTableReg && table) {
|
||||
|
||||
tables.push({
|
||||
name: table, fields: fields, connection: connection
|
||||
name: table,
|
||||
fields: fields,
|
||||
connection: connection
|
||||
});
|
||||
|
||||
|
||||
fields = {};
|
||||
table = null;
|
||||
connection = null;
|
||||
|
@ -1013,14 +997,14 @@ export class GBVMService extends GBService {
|
|||
const talkKeyword = /^\s*BEGIN TALK\s*/gim;
|
||||
let talkReg = talkKeyword.exec(line);
|
||||
if (talkReg && !talk) {
|
||||
talk = "await dk.talk ({pid: pid, text: `";
|
||||
talk = 'await dk.talk ({pid: pid, text: `';
|
||||
emmit = false;
|
||||
}
|
||||
|
||||
const systemPromptKeyword = /^\s*BEGIN SYSTEM PROMPT\s*/gim;
|
||||
let systemPromptReg = systemPromptKeyword.exec(line);
|
||||
if (systemPromptReg && !systemPrompt) {
|
||||
systemPrompt = "await sys.setSystemPrompt ({pid: pid, text: `";
|
||||
systemPrompt = 'await sys.setSystemPrompt ({pid: pid, text: `';
|
||||
emmit = false;
|
||||
}
|
||||
|
||||
|
@ -1038,9 +1022,10 @@ export class GBVMService extends GBService {
|
|||
|
||||
if (tables) {
|
||||
tasks.push({
|
||||
kind: 'writeTableDefinition', file: filename, tables
|
||||
kind: 'writeTableDefinition',
|
||||
file: filename,
|
||||
tables
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
code = `${outputLines.join('\n')}\n`;
|
||||
|
@ -1053,14 +1038,7 @@ export class GBVMService extends GBService {
|
|||
/**
|
||||
* Executes the converted JavaScript from BASIC code inside execution context.
|
||||
*/
|
||||
public static async callVM(
|
||||
text: string,
|
||||
min: GBMinInstance,
|
||||
step,
|
||||
pid,
|
||||
debug: boolean = false,
|
||||
params = []
|
||||
) {
|
||||
public static async callVM(text: string, min: GBMinInstance, step, pid, debug: boolean = false, params = []) {
|
||||
// Creates a class DialogKeywords which is the *this* pointer
|
||||
// in BASIC.
|
||||
|
||||
|
@ -1076,8 +1054,7 @@ export class GBVMService extends GBService {
|
|||
// These variables will be automatically be available as normal BASIC variables.
|
||||
|
||||
try {
|
||||
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken']
|
||||
(min.instance.instanceId, false);
|
||||
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, false);
|
||||
} catch (error) {
|
||||
variables['aadToken'] = 'ERROR: Configure /setupSecurity before using aadToken variable.';
|
||||
}
|
||||
|
@ -1118,12 +1095,10 @@ export class GBVMService extends GBService {
|
|||
let code = min.sandBoxMap[text];
|
||||
const channel = step ? step.context.activity.channelId : 'web';
|
||||
|
||||
|
||||
const dk = new DialogKeywords();
|
||||
const sys = new SystemKeywords();
|
||||
await dk.setFilter({ pid: pid, value: null });
|
||||
|
||||
|
||||
// Find all tokens in .gbot Config.
|
||||
|
||||
const strFind = ' Client ID';
|
||||
|
@ -1159,7 +1134,7 @@ export class GBVMService extends GBService {
|
|||
return await new Promise((resolve, reject) => {
|
||||
sandbox['resolve'] = resolve;
|
||||
// TODO: #411 sandbox['reject'] = reject;
|
||||
sandbox['reject'] = ()=>{};
|
||||
sandbox['reject'] = () => {};
|
||||
|
||||
const vm1 = new NodeVM({
|
||||
allowAsync: true,
|
||||
|
@ -1176,7 +1151,6 @@ export class GBVMService extends GBService {
|
|||
const s = new VMScript(code, { filename: scriptPath });
|
||||
result = vm1.run(s);
|
||||
});
|
||||
|
||||
})();
|
||||
} else {
|
||||
const runnerPath = urlJoin(
|
||||
|
@ -1207,10 +1181,15 @@ export class GBVMService extends GBService {
|
|||
} catch (error) {
|
||||
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any, executable: string, step = null) {
|
||||
public static createProcessInfo(
|
||||
user: GuaribasUser,
|
||||
min: GBMinInstance,
|
||||
channel: any,
|
||||
executable: string,
|
||||
step = null
|
||||
) {
|
||||
const pid = GBAdminService.getNumberIdentifier();
|
||||
GBServer.globals.processes[pid] = {
|
||||
pid: pid,
|
||||
|
|
|
@ -47,33 +47,30 @@ import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
|
|||
* Basic services for BASIC manipulation.
|
||||
*/
|
||||
export class ScheduleServices extends GBService {
|
||||
|
||||
public async deleteScheduleIfAny(min: GBMinInstance, name: string) {
|
||||
|
||||
let i = 1;
|
||||
while (i <= 10) {
|
||||
const task = min['scheduleMap'] ? min['scheduleMap'][name + i] : null;
|
||||
|
||||
if (task) {
|
||||
task.destroy();
|
||||
const id = `${name};${i}`;
|
||||
|
||||
delete min['scheduleMap'][id];
|
||||
const count = await GuaribasSchedule.destroy({
|
||||
where: {
|
||||
instanceId: min.instance.instanceId,
|
||||
name: id
|
||||
}
|
||||
});
|
||||
|
||||
if (count > 0) {
|
||||
GBLogEx.info(min, `BASIC: Removed ${name} SET SCHEDULE and ${count} rows from storage on: ${min.botId}...`);
|
||||
}
|
||||
}
|
||||
const id = `${name};${i}`;
|
||||
|
||||
delete min['scheduleMap'][id];
|
||||
const count = await GuaribasSchedule.destroy({
|
||||
where: {
|
||||
instanceId: min.instance.instanceId,
|
||||
name: id
|
||||
}
|
||||
});
|
||||
|
||||
if (count > 0) {
|
||||
GBLogEx.info(min, `BASIC: Removed ${name} SET SCHEDULE and ${count} rows from storage on: ${min.botId}...`);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,12 +110,10 @@ export class ScheduleServices extends GBService {
|
|||
let i = 0;
|
||||
let lastName = '';
|
||||
|
||||
await CollectionUtil.asyncForEach(schedules, async (item) => {
|
||||
|
||||
await CollectionUtil.asyncForEach(schedules, async item => {
|
||||
if (item.name === lastName) {
|
||||
item.name = item.name + ++i;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
|
@ -169,7 +164,6 @@ export class ScheduleServices extends GBService {
|
|||
},
|
||||
options
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
GBLogEx.error(min, `Running .gbdialog word ${item.name} : ${error}...`);
|
||||
}
|
||||
|
|
|
@ -79,8 +79,11 @@ export class GBConfigService {
|
|||
public static get(key: string): string | undefined {
|
||||
let value = GBConfigService.tryGet(key);
|
||||
|
||||
if (value === undefined) {
|
||||
if (!value) {
|
||||
switch (key) {
|
||||
case 'STORAGE_NAME':
|
||||
value = null;
|
||||
break;
|
||||
case 'CLOUD_USERNAME':
|
||||
value = undefined;
|
||||
break;
|
||||
|
|
|
@ -242,7 +242,7 @@ export class GBCoreService implements IGBCoreService {
|
|||
* Loads all items to start several listeners.
|
||||
*/
|
||||
public async loadInstances(): Promise<IGBInstance[]> {
|
||||
if (process.env.LOAD_ONLY !== undefined) {
|
||||
if (process.env.LOAD_ONLY) {
|
||||
const bots = process.env.LOAD_ONLY.split(`;`);
|
||||
const and = [];
|
||||
await CollectionUtil.asyncForEach(bots, async e => {
|
||||
|
@ -814,8 +814,6 @@ ENDPOINT_UPDATE=true
|
|||
mkdirp.sync(libraryPath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
await this.syncBotStorage(instances, 'default', deployer, libraryPath);
|
||||
|
||||
const files = Fs.readdirSync(libraryPath);
|
||||
|
|
|
@ -254,6 +254,11 @@ export class GBDeployer implements IGBDeployer {
|
|||
if (GBConfigService.get('STORAGE_NAME')) {
|
||||
await this.deployBotOnAzure(instance, GBServer.globals.publicAddress);
|
||||
}
|
||||
|
||||
// Makes available bot to the channels and .gbui interfaces.
|
||||
|
||||
await GBServer.globals.minService.mountBot(instance);
|
||||
|
||||
// Creates remaining objects on the cloud and updates instance information.
|
||||
|
||||
return instance;
|
||||
|
@ -310,11 +315,7 @@ export class GBDeployer implements IGBDeployer {
|
|||
subscriptionId
|
||||
);
|
||||
|
||||
// Makes available bot to the channels and .gbui interfaces.
|
||||
|
||||
await GBServer.globals.minService.mountBot(instance);
|
||||
await GBServer.globals.minService.ensureAPI();
|
||||
}
|
||||
}
|
||||
|
||||
// Saves final instance object and returns it.
|
||||
|
||||
|
|
|
@ -167,9 +167,6 @@ export class GBMinService {
|
|||
|
||||
let i = 1;
|
||||
|
||||
if (instances.length > 1) {
|
||||
}
|
||||
|
||||
await CollectionUtil.asyncForEach(
|
||||
instances,
|
||||
(async instance => {
|
||||
|
@ -184,12 +181,9 @@ export class GBMinService {
|
|||
GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`);
|
||||
}
|
||||
}).bind(this)
|
||||
|
||||
);
|
||||
|
||||
// Loads API.
|
||||
|
||||
await this.ensureAPI();
|
||||
|
||||
// Loads schedules.
|
||||
|
||||
GBLogEx.info(0, `Loading SET SCHEDULE entries...`);
|
||||
|
@ -199,6 +193,37 @@ export class GBMinService {
|
|||
GBLogEx.info(0, `All Bot instances loaded.`);
|
||||
}
|
||||
|
||||
public async startSimpleTest(min) {
|
||||
if (process.env.TEST_MESSAGE && min['isDefault']) {
|
||||
GBLogEx.info(min, `Starting auto test with '${process.env.TEST_MESSAGE}'.`);
|
||||
|
||||
const client = await GBUtil.getDirectLineClient(min);
|
||||
|
||||
const response = await client.apis.Conversations.Conversations_StartConversation();
|
||||
const conversationId = response.obj.conversationId;
|
||||
GBServer.globals.debugConversationId = conversationId;
|
||||
|
||||
const steps = process.env.TEST_MESSAGE.split(';');
|
||||
|
||||
await CollectionUtil.asyncForEach(steps, async (step) => {
|
||||
client.apis.Conversations.Conversations_PostActivity({
|
||||
conversationId: conversationId,
|
||||
activity: {
|
||||
textFormat: 'plain',
|
||||
text: step,
|
||||
type: 'message',
|
||||
from: {
|
||||
id: 'test',
|
||||
name: 'test'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await GBUtil.sleep(3000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes bot endpoint from web listeners and remove bot instance
|
||||
* from list of global server bot instances.
|
||||
|
@ -256,6 +281,8 @@ export class GBMinService {
|
|||
// https://github.com/GeneralBots/BotServer/issues/286
|
||||
// min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
|
||||
|
||||
min['isDefault'] = GBServer.globals.minInstances.length === 0;
|
||||
|
||||
GBServer.globals.minInstances.push(min);
|
||||
const user = null; // No user context.
|
||||
|
||||
|
@ -330,9 +357,6 @@ export class GBMinService {
|
|||
GBLogEx.info(1, `WebDav for ${botId} loaded.`);
|
||||
});
|
||||
}
|
||||
// Loads Named Entity data for this bot.
|
||||
|
||||
// TODO: await KBService.RefreshNER(min);
|
||||
|
||||
// Calls the loadBot context.activity for all packages.
|
||||
|
||||
|
@ -356,34 +380,6 @@ export class GBMinService {
|
|||
});
|
||||
GBLog.verbose(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
|
||||
|
||||
// Test code.
|
||||
if (process.env.TEST_MESSAGE) {
|
||||
GBLogEx.info(min, `Starting auto test with '${process.env.TEST_MESSAGE}'.`);
|
||||
|
||||
const client = await GBUtil.getDirectLineClient(min);
|
||||
|
||||
const response = await client.apis.Conversations.Conversations_StartConversation();
|
||||
const conversationId = response.obj.conversationId;
|
||||
GBServer.globals.debugConversationId = conversationId;
|
||||
|
||||
const steps = process.env.TEST_MESSAGE.split(';');
|
||||
|
||||
await CollectionUtil.asyncForEach(steps, async step => {
|
||||
client.apis.Conversations.Conversations_PostActivity({
|
||||
conversationId: conversationId,
|
||||
activity: {
|
||||
textFormat: 'plain',
|
||||
text: step,
|
||||
type: 'message',
|
||||
from: {
|
||||
id: 'test',
|
||||
name: 'test'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await GBUtil.sleep(3000);
|
||||
});
|
||||
|
||||
// Generates MS Teams manifest.
|
||||
|
||||
|
@ -394,7 +390,6 @@ export class GBMinService {
|
|||
const data = await this.deployer.getBotManifest(instance);
|
||||
Fs.writeFileSync(packageTeams, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Serves individual URL for each bot user interface.
|
||||
|
||||
|
@ -463,6 +458,11 @@ export class GBMinService {
|
|||
.bind(min);
|
||||
|
||||
GBDeployer.mountGBKBAssets(`${botId}.gbkb`, botId, `${botId}.gbkb`);
|
||||
|
||||
// Loads API.
|
||||
|
||||
await this.ensureAPI();
|
||||
|
||||
}
|
||||
|
||||
public static getProviderName(req: any, res: any) {
|
||||
|
@ -839,6 +839,8 @@ export class GBMinService {
|
|||
|
||||
let url = `/api/messages/${instance.botId}`;
|
||||
GBServer.globals.server.post(url, receiver);
|
||||
url = `/api/messages`;
|
||||
GBServer.globals.server.post(url, receiver);
|
||||
|
||||
// NLP Manager.
|
||||
|
||||
|
@ -849,9 +851,6 @@ export class GBMinService {
|
|||
GBServer.globals.minBoot = min;
|
||||
GBServer.globals.minBoot.instance.marketplaceId = GBConfigService.get('MARKETPLACE_ID');
|
||||
GBServer.globals.minBoot.instance.marketplacePassword = GBConfigService.get('MARKETPLACE_SECRET');
|
||||
} else {
|
||||
url = `/api/messages`;
|
||||
GBServer.globals.server.post(url, receiver);
|
||||
}
|
||||
|
||||
if (min.instance.facebookWorkplaceVerifyToken) {
|
||||
|
|
|
@ -12,197 +12,214 @@ const conversationsCleanupInterval = 10000;
|
|||
const conversations: { [key: string]: IConversation } = {};
|
||||
const botDataStore: { [key: string]: IBotData } = {};
|
||||
|
||||
export const getRouter = (serviceUrl: string, botUrl: string, conversationInitRequired = true, botId): express.Router => {
|
||||
const router = express.Router();
|
||||
export const getRouter = (
|
||||
serviceUrl: string,
|
||||
botUrl: string,
|
||||
conversationInitRequired = true,
|
||||
botId
|
||||
): express.Router => {
|
||||
const router = express.Router();
|
||||
|
||||
router.use(bodyParser.json()); // for parsing application/json
|
||||
router.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
|
||||
router.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, PATCH, OPTIONS');
|
||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, x-ms-bot-agent');
|
||||
next();
|
||||
});
|
||||
router.use(bodyParser.json()); // for parsing application/json
|
||||
router.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
|
||||
router.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, PATCH, OPTIONS');
|
||||
res.header(
|
||||
'Access-Control-Allow-Headers',
|
||||
'Origin, X-Requested-With, Content-Type, Accept, Authorization, x-ms-bot-agent'
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
// CLIENT ENDPOINT
|
||||
router.options(`/directline/${botId}/`, (req, res) => {
|
||||
res.status(200).end();
|
||||
});
|
||||
// CLIENT ENDPOINT
|
||||
router.options(`/directline/${botId}/`, (req, res) => {
|
||||
res.status(200).end();
|
||||
});
|
||||
|
||||
// Creates a conversation
|
||||
const reqs = (req, res) => {
|
||||
|
||||
const conversationId: string = uuidv4.v4().toString();
|
||||
conversations[conversationId] = {
|
||||
conversationId,
|
||||
history: [],
|
||||
};
|
||||
console.log('Created conversation with conversationId: ' + conversationId);
|
||||
|
||||
const activity = createConversationUpdateActivity(serviceUrl, conversationId);
|
||||
fetch(botUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(activity),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then((response) => {
|
||||
res.status(response.status).send({
|
||||
conversationId,
|
||||
expiresIn,
|
||||
});
|
||||
});
|
||||
// Creates a conversation
|
||||
const reqs = (req, res) => {
|
||||
const conversationId: string = uuidv4.v4().toString();
|
||||
conversations[conversationId] = {
|
||||
conversationId,
|
||||
history: []
|
||||
};
|
||||
console.log('Created conversation with conversationId: ' + conversationId);
|
||||
|
||||
router.post('/v3/directline/conversations',reqs );
|
||||
router.post(`/directline/${botId}/conversations`,reqs );
|
||||
router.post(`/directline/conversations`,reqs );
|
||||
|
||||
// Reconnect API
|
||||
router.get('/v3/directline/conversations/:conversationId', (req, res) => {
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
if (conversation) {
|
||||
res.status(200).send(conversation);
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
const activity = createConversationUpdateActivity(serviceUrl, conversationId);
|
||||
fetch(botUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(activity),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(response => {
|
||||
res.status(response.status).send({
|
||||
conversationId,
|
||||
expiresIn
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
router.post('/v3/directline/conversations', reqs);
|
||||
router.post(`/directline/${botId}/conversations`, reqs);
|
||||
router.post(`/directline/conversations`, reqs);
|
||||
|
||||
// Reconnect API
|
||||
router.get('/v3/directline/conversations/:conversationId', (req, res) => {
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
if (conversation) {
|
||||
res.status(200).send(conversation);
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
|
||||
console.warn('/v3/directline/conversations/:conversationId not implemented');
|
||||
});
|
||||
|
||||
// Gets activities from store (local history array for now)
|
||||
router.get(`/directline/${botId}/conversations/:conversationId/activities`, (req, res) => {
|
||||
const watermark = req.query.watermark && req.query.watermark !== 'null' ? Number(req.query.watermark) : 0;
|
||||
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
|
||||
if (conversation) {
|
||||
// If the bot has pushed anything into the history array
|
||||
if (conversation.history.length > watermark) {
|
||||
const activities = conversation.history.slice(watermark);
|
||||
res.status(200).json({
|
||||
activities,
|
||||
watermark: watermark + activities.length
|
||||
});
|
||||
} else {
|
||||
res.status(200).send({
|
||||
activities: [],
|
||||
watermark
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
// Sends message to bot. Assumes message activities
|
||||
router.post(`/directline/${botId}/conversations/:conversationId/activities`, (req, res) => {
|
||||
const incomingActivity = req.body;
|
||||
// Make copy of activity. Add required fields
|
||||
const activity = createMessageActivity(incomingActivity, serviceUrl, req.params.conversationId);
|
||||
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
|
||||
if (conversation) {
|
||||
conversation.history.push(activity);
|
||||
fetch(botUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(activity),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
console.warn('/v3/directline/conversations/:conversationId not implemented');
|
||||
});
|
||||
}).then(response => {
|
||||
res.status(response.status).json({ id: activity.id });
|
||||
});
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
// Gets activities from store (local history array for now)
|
||||
router.get(`/directline/${botId}/conversations/:conversationId/activities`, (req, res) => {
|
||||
const watermark = req.query.watermark && req.query.watermark !== 'null' ? Number(req.query.watermark) : 0;
|
||||
router.post('/v3/directline/conversations/:conversationId/upload', (req, res) => {
|
||||
console.warn('/v3/directline/conversations/:conversationId/upload not implemented');
|
||||
});
|
||||
router.get('/v3/directline/conversations/:conversationId/stream', (req, res) => {
|
||||
console.warn('/v3/directline/conversations/:conversationId/stream not implemented');
|
||||
});
|
||||
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
// BOT CONVERSATION ENDPOINT
|
||||
|
||||
if (conversation) {
|
||||
// If the bot has pushed anything into the history array
|
||||
if (conversation.history.length > watermark) {
|
||||
const activities = conversation.history.slice(watermark);
|
||||
res.status(200).json({
|
||||
activities,
|
||||
watermark: watermark + activities.length,
|
||||
});
|
||||
} else {
|
||||
res.status(200).send({
|
||||
activities: [],
|
||||
watermark,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
router.post('/v3/conversations', (req, res) => {
|
||||
console.warn('/v3/conversations not implemented');
|
||||
});
|
||||
|
||||
// Sends message to bot. Assumes message activities
|
||||
router.post(`/directline/${botId}/conversations/:conversationId/activities`, (req, res) => {
|
||||
const incomingActivity = req.body;
|
||||
// Make copy of activity. Add required fields
|
||||
const activity = createMessageActivity(incomingActivity, serviceUrl, req.params.conversationId);
|
||||
router.post('/v3/conversations/:conversationId/activities', (req, res) => {
|
||||
let activity: IActivity;
|
||||
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
activity = req.body;
|
||||
activity.id = uuidv4.v4();
|
||||
activity.from = { id: 'id', name: 'Bot' };
|
||||
|
||||
if (conversation) {
|
||||
conversation.history.push(activity);
|
||||
fetch(botUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(activity),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then((response) => {
|
||||
res.status(response.status).json({ id: activity.id });
|
||||
});
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
if (conversation) {
|
||||
conversation.history.push(activity);
|
||||
res.status(200).send();
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/v3/directline/conversations/:conversationId/upload', (req, res) => { console.warn('/v3/directline/conversations/:conversationId/upload not implemented'); });
|
||||
router.get('/v3/directline/conversations/:conversationId/stream', (req, res) => { console.warn('/v3/directline/conversations/:conversationId/stream not implemented'); });
|
||||
router.post('/v3/conversations/:conversationId/activities/:activityId', (req, res) => {
|
||||
let activity: IActivity;
|
||||
|
||||
// BOT CONVERSATION ENDPOINT
|
||||
activity = req.body;
|
||||
activity.id = uuidv4.v4();
|
||||
activity.from = { id: 'id', name: 'Bot' };
|
||||
|
||||
router.post('/v3/conversations', (req, res) => { console.warn('/v3/conversations not implemented'); });
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
if (conversation) {
|
||||
conversation.history.push(activity);
|
||||
res.status(200).send();
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/v3/conversations/:conversationId/activities', (req, res) => {
|
||||
let activity: IActivity;
|
||||
router.get('/v3/conversations/:conversationId/members', (req, res) => {
|
||||
console.warn('/v3/conversations/:conversationId/members not implemented');
|
||||
});
|
||||
router.get('/v3/conversations/:conversationId/activities/:activityId/members', (req, res) => {
|
||||
console.warn('/v3/conversations/:conversationId/activities/:activityId/members');
|
||||
});
|
||||
|
||||
activity = req.body;
|
||||
activity.id = uuidv4.v4();
|
||||
activity.from = { id: 'id', name: 'Bot' };
|
||||
// BOTSTATE ENDPOINT
|
||||
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
if (conversation) {
|
||||
conversation.history.push(activity);
|
||||
res.status(200).send();
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
router.get('/v3/botstate/:channelId/users/:userId', (req, res) => {
|
||||
console.log('Called GET user data');
|
||||
getBotData(req, res);
|
||||
});
|
||||
|
||||
router.post('/v3/conversations/:conversationId/activities/:activityId', (req, res) => {
|
||||
let activity: IActivity;
|
||||
router.get('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
|
||||
console.log('Called GET conversation data');
|
||||
getBotData(req, res);
|
||||
});
|
||||
|
||||
activity = req.body;
|
||||
activity.id = uuidv4.v4();
|
||||
activity.from = { id: 'id', name: 'Bot' };
|
||||
router.get('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
|
||||
console.log('Called GET private conversation data');
|
||||
getBotData(req, res);
|
||||
});
|
||||
|
||||
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
|
||||
if (conversation) {
|
||||
conversation.history.push(activity);
|
||||
res.status(200).send();
|
||||
} else {
|
||||
// Conversation was never initialized
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
router.post('/v3/botstate/:channelId/users/:userId', (req, res) => {
|
||||
console.log('Called POST setUserData');
|
||||
setUserData(req, res);
|
||||
});
|
||||
|
||||
router.get('/v3/conversations/:conversationId/members', (req, res) => { console.warn('/v3/conversations/:conversationId/members not implemented'); });
|
||||
router.get('/v3/conversations/:conversationId/activities/:activityId/members', (req, res) => { console.warn('/v3/conversations/:conversationId/activities/:activityId/members'); });
|
||||
router.post('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
|
||||
console.log('Called POST setConversationData');
|
||||
setConversationData(req, res);
|
||||
});
|
||||
|
||||
// BOTSTATE ENDPOINT
|
||||
router.post('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
|
||||
setPrivateConversationData(req, res);
|
||||
});
|
||||
|
||||
router.get('/v3/botstate/:channelId/users/:userId', (req, res) => {
|
||||
console.log('Called GET user data');
|
||||
getBotData(req, res);
|
||||
});
|
||||
router.delete('/v3/botstate/:channelId/users/:userId', (req, res) => {
|
||||
console.log('Called DELETE deleteStateForUser');
|
||||
deleteStateForUser(req, res);
|
||||
});
|
||||
|
||||
router.get('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
|
||||
console.log(('Called GET conversation data'));
|
||||
getBotData(req, res);
|
||||
});
|
||||
|
||||
router.get('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
|
||||
console.log('Called GET private conversation data');
|
||||
getBotData(req, res);
|
||||
});
|
||||
|
||||
router.post('/v3/botstate/:channelId/users/:userId', (req, res) => {
|
||||
console.log('Called POST setUserData');
|
||||
setUserData(req, res);
|
||||
});
|
||||
|
||||
router.post('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
|
||||
console.log('Called POST setConversationData');
|
||||
setConversationData(req, res);
|
||||
});
|
||||
|
||||
router.post('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
|
||||
setPrivateConversationData(req, res);
|
||||
});
|
||||
|
||||
router.delete('/v3/botstate/:channelId/users/:userId', (req, res) => {
|
||||
console.log('Called DELETE deleteStateForUser');
|
||||
deleteStateForUser(req, res);
|
||||
});
|
||||
|
||||
return router;
|
||||
return router;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -212,114 +229,132 @@ export const getRouter = (serviceUrl: string, botUrl: string, conversationInitRe
|
|||
* @param conversationInitRequired Requires that a conversation is initialized before it is accessed, returning a 400
|
||||
* when not the case. If set to false, a new conversation reference is created on the fly. This is true by default.
|
||||
*/
|
||||
export const initializeRoutes = (app: express.Express, port: number, botUrl: string, conversationInitRequired = true, botId) => {
|
||||
conversationsCleanup();
|
||||
export const initializeRoutes = (
|
||||
app: express.Express,
|
||||
port: number,
|
||||
botUrl: string,
|
||||
conversationInitRequired = true,
|
||||
botId
|
||||
) => {
|
||||
conversationsCleanup();
|
||||
|
||||
const directLineEndpoint = `http://127.0.0.1:${port}`;
|
||||
const router = getRouter(directLineEndpoint, botUrl, conversationInitRequired, botId);
|
||||
|
||||
app.use(router);
|
||||
console.log(`Routing messages to bot on ${botUrl}`);
|
||||
const directLineEndpoint = `http://127.0.0.1:${port}`;
|
||||
const router = getRouter(directLineEndpoint, botUrl, conversationInitRequired, botId);
|
||||
|
||||
app.use(router);
|
||||
};
|
||||
|
||||
const getConversation = (conversationId: string, conversationInitRequired: boolean) => {
|
||||
|
||||
// Create conversation on the fly when needed and init not required
|
||||
if (!conversations[conversationId] && !conversationInitRequired) {
|
||||
conversations[conversationId] = {
|
||||
conversationId,
|
||||
history: [],
|
||||
};
|
||||
}
|
||||
return conversations[conversationId];
|
||||
// Create conversation on the fly when needed and init not required
|
||||
if (!conversations[conversationId] && !conversationInitRequired) {
|
||||
conversations[conversationId] = {
|
||||
conversationId,
|
||||
history: []
|
||||
};
|
||||
}
|
||||
return conversations[conversationId];
|
||||
};
|
||||
|
||||
const getBotDataKey = (channelId: string, conversationId: string, userId: string) => {
|
||||
return `$${channelId || '*'}!${conversationId || '*'}!${userId || '*'}`;
|
||||
return `$${channelId || '*'}!${conversationId || '*'}!${userId || '*'}`;
|
||||
};
|
||||
|
||||
const setBotData = (channelId: string, conversationId: string, userId: string, incomingData: IBotData): IBotData => {
|
||||
const key = getBotDataKey(channelId, conversationId, userId);
|
||||
const newData: IBotData = {
|
||||
eTag: new Date().getTime().toString(),
|
||||
data: incomingData.data,
|
||||
};
|
||||
const key = getBotDataKey(channelId, conversationId, userId);
|
||||
const newData: IBotData = {
|
||||
eTag: new Date().getTime().toString(),
|
||||
data: incomingData.data
|
||||
};
|
||||
|
||||
if (incomingData) {
|
||||
botDataStore[key] = newData;
|
||||
} else {
|
||||
delete botDataStore[key];
|
||||
newData.eTag = '*';
|
||||
}
|
||||
if (incomingData) {
|
||||
botDataStore[key] = newData;
|
||||
} else {
|
||||
delete botDataStore[key];
|
||||
newData.eTag = '*';
|
||||
}
|
||||
|
||||
return newData;
|
||||
return newData;
|
||||
};
|
||||
|
||||
const getBotData = (req: express.Request, res: express.Response) => {
|
||||
const key = getBotDataKey(req.params.channelId, req.params.conversationId, req.params.userId);
|
||||
console.log('Data key: ' + key);
|
||||
const key = getBotDataKey(req.params.channelId, req.params.conversationId, req.params.userId);
|
||||
console.log('Data key: ' + key);
|
||||
|
||||
res.status(200).send(botDataStore[key] || { data: null, eTag: '*' });
|
||||
res.status(200).send(botDataStore[key] || { data: null, eTag: '*' });
|
||||
};
|
||||
|
||||
const setUserData = (req: express.Request, res: express.Response) => {
|
||||
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
|
||||
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
|
||||
};
|
||||
|
||||
const setConversationData = (req: express.Request, res: express.Response) => {
|
||||
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
|
||||
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
|
||||
};
|
||||
|
||||
const setPrivateConversationData = (req: express.Request, res: express.Response) => {
|
||||
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
|
||||
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
|
||||
};
|
||||
|
||||
export const start = (server, botId)=>{
|
||||
const port = GBConfigService.getServerPort();
|
||||
initializeRoutes(server, Number(port), `http://127.0.0.1:${port}/api/messages/${botId}`, null, botId);
|
||||
}
|
||||
export const start = (server, botId) => {
|
||||
const port = GBConfigService.getServerPort();
|
||||
initializeRoutes(server, Number(port), `http://127.0.0.1:${port}/api/messages/${botId}`, null, botId);
|
||||
|
||||
if (botId === 'default') {
|
||||
initializeRoutes(server, Number(port), `http://127.0.0.1:${port}/api/messages`, null, botId);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteStateForUser = (req: express.Request, res: express.Response) => {
|
||||
Object.keys(botDataStore)
|
||||
.forEach((key) => {
|
||||
if (key.endsWith(`!{req.query.userId}`)) {
|
||||
delete botDataStore[key];
|
||||
}
|
||||
});
|
||||
res.status(200).send();
|
||||
Object.keys(botDataStore).forEach(key => {
|
||||
if (key.endsWith(`!{req.query.userId}`)) {
|
||||
delete botDataStore[key];
|
||||
}
|
||||
});
|
||||
res.status(200).send();
|
||||
};
|
||||
|
||||
// CLIENT ENDPOINT HELPERS
|
||||
const createMessageActivity = (incomingActivity: IMessageActivity, serviceUrl: string, conversationId: string): IMessageActivity => {
|
||||
return { ...incomingActivity, channelId: 'emulator', serviceUrl, conversation: { id: conversationId }, id: uuidv4.v4() };
|
||||
const createMessageActivity = (
|
||||
incomingActivity: IMessageActivity,
|
||||
serviceUrl: string,
|
||||
conversationId: string
|
||||
): IMessageActivity => {
|
||||
return {
|
||||
...incomingActivity,
|
||||
channelId: 'emulator',
|
||||
serviceUrl,
|
||||
conversation: { id: conversationId },
|
||||
id: uuidv4.v4()
|
||||
};
|
||||
};
|
||||
|
||||
const createConversationUpdateActivity = (serviceUrl: string, conversationId: string): IConversationUpdateActivity => {
|
||||
const activity: IConversationUpdateActivity = {
|
||||
type: 'conversationUpdate',
|
||||
channelId: 'emulator',
|
||||
serviceUrl,
|
||||
conversation: { id: conversationId },
|
||||
id: uuidv4.v4(),
|
||||
membersAdded: [],
|
||||
membersRemoved: [],
|
||||
from: { id: 'offline-directline', name: 'Offline Directline Server' },
|
||||
};
|
||||
return activity;
|
||||
const activity: IConversationUpdateActivity = {
|
||||
type: 'conversationUpdate',
|
||||
channelId: 'emulator',
|
||||
serviceUrl,
|
||||
conversation: { id: conversationId },
|
||||
id: uuidv4.v4(),
|
||||
membersAdded: [],
|
||||
membersRemoved: [],
|
||||
from: { id: 'offline-directline', name: 'Offline Directline Server' }
|
||||
};
|
||||
return activity;
|
||||
};
|
||||
|
||||
const conversationsCleanup = () => {
|
||||
setInterval(() => {
|
||||
const expiresTime = moment().subtract(expiresIn, 'seconds');
|
||||
Object.keys(conversations).forEach((conversationId) => {
|
||||
if (conversations[conversationId].history.length > 0) {
|
||||
const lastTime = moment(conversations[conversationId].history[conversations[conversationId].history.length - 1].localTimestamp);
|
||||
if (lastTime < expiresTime) {
|
||||
delete conversations[conversationId];
|
||||
console.log('deleted cId: ' + conversationId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, conversationsCleanupInterval);
|
||||
setInterval(() => {
|
||||
const expiresTime = moment().subtract(expiresIn, 'seconds');
|
||||
Object.keys(conversations).forEach(conversationId => {
|
||||
if (conversations[conversationId].history.length > 0) {
|
||||
const lastTime = moment(
|
||||
conversations[conversationId].history[conversations[conversationId].history.length - 1].localTimestamp
|
||||
);
|
||||
if (lastTime < expiresTime) {
|
||||
delete conversations[conversationId];
|
||||
console.log('deleted cId: ' + conversationId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, conversationsCleanupInterval);
|
||||
};
|
||||
|
|
26
src/app.ts
26
src/app.ts
|
@ -122,10 +122,7 @@ export class GBServer {
|
|||
});
|
||||
|
||||
process.on('uncaughtException', (err, p) => {
|
||||
GBLogEx.error(
|
||||
0,
|
||||
`GBEXCEPTION: ${GBUtil.toYAML(err)}`
|
||||
);
|
||||
GBLogEx.error(0, `GBEXCEPTION: ${GBUtil.toYAML(err)}`);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (err, p) => {
|
||||
|
@ -138,10 +135,7 @@ export class GBServer {
|
|||
}
|
||||
|
||||
if (!bypass) {
|
||||
GBLogEx.error(
|
||||
0,
|
||||
`GBREJECTION: ${GBUtil.toYAML(err)}`
|
||||
);
|
||||
GBLogEx.error(0, `GBREJECTION: ${GBUtil.toYAML(err)}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -186,7 +180,6 @@ export class GBServer {
|
|||
|
||||
// Creates a boot instance or load it from storage.
|
||||
|
||||
|
||||
if (GBConfigService.get('STORAGE_SERVER')) {
|
||||
azureDeployer = await AzureDeployerService.createInstance(deployer);
|
||||
await core.initStorage();
|
||||
|
@ -245,17 +238,20 @@ export class GBServer {
|
|||
}
|
||||
}
|
||||
|
||||
if (!GBConfigService.get('STORAGE_NAME')) {
|
||||
const conversationalService: GBConversationalService = new GBConversationalService(core);
|
||||
const adminService: GBAdminService = new GBAdminService(core);
|
||||
const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer);
|
||||
GBServer.globals.minService = minService;
|
||||
|
||||
// Just sync if not using LOAD_ONLY.
|
||||
|
||||
if (!GBConfigService.get('STORAGE_NAME') && !process.env.LOAD_ONLY) {
|
||||
await core['ensureFolders'](instances, deployer);
|
||||
}
|
||||
GBServer.globals.bootInstance = instances[0];
|
||||
|
||||
// Builds minimal service infrastructure.
|
||||
|
||||
const conversationalService: GBConversationalService = new GBConversationalService(core);
|
||||
const adminService: GBAdminService = new GBAdminService(core);
|
||||
const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer);
|
||||
GBServer.globals.minService = minService;
|
||||
await minService.buildMin(instances);
|
||||
|
||||
server.all('*', async (req, res, next) => {
|
||||
|
@ -291,6 +287,8 @@ export class GBServer {
|
|||
|
||||
GBLogEx.info(0, `The Bot Server is in RUNNING mode...`);
|
||||
|
||||
await minService.startSimpleTest(GBServer.globals.minBoot);
|
||||
|
||||
// Opens Navigator.
|
||||
|
||||
if (process.env.DEV_OPEN_BROWSER) {
|
||||
|
|
|
@ -76,8 +76,8 @@ export class GBUtil {
|
|||
}
|
||||
};
|
||||
if (!GBConfigService.get('STORAGE_NAME')) {
|
||||
config['spec'].url = `http://127.0.0.1:${GBConfigService.getServerPort()}/api/messages`,
|
||||
config['spec'].servers = [{ url: `http://127.0.0.1:${GBConfigService.getServerPort()}/api/messages` }];
|
||||
config['spec'].url = `http://127.0.0.1:${GBConfigService.getServerPort()}/api/messages/${min.botId}`,
|
||||
config['spec'].servers = [{ url: `http://127.0.0.1:${GBConfigService.getServerPort()}/api/messages/${min.botId}` }];
|
||||
config['spec'].openapi = '3.0.0';
|
||||
delete config['spec'].host;
|
||||
delete config['spec'].swagger;
|
||||
|
|
Loading…
Add table
Reference in a new issue