This commit is contained in:
Rico Olivetti 2023-09-29 19:04:38 -03:00
commit edd0fe9fea
19 changed files with 624 additions and 316 deletions

View file

@ -97,7 +97,7 @@
"botlib": "3.0.11",
"c3-chart-maker": "0.2.8",
"cd": "0.3.3",
"chatgpt": "2.4.2",
"chatgpt": "^2.4.2",
"chrome-remote-interface": "0.31.3",
"cli-progress": "3.11.2",
"cli-spinner": "0.2.10",
@ -128,7 +128,7 @@
"moment": "1.3.0",
"ms-rest-azure": "3.0.0",
"nexmo": "2.9.1",
"ngrok": "4.3.3",
"ngrok": "5.0.0-beta.2",
"node-cron": "3.0.2",
"node-html-parser": "6.1.5",
"node-nlp": "4.26.1",
@ -136,7 +136,7 @@
"npm": "9.6.1",
"open": "8.4.0",
"open-docxtemplater-image-module": "1.0.3",
"openai": "3.3.0",
"openai": "4.6.0",
"pdf-extraction": "1.0.2",
"pdf-to-png-converter": "3.1.0",
"pdfkit": "0.13.0",
@ -180,7 +180,7 @@
"vm2-process": "2.1.1",
"walk-promise": "0.2.0",
"washyourmouthoutwithsoap": "1.0.2",
"whatsapp-web.js": "git://github.com/pedroslopez/whatsapp-web.js#abac063b779570729476cf42e29dc694e5345ca6",
"whatsapp-web.js": "git://github.com/pedroslopez/whatsapp-web.js#b671b0c708f0bc6187ccec078a0f3e9c08db4bce",
"winston": "3.8.2",
"winston-logs-display": "1.0.0",
"ws": "8.12.1",

View file

@ -421,7 +421,7 @@ export class AdminDialog extends IGBDialog {
min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
const redirectUri = urlJoin(min.instance.botEndpoint, min.instance.botId, '/token');
const redirectUri = urlJoin(process.env.BOT_URL, min.instance.botId, '/token');
const url = `https://login.microsoftonline.com/${step.activeDialog.state.authenticatorTenant}/oauth2/authorize?client_id=${min.instance.marketplaceId}&response_type=code&redirect_uri=${redirectUri}&scope=https://graph.microsoft.com/.default&state=${state}&response_mode=query`;
await min.conversationalService.sendText(min, step, Messages[locale].consent(url));

View file

@ -170,6 +170,11 @@ export class GBAdminService implements IGBAdminService {
// .gbot packages are handled using storage API, so no download
// of local resources is required.
const gbai = DialogKeywords.getGBAIPath(min.instance.botId);
if (packageType === 'gbkb') {
await deployer['cleanupPackage'](min.instance, packageName);
}
await deployer['downloadFolder'](min,
Path.join('work', `${gbai}`),
Path.basename(localFolder));
@ -178,9 +183,10 @@ export class GBAdminService implements IGBAdminService {
}
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
const service = await AzureDeployerService.createInstance(deployer);
const searchIndex = min.instance.searchIndex ? min.instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
await deployer.rebuildIndex(
min.instance,
service.getKBSearchSchema(min.instance.searchIndex)
service.getKBSearchSchema(searchIndex)
);
}

View file

@ -266,6 +266,11 @@ export class AzureDeployerService implements IGBInstallationDeployer {
}
public async updateBotProxy(botId: string, group: string, endpoint: string) {
if (!await this.botExists(botId)) {
GBLog.error(`Bot ${botId} does not exist on cloud.`);
return;
}
const baseUrl = `https://management.azure.com/`;
const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD');
@ -455,7 +460,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
};
GBLog.info(`Deploying Bot...`);
instance.botEndpoint = this.defaultEndPoint;
instance.botEndpoint = 'TODO: remove this column.';
instance = await this.internalDeployBot(
instance,
@ -565,9 +570,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
luisAppIds: [nlpAppId],
luisKey: nlpKey,
msaAppId: appId,
msaAppPassword: appPassword,
enabledChannels: ['webchat', 'skype'], //, "facebook"],
configuredChannels: ['webchat', 'skype'] //, "facebook"]
msaAppPassword: appPassword
}
};

View file

@ -60,7 +60,7 @@ export function createKoaHttpServer(
app.use(koaBody.koaBody({ multipart: true }));
app.use(middleware);
const server = app.listen(port);
const SERVER_TIMEOUT = 60 * 60 * 24 * 1000; // Equals to client RPC set .
const SERVER_TIMEOUT = 60 * 60 * 24 * 1000; // Equals to client RPC set.
server.timeout = SERVER_TIMEOUT;
return {

View file

@ -1230,7 +1230,9 @@ export class DialogKeywords {
GBLog.info(`BASIC: Markdown file ${filename} not found on database for ${min.instance.botId}.`);
}
await min.conversationalService['playMarkdown'](min, md, DialogKeywords.getChannel(), mobile);
await min.conversationalService['playMarkdown'](min, md, DialogKeywords.getChannel(), null, mobile);
return;
} else {
const gbaiName = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
@ -1246,12 +1248,12 @@ export class DialogKeywords {
}
if (!url) {
const imageData = await (await fetch(url)).arrayBuffer();
const ext = mime.extension(Path.extname(filename.localName));
// Prepare a cache to be referenced by Bot Framework.
let buf: any = Buffer.from(imageData);
const buf = Fs.readFileSync(filename);
const gbaiName = DialogKeywords.getGBAIPath(min.botId);
const localName = Path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.${ext}`);
Fs.writeFileSync(localName, buf, { encoding: null });

View file

@ -89,6 +89,7 @@ export class GBVMService extends GBService {
const docxStat = Fs.statSync(urlJoin(folder, wordFile));
const interval = 3000; // If compiled is older 30 seconds, then recompile.
let writeVBS = true;
if (Fs.existsSync(fullVbsFile)) {
const vbsStat = Fs.statSync(fullVbsFile);
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
@ -102,6 +103,8 @@ export class GBVMService extends GBService {
if (writeVBS) {
let text = await this.getTextFromWord(folder, wordFile);
// Pre process SET SCHEDULE calls.
const schedule = GBVMService.getSetScheduleKeywordArgs(text);
const s = new ScheduleServices();
if (schedule) {
@ -110,10 +113,14 @@ export class GBVMService extends GBService {
await s.deleteScheduleIfAny(min, mainName);
}
text = text.replace(/^\s*SET SCHEDULE (.*)/gim, '');
// Write VBS file without pragma keywords.
Fs.writeFileSync(urlJoin(folder, vbsFile), text);
}
// Process node_modules install.
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
if (!Fs.existsSync(node_modules)) {
const packageJson = `
@ -143,7 +150,7 @@ export class GBVMService extends GBService {
const fullFilename = urlJoin(folder, filename);
if (process.env.DEV_HOTSWAP) {
Fs.watchFile(fullFilename, async () => {
await this.translateBASIC(fullFilename, min);
await this.translateBASIC(mainName, fullFilename, min);
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
});
@ -156,17 +163,17 @@ export class GBVMService extends GBService {
const jsStat = Fs.statSync(jsfile);
const interval = 30000; // If compiled is older 30 seconds, then recompile.
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
await this.translateBASIC(fullFilename, min);
await this.translateBASIC(mainName, fullFilename, min);
}
} else {
await this.translateBASIC(fullFilename, min);
await this.translateBASIC(mainName, fullFilename, min);
}
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
return filename;
}
public async translateBASIC(filename: any, min: GBMinInstance) {
public async translateBASIC(mainName, filename: any, min: GBMinInstance) {
// Converts General Bots BASIC into regular VBS
let basicCode: string = Fs.readFileSync(filename, 'utf8');
@ -191,10 +198,16 @@ export class GBVMService extends GBService {
}
} while (include);
let { code, jsonMap } = await this.convert(basicCode);
let { code, map, metadata } = await this.convert(mainName, basicCode);
// Generates function JSON metadata to be used later.
const jsonFile = `${filename}.json`;
Fs.writeFileSync(jsonFile, JSON.stringify(metadata));
const mapFile = `${filename}.map`;
Fs.writeFileSync(mapFile, JSON.stringify(jsonMap));
Fs.writeFileSync(mapFile, JSON.stringify(map));
// Run JS into the GB context.
@ -211,14 +224,14 @@ export class GBVMService extends GBService {
// Unmarshalls Local variables from server VM.
let pid = this.pid;
const pid = this.pid;
let id = this.id;
let username = this.username;
let mobile = this.mobile;
let from = this.from;
let channel = this.channel;
let ENTER = this.ENTER;
let headers = this.headers;
const channel = this.channel;
const ENTER = this.ENTER;
const headers = this.headers;
let data = this.data;
let list = this.list;
let httpUsername = this.httpUsername;
@ -226,9 +239,9 @@ export class GBVMService extends GBService {
let today = this.today;
let now = this.now;
let page = null;
let files = [];
const files = [];
let col = 1;
let index = 1
let index = 1;
// Makes objects in BASIC insensitive.
@ -236,7 +249,7 @@ export class GBVMService extends GBService {
if (!listOrRow) {
return listOrRow
return listOrRow;
};
const lowercase = (oldKey) => typeof oldKey === 'string' ? oldKey.toLowerCase() : oldKey;
@ -342,13 +355,54 @@ export class GBVMService extends GBService {
return text;
}
public static getMetadata(mainName: string, propertiesText, description) {
const properties = [];
if (propertiesText) {
const getType = asClause => {
if (asClause.indexOf('AS STRING')) {
return 'string';
} else {
return 'enum';
}
};
for (let i = 0; i < propertiesText.length; i++) {
const propertiesExp = propertiesText[i];
const t = getType(propertiesExp[2]);
let element = {};
element['type'] = t;
if (t === 'enum') {
element['enum'] = propertiesExp[2];
} else if (t === 'string') {
element['description'] = propertiesExp[2];
}
properties.push(element);
}
}
let json = {
name: `${mainName}`,
description: description ? description[1] : '',
parameters: {
type: 'object',
properties: properties ? properties : []
}
};
return json;
}
/**
* Converts General Bots BASIC
*
*
* @param code General Bots BASIC
*/
public async convert(code: string) {
public async convert(mainName: string, code: string) {
// Start and End of VB2TS tags of processing.
code = process.env.ENABLE_AUTH ? `hear GBLogExin as login\n${code}` : code;
@ -356,13 +410,15 @@ export class GBVMService extends GBService {
const keywords = KeywordsExpressions.getKeywords();
let current = 41;
const map = {};
let properties = [];
let description;
for (let i = 1; i <= lines.length; i++) {
let line = lines[i - 1];
// Remove lines before statments.
line = line.replace(/^\s*\d+\s*/gi,'');
line = line.replace(/^\s*\d+\s*/gi, '');
for (let j = 0; j < keywords.length; j++) {
line = line.replace(keywords[j][0], keywords[j][1]);
@ -374,10 +430,27 @@ export class GBVMService extends GBService {
current = current + (add ? add : 0);
map[i] = current;
lines[i - 1] = line;
// Pre-process static KEYWORDS.
const params = /^\s*PARAM\s*(.*)\s*AS\s*(.*)/gim;
const param = params.exec(line);
if (param) {
properties.push(param);
}
const descriptionKeyword = /^\s*DESCRIPTION\s*\"(.*)\"/gim;
let descriptionReg = descriptionKeyword.exec(line);
if (descriptionReg) {
description = descriptionReg[1];
}
}
code = `${lines.join('\n')}\n`;
return { code, jsonMap: map };
let metadata = GBVMService.getMetadata(mainName, properties, description);
return { code, map, metadata };
}
/**
@ -389,7 +462,8 @@ export class GBVMService extends GBService {
step,
user: GuaribasUser,
deployer: GBDeployer,
debug: boolean = false
debug: boolean = false,
params = []
) {
// Creates a class DialogKeywords which is the *this* pointer
// in BASIC.
@ -414,17 +488,24 @@ export class GBVMService extends GBService {
}
}
// Adds params as variables to be added later as global objects..
let keys = Object.keys(params);
for (let j = 0; j < keys.length; j++) {
variables[keys[j]] = params[keys[j]];
}
const botId = min.botId;
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
const gbdialogPath = urlJoin(process.cwd(), 'work', path);
const scriptPath = urlJoin(gbdialogPath, `${text}.js`);
let code = min.sandBoxMap[text];
const channel = step? step.context.activity.channelId : 'web';
const channel = step ? step.context.activity.channelId : 'web';
const pid = GBVMService.createProcessInfo(user, min, channel);
const dk = new DialogKeywords();
const sys = new SystemKeywords();
await dk.setFilter ({pid: pid, value: null });
await dk.setFilter({ pid: pid, value: null });
sandbox['variables'] = variables;
sandbox['id'] = sys.getRandomId();
@ -489,7 +570,6 @@ export class GBVMService extends GBService {
}
} catch (error) {
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
} finally {
}
return result;

View file

@ -331,7 +331,7 @@ export class KeywordsExpressions {
/^\s*(.*)\=\s*(REWRITE)(\s*)(.*)/gim,
($0, $1, $2, $3, $4) => {
const params = this.getParams($4, ['text']);
return `await sys.rewrite ({pid: pid, ${params}})`;
return `${$1} = await sys.rewrite ({pid: pid, ${params}})`;
}
];

View file

@ -311,7 +311,7 @@ export class GBConversationalService {
caption: string,
channel: string
): Promise<any> {
return await this.sendFile(min, step, mobile, url , caption);
return await this.sendFile(min, step, mobile, url, caption);
}
public async sendFile(
@ -462,7 +462,7 @@ export class GBConversationalService {
return new Promise<string>(async (resolve, reject) => {
try {
const oggFile = new Readable();
oggFile._read = () => {}; // _read is required but you can noop it
oggFile._read = () => { }; // _read is required but you can noop it
oggFile.push(buffer);
oggFile.push(null);
@ -526,8 +526,12 @@ export class GBConversationalService {
});
}
public async playMarkdown(min: GBMinInstance, answer: string, channel: string, step: GBDialogStep, mobile: string) {
const user = step ? await min.userProfile.get(step.context, {}) : null;
public async playMarkdown(min: GBMinInstance, answer: string, channel: string,
step: GBDialogStep, mobile: string) {
const sec = new SecService();
const user = await sec.getUserFromSystemId(mobile ? mobile : step.context.activity.from.id);
let text = answer;
// Calls language translator.
@ -994,7 +998,7 @@ export class GBConversationalService {
}
}
public static async handleText (min, user, step, text: string){
public static async handleText(min, user, step, text: string) {
const sec = new SecService();
text = text.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, '');
@ -1059,7 +1063,13 @@ export class GBConversationalService {
// If it is a group, spells and sends them back.
const group = step.context.activity['group'];
if (textProcessed !== text && group) {
const groupSpell = group ? await min.core.getParam(
min.instance,
'Group Spell',
false) : false;
if (textProcessed !== text && group && groupSpell) {
await min.whatsAppDirectLine.sendToDevice(group, `Spell: ${text}`);
}
@ -1068,14 +1078,14 @@ export class GBConversationalService {
let locale = min.core.getParam(
min.instance,
'Default User Language',
GBConfigService.get('DEFAULT_USER_LANGUAGE')
);
GBConfigService.get('DEFAULT_USER_LANGUAGE'));
const detectLanguage =
min.core.getParam(
min.instance,
'Language Detector',
GBConfigService.getBoolean('LANGUAGE_DETECTOR')
) === 'true';
false) != false;
locale = user.locale;
if (text != '' && detectLanguage && !locale) {
locale = await min.conversationalService.getLanguage(min, text);
@ -1221,7 +1231,7 @@ export class GBConversationalService {
*/
public async sendOnConversation(min: GBMinInstance, user: GuaribasUser, message: any) {
if (message['buttons'] || message['sections']) {
await min['whatsAppDirectLine'].sendToDevice(user.userSystemId, message, user.conversationReference );
await min['whatsAppDirectLine'].sendToDevice(user.userSystemId, message, user.conversationReference);
} else if (user.conversationReference.startsWith('spaces')) {
await min['googleDirectLine'].sendToDevice(user.userSystemId, null, user.conversationReference, message);
} else {

View file

@ -433,6 +433,7 @@ ENDPOINT_UPDATE=true
await CollectionUtil.asyncForEach(instances, async instance => {
GBLog.info(`Updating bot endpoint for ${instance.botId}...`);
try {
await installationDeployer.updateBotProxy(
instance.botId,
GBConfigService.get('CLOUD_GROUP'),
@ -682,22 +683,20 @@ ENDPOINT_UPDATE=true
value = params ? params[name] : defaultValue;
}
if (typeof defaultValue === 'boolean') {
return new Boolean(value ? value.toString().toLowerCase() === 'true' : defaultValue);
return new Boolean(value ? value.toString().toLowerCase() === 'true' : defaultValue).valueOf();
}
if (typeof defaultValue === 'string') {
return value ? value : defaultValue;
}
if (typeof defaultValue === 'number') {
return new Number(value ? value : defaultValue ? defaultValue : 0);
return new Number(value ? value : defaultValue ? defaultValue : 0).valueOf();
}
if (instance['dataValues'] && !value) {
value = instance['dataValues'][name];
if (value === null) {
const minBoot = GBServer.globals.minBoot as any;
if (minBoot.instance && minBoot.instance.datavalues) {
value = minBoot.instance.datavalues[name];
}
value = minBoot.instance[name];
}
}

View file

@ -105,10 +105,18 @@ export class GBDeployer implements IGBDeployer {
*/
public static async internalGetDriveClient(min: GBMinInstance) {
let token;
if (min['cacheToken']) {
// TODO: Add expiration logic.
if (min['cacheToken'] && null) {
return min['cacheToken'];
} else {
token = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, true);
// Get token as root only if the bot does not have
// an custom tenant for retrieving packages.
token = await (min.adminService as any)['acquireElevatedToken']
(min.instance.instanceId, min.instance.authenticatorTenant?false:true);
const siteId = process.env.STORAGE_SITE_ID;
const libraryId = process.env.STORAGE_LIBRARY;
@ -280,7 +288,6 @@ export class GBDeployer implements IGBDeployer {
`${publicAddress}/api/messages/${instance.botId}`
);
} else {
const botId = GBConfigService.get('BOT_ID');
// Internally create resources on cloud provider.
@ -303,6 +310,7 @@ export class GBDeployer implements IGBDeployer {
// 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.
@ -553,7 +561,7 @@ export class GBDeployer implements IGBDeployer {
});
}
public async deployPackage(min: GBMinInstance, localPath: string) {
// TODO: @alanperdomo: Adjust interface mismatch.
// TODO: Adjust interface mismatch.
}
/**
* Deploys a folder into the bot storage.
@ -655,6 +663,30 @@ export class GBDeployer implements IGBDeployer {
}
}
/**
* Removes the package local files from cache.
*/
public async cleanupPackage(instance: IGBInstance, packageName: string) {
const path = DialogKeywords.getGBAIPath(instance.botId, null, packageName);
const localFolder = Path.join('work', path);
rimraf.sync(localFolder);
}
/**
* Removes the package from the storage and local work folders.
*/
public async undeployPackageFromPackageName(instance: IGBInstance, packageName: string) {
// Gets information about the package.
const packageType = Path.extname(packageName);
const p = await this.getStoragePackageByName(instance.instanceId, packageName);
const path = DialogKeywords.getGBAIPath(instance.botId, null, packageName);
const localFolder = Path.join('work', path);
return await this.undeployPackageFromLocalPath(instance, localFolder);
}
/**
* Removes the package from the storage and local work folders.
*/
@ -677,8 +709,11 @@ export class GBDeployer implements IGBDeployer {
const service = new KBService(this.core.sequelize);
rimraf.sync(localPath);
return await service.undeployKbFromStorage(instance, this, p.packageId);
if (p){
await service.undeployKbFromStorage(instance, this, p.packageId);
}
return;
case '.gbui':
break;
@ -709,19 +744,29 @@ export class GBDeployer implements IGBDeployer {
public async rebuildIndex(instance: IGBInstance, searchSchema: any) {
// Prepares search.
let release;
try {
GBLogEx.info(instance.instanceId, `Acquiring rebuildIndex mutex...`);
release = await GBServer.globals.indexSemaphore.acquire();
GBLogEx.info(instance.instanceId, `Acquire rebuildIndex done.`);
// TODO: Semaphore logic.
//try {
GBLogEx.info(instance.instanceId, `rebuildIndex running...`);
// release = await GBServer.globals.indexSemaphore.acquire();
// GBLogEx.info(instance.instanceId, `Acquire rebuildIndex done.`);
const key = instance.searchKey ? instance.searchKey : GBServer.globals.minBoot.instance.searchKey;
const searchIndex = instance.searchIndex ? instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
const searchIndexer = instance.searchIndexer
? instance.searchIndexer
: GBServer.globals.minBoot.instance.searchIndexer;
const host = instance.searchHost ? instance.searchHost : GBServer.globals.minBoot.instance.searchHost;
// Prepares search.
const search = new AzureSearch(
instance.searchKey,
instance.searchHost,
instance.searchIndex,
instance.searchIndexer
key,
host,
searchIndex,
searchIndexer
);
const connectionString = GBDeployer.getConnectionStringFromInstance(instance);
const connectionString = GBDeployer.getConnectionStringFromInstance(GBServer.globals.minBoot.instance);
const dsName = 'gb';
// Removes any previous index.
@ -758,13 +803,13 @@ export class GBDeployer implements IGBDeployer {
}
await search.createIndex(searchSchema, dsName);
release();
// release();
GBLogEx.info(instance.instanceId, `Released rebuildIndex mutex.`);
} catch {
if (release) {
release();
}
}
//} catch {
// if (release) {
// release();
// }
//}
}
/**

View file

@ -60,10 +60,18 @@ export class GBImporter {
localPath: string,
additionalInstance: IGBInstance = null
) {
const settingsJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
const file = urlJoin(localPath, 'settings.json');
let settingsJson = {botId: botId};
if (Fs.existsSync(file)){
settingsJson = JSON.parse(Fs.readFileSync(file, 'utf8'));
if (botId === undefined) {
botId = settingsJson.botId;
}
}
let instance: IGBInstance;
if (botId === undefined) {
botId = GBConfigService.get('BOT_ID');
@ -111,10 +119,10 @@ export class GBImporter {
localPath: string,
settingsJson: any
) {
const packageJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
const servicesJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8'));
const fullSettingsJson = { ...GBServer.globals.bootInstance, ...packageJson, ...settingsJson, ...servicesJson };
const fullSettingsJson = { ...GBServer.globals.bootInstance, ...settingsJson,
description:"General Bot", title:botId
};
if (botId !== undefined) {
fullSettingsJson.botId = botId;

View file

@ -148,6 +148,11 @@ export class GBMinService {
this.deployer = deployer;
}
public async enableAPI(min: GBMinInstance) {
}
/**
* Constructs a new minimal instance for each bot.
*/
@ -167,51 +172,8 @@ export class GBMinService {
GBServer.globals.server.get('/instances/:botId', this.handleGetInstanceForClient.bind(this));
}
function getRemoteId(ctx: Koa.Context) {
return '1'; // share a single session for now, real impl could use cookies or some other meaning for HTTP sessions
}
GBLogEx.info(0, 'Loading General Bots API...');
let proxies = {};
await CollectionUtil.asyncForEach(instances, async instance => {
const proxy = {
dk: new DialogKeywords(),
wa: new WebAutomationServices(),
sys: new SystemKeywords(),
dbg: new DebuggerService(),
img: new ImageProcessingServices()
};
proxies[instance.botId] = proxy;
});
const opts = {
pingSendTimeout: null,
keepAliveTimeout: null,
listeners: {
unsubscribed(subscriptions: number): void {},
subscribed(subscriptions: number): void {},
disconnected(remoteId: string, connections: number): void {},
connected(remoteId: string, connections: number): void {},
messageIn(...params): void {
params.shift();
GBLogEx.verbose(0, '[IN] ' + params);
},
messageOut(...params): void {
params.shift();
GBLogEx.verbose(0, '[OUT] ' + params);
}
}
};
GBServer.globals.server.dk = createRpcServer(
proxies,
createKoaHttpServer(GBVMService.API_PORT, getRemoteId, { prefix: `api/v3` }),
opts
);
// Calls mountBot event to all bots.
let i = 1;
if (instances.length > 1) {
@ -245,6 +207,10 @@ export class GBMinService {
this.bar1.stop();
}
// Loads API.
await this.ensureAPI();
// Loads schedules.
GBLog.info(`Loading SET SCHEDULE entries...`);
@ -292,7 +258,7 @@ export class GBMinService {
/**
* Unmounts the bot web site (default.gbui) secure domain, if any.
*/
public async unloadDomain(instance: IGBInstance) {}
public async unloadDomain(instance: IGBInstance) { }
/**
* Mount the instance by creating an BOT Framework bot object,
@ -542,7 +508,7 @@ export class GBMinService {
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
urlJoin(instance.botEndpoint, min.instance.botId, '/token'),
urlJoin(process.env.BOT_URL, min.instance.botId, '/token'),
resource,
instance.marketplaceId,
instance.marketplacePassword,
@ -561,7 +527,7 @@ export class GBMinService {
// Inform the home for default .gbui after finishing token retrival.
res.redirect(min.instance.botEndpoint);
res.redirect(process.env.BOT_URL);
}
}
);
@ -579,9 +545,8 @@ export class GBMinService {
min.instance.authenticatorTenant,
'/oauth2/authorize'
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.marketplaceId
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId
}&redirect_uri=${urlJoin(process.env.BOT_URL, min.instance.botId, 'token')}`;
GBLog.info(`HandleOAuthRequests: ${authorizationUrl}.`);
res.redirect(authorizationUrl);
});
@ -772,6 +737,8 @@ export class GBMinService {
WhatsappDirectLine.botGroups[min.botId] = group;
const minBoot = GBServer.globals.minBoot as any;
// If there is WhatsApp configuration specified, initialize
// infrastructure objects.
@ -788,7 +755,6 @@ export class GBMinService {
await min.whatsAppDirectLine.setup(true);
} else {
const minBoot = GBServer.globals.minBoot as any;
if (min !== minBoot && minBoot.instance.whatsappServiceKey) {
min.whatsAppDirectLine = new WhatsappDirectLine(
min,
@ -803,6 +769,13 @@ export class GBMinService {
}
}
// Builds bot numbers map in WhatsAppDirectLine globals.
let botNumber = min.core.getParam<string>(min.instance, 'Bot Number', null);
if (botNumber) {
WhatsappDirectLine.botsByNumber[botNumber] = min.whatsAppDirectLine;
}
// Setups default BOT Framework dialogs.
min.userProfile = conversationState.createProperty('userProfile');
@ -1069,8 +1042,7 @@ export class GBMinService {
await this.processEventActivity(min, user, context, step);
}
} catch (error) {
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
error.error ? (error.error.stack ? error.error.stack : '') : ''
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
GBLog.error(msg);
@ -1176,8 +1148,7 @@ export class GBMinService {
return utterance.match(Messages.global_quit);
}
private async handleUploads(min, step, user, params)
{
private async handleUploads(min, step, user, params, autoSave) {
// Prepare Promises to download each attachment and then execute each Promise.
if (
@ -1194,7 +1165,7 @@ export class GBMinService {
// In case of not having HEAR activated before, it is
// a upload with no Dialog, so run Auto Save to .gbdrive.
if (!min.cbMap[user.userId]) {
if (!min.cbMap[user.userId] && autoSave) {
const t = new SystemKeywords();
GBLog.info(`BASIC (${min.botId}): Upload done for ${attachmentData.fileName}.`);
const handle = WebAutomationServices.cyrb53(min.botId + attachmentData.fileName);
@ -1326,10 +1297,11 @@ export class GBMinService {
context.activity.text
);
}
}
const conversationReference = JSON.stringify(TurnContext.getConversationReference(context.activity));
await sec.updateConversationReferenceById(userId, conversationReference);
}
}
if (GBMinService.userMobile(step)) {
const startDialog = user.hearOnDialog ? user.hearOnDialog : min.core.getParam(min.instance, 'Start Dialog', null);
@ -1351,7 +1323,8 @@ export class GBMinService {
}
}
await this.handleUploads(min, step, user, params);
const notes = min.core.getParam(min.instance, 'Notes', null);
await this.handleUploads(min, step, user, params, notes != null);
// Files in .gbdialog can be called directly by typing its name normalized into JS .
@ -1408,10 +1381,10 @@ export class GBMinService {
step.context.activity['originalText']
step.context.activity['text'] = text;
const notes = min.core.getParam(min.instance, 'Notes', null);
if (notes && text && text !== "") {
const sys = new SystemKeywords();
await sys.note({pid, text});
await sys.note({ pid, text });
await step.context.sendActivity('OK.');
return;
}
@ -1459,8 +1432,7 @@ export class GBMinService {
try {
await step.continueDialog();
} catch (error) {
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
error.error ? (error.error.stack ? error.error.stack : '') : ''
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
GBLog.error(msg);
await min.conversationalService.sendText(
@ -1502,4 +1474,92 @@ export class GBMinService {
}
}
}
public async ensureAPI() {
const mins = GBServer.globals.minInstances;
function getRemoteId(ctx: Koa.Context) {
return '1'; // Each bot has its own API.
}
const close = async () => {
return new Promise(resolve => {
if (GBServer.globals.server.apiServer) {
GBServer.globals.server.apiServer.close(
cb => {
resolve(true);
GBLogEx.info(0, 'Reloading General Bots API...');
}
);
}
else{
resolve(true);
GBLogEx.info(0, 'Loading General Bots API...');
}
});
};
await close();
let proxies = {};
await CollectionUtil.asyncForEach(mins, async min => {
let dialogs = {};
await CollectionUtil.asyncForEach(Object.values(min.scriptMap), async script => {
const f = new Function()
dialogs[script] = async (data)=> {
let params;
if (data){
params = JSON.parse(data);
}
await GBVMService.callVM(script, min, null, null, null, false, params);
}
});
const proxy = {
dk: new DialogKeywords(),
wa: new WebAutomationServices(),
sys: new SystemKeywords(),
dbg: new DebuggerService(),
img: new ImageProcessingServices(),
dialogs: dialogs
};
proxies[min.botId] = proxy;
});
const opts = {
pingSendTimeout: null,
keepAliveTimeout: null,
listeners: {
unsubscribed(subscriptions: number): void { },
subscribed(subscriptions: number): void { },
disconnected(remoteId: string, connections: number): void { },
connected(remoteId: string, connections: number): void { },
messageIn(...params): void {
params.shift();
GBLogEx.verbose(0, '[IN] ' + params);
},
messageOut(...params): void {
params.shift();
GBLogEx.verbose(0, '[OUT] ' + params);
}
}
};
GBServer.globals.server.apiServer = createKoaHttpServer(GBVMService.API_PORT, getRemoteId, { prefix: `api/v3` });
createRpcServer(
proxies,
GBServer.globals.server.apiServer,
opts
);
}
}

View file

@ -33,9 +33,57 @@
'use strict';
import { GBMinInstance } from 'botlib';
import { Configuration, OpenAIApi } from "openai";
//import OpenAI from "openai";
import { ChatGPTAPIBrowser, getOpenAIAuth } from 'chatgpt'
import { CollectionUtil } from 'pragmatismo-io-framework';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import Path from 'path';
import * as Fs from 'fs';
export class ChatServices {
public static async sendMessage(min: GBMinInstance, text: string) {
let key;
if (process.env.OPENAI_KEY) {
key = process.env.OPENAI_KEY;
}
else {
key = min.core.getParam(min.instance, 'Open AI Key', null);
}
if (!key) {
throw new Error('Open AI Key not configured in .gbot.');
}
let functions = [];
// Adds .gbdialog as functions if any to GPT Functions.
await CollectionUtil.asyncForEach(Object.values(min.scriptMap), async script => {
const path = DialogKeywords.getGBAIPath(min.botId, "gbdialog", null);
const localFolder = Path.join('work', path, `${script}.json`);
if (Fs.existsSync(localFolder)) {
const func = Fs.readFileSync(localFolder).toJSON();
functions.push(func);
}
});
// Calls Model.
// const openai = new OpenAI({
// apiKey: key
// });
// const chatCompletion = await openai.chat.completions.create({
// model: "gpt-3.5-turbo",
// messages: [{ role: "user", content: text }],
// functions: functions
// });
// return chatCompletion.choices[0].message.content;
}
/**
* Generate text
*
@ -45,22 +93,25 @@ export class ChatServices {
*
*/
public static async continue(min: GBMinInstance, text: string, chatId) {
let key = min.core.getParam(min.instance, 'Open AI Key', null);
let key;
if (process.env.OPENAI_KEY) {
key = process.env.OPENAI_KEY;
}
else {
key = min.core.getParam(min.instance, 'Open AI Key', null);
}
if (!key) {
throw new Error('Open AI Key not configured in .gbot.');
}
// const openai = new OpenAI({
// apiKey: key
// });
// const chatCompletion = await openai.chat.completions.create({
// model: "gpt-3.5-turbo",
// messages: [{ role: "user", content: text }]
const configuration = new Configuration({
apiKey: key,
});
const openai = new OpenAIApi(configuration);
const chatCompletion = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [{role: "user", content: text}],
});
return chatCompletion.data.choices[0].message.content;
// });
// return chatCompletion.choices[0].message.content;
}
}

View file

@ -108,8 +108,14 @@ export class AskDialog extends IGBDialog {
text = Messages[locale].ask_first_time;
} else if (step.options && step.options.isReturning && !step.context.activity.group) {
const askForMore = min.core.getParam(min.instance, 'Ask For More', null);
if (askForMore){
text = askForMore ;
}
else
{
text = askForMore ? Messages[locale].anything_else : '';
return await step.endDialog(null);
}
} else if (step.context.activity.group || (step.options && step.options.emptyPrompt)) {
return await step.next();
} else if (user.subjects.length > 0) {

View file

@ -289,9 +289,14 @@ export class KBService implements IGBKBService {
}
}
const key = instance.searchKey ? instance.searchKey:
GBServer.globals.minBoot.instance.searchKey;
const host = instance.searchHost ? instance.searchHost :
GBServer.globals.minBoot.instance.searchHost;
// No direct match found, so Search is used.
if (instance.searchKey !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
if (key !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
interface SearchResults {
instanceId: number;
questionId: number;
@ -303,11 +308,11 @@ export class KBService implements IGBKBService {
subject4: string;
}
const client = new SearchClient<any>('https://' + instance.searchHost, 'azuresql-index', {
key: instance.searchKey
const client = new SearchClient<any>('https://' + host, 'azuresql-index', {
key: key
} as any);
const results = await client.search(query, {
const results = await client.search(query.substring(0,499), {
filter: `instanceId eq ${instance.instanceId} and skipIndex eq false`,
searchFields: ['content', 'subject1', 'subject2', 'subject3', 'subject4'],
select: ['instanceId', 'questionId', 'answerId'],
@ -662,6 +667,8 @@ export class KBService implements IGBKBService {
const subjectFile = urlJoin(localPath, 'subjects.json');
const menuFile = urlJoin(localPath, 'menu.xlsx');
// Imports menu.xlsx if any.
if (Fs.existsSync(subjectFile) || Fs.existsSync(menuFile)) {
await this.importSubjectFile(packageStorage.packageId, subjectFile, menuFile, instance);
}
@ -1012,11 +1019,13 @@ export class KBService implements IGBKBService {
const packageName = Path.basename(localPath);
const instance = await core.loadInstanceByBotId(min.botId);
GBLog.info(`[GBDeployer] Importing: ${localPath}`);
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
await this.importKbPackage(min, localPath, p, instance);
GBDeployer.mountGBKBAssets(packageName, min.botId, localPath);
const service = await AzureDeployerService.createInstance(deployer);
await deployer.rebuildIndex(instance, service.getKBSearchSchema(instance.searchIndex));
const searchIndex = instance.searchIndex ? instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
await deployer.rebuildIndex(instance, service.getKBSearchSchema(searchIndex));
min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
await KBService.RefreshNER(min);

View file

@ -51,6 +51,7 @@ import pkg from 'whatsapp-web.js';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import e from 'express';
const { List, Buttons, Client, MessageMedia } = pkg;
/**
@ -58,6 +59,7 @@ const { List, Buttons, Client, MessageMedia } = pkg;
*/
export class WhatsappDirectLine extends GBService {
public static conversationIds = {};
public static botsByNumber = {};
public static mobiles = {};
public static phones = {};
public static chatIds = {};
@ -121,7 +123,7 @@ export class WhatsappDirectLine extends GBService {
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${this.min.instance.webchatKey}`;
req.headers['Authorization'] = `Bearer ${this.min.instance.whatsappBotKey}`;
}
});
this.directLineClient = client;
@ -197,8 +199,7 @@ export class WhatsappDirectLine extends GBService {
};
if (setUrl) {
createClient.bind(this)();
}
else {
} else {
this.customClient = minBoot.whatsAppDirectLine.customClient;
}
setUrl = false;
@ -303,7 +304,7 @@ export class WhatsappDirectLine extends GBService {
public async received(req, res) {
const provider = WhatsappDirectLine.providerFromRequest(req);
let message, from, fromName, text: string;
let message, to, from, fromName, text: string;
let group = '';
let answerText = null;
let attachments = null;
@ -311,6 +312,18 @@ export class WhatsappDirectLine extends GBService {
switch (provider) {
case 'GeneralBots':
message = req;
to = message.to.endsWith('@g.us') ? message.to.split('@')[0] : message.to.split('@')[0];
const newThis = WhatsappDirectLine.botsByNumber[to];
// If there is a number specified, checks if it
// is related to a custom bot and reroutes immediately.
if (newThis !== this && newThis.min.botId !== GBServer.globals.minBoot.botId) {
await newThis.received(req, res);
return;
}
text = message.body;
from = message.from.endsWith('@g.us') ? message.author.split('@')[0] : message.from.split('@')[0];
fromName = message._data.notifyName;
@ -318,9 +331,14 @@ export class WhatsappDirectLine extends GBService {
if (message.hasMedia) {
const base64Image = await message.downloadMedia();
let buf: any = Buffer.from(base64Image.data, "base64");
let buf: any = Buffer.from(base64Image.data, 'base64');
const gbaiName = DialogKeywords.getGBAIPath(this.min.botId);
const localName = Path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.docx`);
const localName = Path.join(
'work',
gbaiName,
'cache',
`tmp${GBAdminService.getRndReadableIdentifier()}.docx`
);
Fs.writeFileSync(localName, buf, { encoding: null });
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName));
@ -401,10 +419,12 @@ export class WhatsappDirectLine extends GBService {
// Bot name must be specified on config.
if (botGroupID === group) {
// Shortcut has been mentioned?
let found = false;
parts.forEach(e1 => {
botShortcuts.forEach(e2 => {
if (e1 === e2 && !found) {
found = true;
@ -425,17 +445,18 @@ export class WhatsappDirectLine extends GBService {
}
});
}
});
// Ignore group messages without the mention to Bot.
let smsServiceNumber = this.min.core.getParam<string>(this.min.instance, 'whatsappServiceNumber', null);
if (smsServiceNumber && !answerText) {
smsServiceNumber = smsServiceNumber.replace('+', '');
if (!message.body.startsWith('@' + smsServiceNumber)) {
let botNumber = this.min.core.getParam<string>(this.min.instance, 'Bot Number', null);
if (botNumber && !answerText && !found) {
botNumber = botNumber.replace('+', '');
if (!message.body.startsWith('@' + botNumber)) {
return;
}
}
});
}
}
@ -528,7 +549,6 @@ export class WhatsappDirectLine extends GBService {
);
if (user.agentSystemId.indexOf('@') !== -1) {
// Agent is from Teams.
await this.min.conversationalService['sendOnConversation'](
this.min,
@ -566,7 +586,7 @@ export class WhatsappDirectLine extends GBService {
const prompt = `the person said: ${text}. what can I tell her?`;
const answer = await ChatServices.continue(this.min, prompt, 0);
text = `${text} \n\nGeneral Bots: ${answer}`
text = `${text} \n\nGeneral Bots: ${answer}`;
if (user.agentSystemId.indexOf('@') !== -1) {
// Agent is from Teams or Google Chat.
@ -609,11 +629,9 @@ export class WhatsappDirectLine extends GBService {
}
private async endTransfer(id: string, locale: string, user: GuaribasUser, agent: GuaribasUser, sec: SecService) {
await this.sendToDeviceEx(id, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
if (user.agentSystemId.indexOf('@') !== -1) {
// Agent is from Teams.
await this.min.conversationalService['sendOnConversation'](
@ -621,9 +639,7 @@ export class WhatsappDirectLine extends GBService {
agent,
Messages[this.locale].notify_end_transfer(this.min.instance.botId)
);
} else {
await this.sendToDeviceEx(
user.agentSystemId,
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
@ -712,15 +728,13 @@ export class WhatsappDirectLine extends GBService {
if (activity.attachments) {
await CollectionUtil.asyncForEach(activity.attachments, async attachment => {
switch (attachment.contentType) {
case 'application/vnd.microsoft.card.hero':
output += `\n${this.renderHeroCard(attachment)}`;
break;
case 'image/png':
await this.sendFileToDevice(to, attachment.contentUrl,
attachment.name, attachment.name, 0);
await this.sendFileToDevice(to, attachment.contentUrl, attachment.name, attachment.name, 0);
return;
@ -742,6 +756,7 @@ export class WhatsappDirectLine extends GBService {
switch (this.provider) {
case 'GeneralBots':
const attachment = await MessageMedia.fromUrl(url);
to = to.replace('+', '');
if (to.indexOf('@') == -1) {
if (to.length == 18) {
to = to + '@g.us';
@ -885,6 +900,7 @@ export class WhatsappDirectLine extends GBService {
switch (this.provider) {
case 'GeneralBots':
to = to.replace('+', '');
if (to.indexOf('@') == -1) {
if (to.length == 18) {
to = to + '@g.us';
@ -963,11 +979,9 @@ export class WhatsappDirectLine extends GBService {
switch (provider) {
case 'GeneralBots':
// Ignore E2E messages and status updates.
if (req.type && req.type === 'e2e_notification'
|| req.isStatus) {
if ((req.type && req.type === 'e2e_notification') || req.isStatus) {
return;
}
@ -1018,16 +1032,25 @@ export class WhatsappDirectLine extends GBService {
}
const sec = new SecService();
let user = await sec.getUserFromSystemId(id);
// Tries to find if user wants to switch bots.
let toSwitchMin = GBServer.globals.minInstances.filter(
p => p.instance.botId.toLowerCase() === text.toLowerCase()
)[0];
GBLog.info(`A WhatsApp mobile requested instance for: ${botId}.`);
let urlMin: any = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
const botNumber = urlMin ? urlMin.core.getParam(urlMin.instance, 'Bot Number', null) : null;
if (botNumber) {
if (botNumber && GBServer.globals.minBoot.botId !== urlMin.botId) {
GBLog.info(`${user.userSystemId} fixed by bot number talked to: ${botId}.`);
user = await sec.updateUserInstance(user.userSystemId, urlMin.instance.instanceId);
}
let activeMin;
// Processes group behaviour.
@ -1071,12 +1094,18 @@ export class WhatsappDirectLine extends GBService {
// Detects if the welcome message is enabled.
if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') {
// Tries to find if user wants to switch bots.
let toSwitchMin = GBServer.globals.minInstances.filter(
p => p.instance.botId.toLowerCase() === text.toLowerCase()
if (process.env.WHATSAPP_WELCOME_DISABLED === 'true') {
let minInstance = GBServer.globals.minInstances.filter(
p => p.instance.botId.toLowerCase() === botId.toLowerCase()
)[0];
// Just pass the message to the receiver.
await minInstance.whatsAppDirectLine.received(req, res);
return;
}
if (!toSwitchMin) {
toSwitchMin = GBServer.globals.minInstances.filter(p =>
p.instance.activationCode ? p.instance.activationCode.toLowerCase() === text.toLowerCase() : false
@ -1125,16 +1154,20 @@ export class WhatsappDirectLine extends GBService {
} else {
// User wants to switch bots.
if (toSwitchMin !== undefined) {
if (toSwitchMin) {
GBLog.info(`Switching bots from ${botId} to ${toSwitchMin.botId}...`);
// So gets the new bot instance information and prepares to
// auto start dialog if any is specified.
activeMin = toSwitchMin;
const instance = await this.min.core.loadInstanceByBotId(activeMin.botId);
await sec.updateUserInstance(id, instance.instanceId);
user = await sec.updateUserInstance(id, instance.instanceId);
await (activeMin as any).whatsAppDirectLine.resetConversationId(activeMin.botId, id, '');
const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
if (startDialog) {
GBLog.info(`Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`);
if (provider === 'chatapi') {
req.body.messages[0].body = `/start`;
@ -1165,8 +1198,7 @@ export class WhatsappDirectLine extends GBService {
id,
`O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...`
);
}
else {
} else {
if ((activeMin as any).whatsAppDirectLine) {
t = (activeMin as any).whatsAppDirectLine;
} else {
@ -1174,19 +1206,11 @@ export class WhatsappDirectLine extends GBService {
}
}
t.received(req, res);
await t.received(req, res);
}
}
} else {
let minInstance = GBServer.globals.minInstances.filter(
p => p.instance.botId.toLowerCase() === botId.toLowerCase()
)[0];
// Just pass the message to the receiver.
await minInstance.whatsAppDirectLine.received(req, res);
}
} catch (error) {
error = error['e'] ? error['e'] : error;
GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error} ${error.stack}`);
}
}

View file

@ -48,6 +48,7 @@ export class RootData {
public publicAddress: string; // URI for BotServer.
public server: any; // Express reference.
public httpsServer: any; // Express reference (HTTPS).
public apiServer: any; // Koa reference (HTTPS) for GB API (isolated from /).
public sysPackages: any[]; // Loaded system package list.
public appPackages: any[]; // Loaded .gbapp package list.
public minService: GBMinService; // Minimalist service core.

View file

@ -59,7 +59,6 @@ import * as winston from 'winston-logs-display';
import { RootData } from './RootData.js';
import { GBSSR } from '../packages/core.gbapp/services/GBSSR.js';
import { Mutex } from 'async-mutex';
import { GBVMService } from '../packages/basic.gblib/services/GBVMService.js';
/**
* General Bots open-core entry point.
@ -77,6 +76,8 @@ export class GBServer {
GBConfigService.init();
const port = GBConfigService.getServerPort();
if (process.env.TEST_SHELL) {
GBLog.info(`Running TEST_SHELL: ${process.env.TEST_SHELL}...`);
try {
@ -108,6 +109,7 @@ export class GBServer {
// Setups global error handlers.
process.on('unhandledRejection', (err, p) => {
err = err['e'] ? err['e'] : err;
GBLog.error(`UNHANDLED_REJECTION(promises): ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`);
if (err['response']?.obj?.httpStatusCode === 404) {
GBLog.warn(`Check reverse proxy: ${process.env.BOT_URL} as it seems to be invalid.`);
@ -116,6 +118,7 @@ export class GBServer {
process.on('uncaughtException', (err, p) => {
if (err !== null) {
err = err['e'] ? err['e'] : err;
GBLog.error(`UNCAUGHT_EXCEPTION: ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`);
} else {
GBLog.error('UNCAUGHT_EXCEPTION: Unknown error (err is null)');
@ -136,6 +139,7 @@ export class GBServer {
const mainCallback = () => {
(async () => {
try {
GBLog.info(`Now accepting connections on ${port}...`);
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';