Merge branch 'main' of https://github.com/GeneralBots/BotServer
This commit is contained in:
commit
edd0fe9fea
19 changed files with 624 additions and 316 deletions
|
@ -97,7 +97,7 @@
|
||||||
"botlib": "3.0.11",
|
"botlib": "3.0.11",
|
||||||
"c3-chart-maker": "0.2.8",
|
"c3-chart-maker": "0.2.8",
|
||||||
"cd": "0.3.3",
|
"cd": "0.3.3",
|
||||||
"chatgpt": "2.4.2",
|
"chatgpt": "^2.4.2",
|
||||||
"chrome-remote-interface": "0.31.3",
|
"chrome-remote-interface": "0.31.3",
|
||||||
"cli-progress": "3.11.2",
|
"cli-progress": "3.11.2",
|
||||||
"cli-spinner": "0.2.10",
|
"cli-spinner": "0.2.10",
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
"moment": "1.3.0",
|
"moment": "1.3.0",
|
||||||
"ms-rest-azure": "3.0.0",
|
"ms-rest-azure": "3.0.0",
|
||||||
"nexmo": "2.9.1",
|
"nexmo": "2.9.1",
|
||||||
"ngrok": "4.3.3",
|
"ngrok": "5.0.0-beta.2",
|
||||||
"node-cron": "3.0.2",
|
"node-cron": "3.0.2",
|
||||||
"node-html-parser": "6.1.5",
|
"node-html-parser": "6.1.5",
|
||||||
"node-nlp": "4.26.1",
|
"node-nlp": "4.26.1",
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
"npm": "9.6.1",
|
"npm": "9.6.1",
|
||||||
"open": "8.4.0",
|
"open": "8.4.0",
|
||||||
"open-docxtemplater-image-module": "1.0.3",
|
"open-docxtemplater-image-module": "1.0.3",
|
||||||
"openai": "3.3.0",
|
"openai": "4.6.0",
|
||||||
"pdf-extraction": "1.0.2",
|
"pdf-extraction": "1.0.2",
|
||||||
"pdf-to-png-converter": "3.1.0",
|
"pdf-to-png-converter": "3.1.0",
|
||||||
"pdfkit": "0.13.0",
|
"pdfkit": "0.13.0",
|
||||||
|
@ -180,7 +180,7 @@
|
||||||
"vm2-process": "2.1.1",
|
"vm2-process": "2.1.1",
|
||||||
"walk-promise": "0.2.0",
|
"walk-promise": "0.2.0",
|
||||||
"washyourmouthoutwithsoap": "1.0.2",
|
"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": "3.8.2",
|
||||||
"winston-logs-display": "1.0.0",
|
"winston-logs-display": "1.0.0",
|
||||||
"ws": "8.12.1",
|
"ws": "8.12.1",
|
||||||
|
|
|
@ -421,7 +421,7 @@ export class AdminDialog extends IGBDialog {
|
||||||
|
|
||||||
min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
|
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`;
|
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));
|
await min.conversationalService.sendText(min, step, Messages[locale].consent(url));
|
||||||
|
|
|
@ -170,6 +170,11 @@ export class GBAdminService implements IGBAdminService {
|
||||||
// .gbot packages are handled using storage API, so no download
|
// .gbot packages are handled using storage API, so no download
|
||||||
// of local resources is required.
|
// of local resources is required.
|
||||||
const gbai = DialogKeywords.getGBAIPath(min.instance.botId);
|
const gbai = DialogKeywords.getGBAIPath(min.instance.botId);
|
||||||
|
|
||||||
|
if (packageType === 'gbkb') {
|
||||||
|
await deployer['cleanupPackage'](min.instance, packageName);
|
||||||
|
}
|
||||||
|
|
||||||
await deployer['downloadFolder'](min,
|
await deployer['downloadFolder'](min,
|
||||||
Path.join('work', `${gbai}`),
|
Path.join('work', `${gbai}`),
|
||||||
Path.basename(localFolder));
|
Path.basename(localFolder));
|
||||||
|
@ -178,9 +183,10 @@ export class GBAdminService implements IGBAdminService {
|
||||||
}
|
}
|
||||||
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
|
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
const service = await AzureDeployerService.createInstance(deployer);
|
const service = await AzureDeployerService.createInstance(deployer);
|
||||||
|
const searchIndex = min.instance.searchIndex ? min.instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
|
||||||
await deployer.rebuildIndex(
|
await deployer.rebuildIndex(
|
||||||
min.instance,
|
min.instance,
|
||||||
service.getKBSearchSchema(min.instance.searchIndex)
|
service.getKBSearchSchema(searchIndex)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -266,6 +266,11 @@ export class AzureDeployerService implements IGBInstallationDeployer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateBotProxy(botId: string, group: string, endpoint: string) {
|
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 baseUrl = `https://management.azure.com/`;
|
||||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
|
@ -455,7 +460,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
|
||||||
};
|
};
|
||||||
|
|
||||||
GBLog.info(`Deploying Bot...`);
|
GBLog.info(`Deploying Bot...`);
|
||||||
instance.botEndpoint = this.defaultEndPoint;
|
instance.botEndpoint = 'TODO: remove this column.';
|
||||||
|
|
||||||
instance = await this.internalDeployBot(
|
instance = await this.internalDeployBot(
|
||||||
instance,
|
instance,
|
||||||
|
@ -565,9 +570,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
|
||||||
luisAppIds: [nlpAppId],
|
luisAppIds: [nlpAppId],
|
||||||
luisKey: nlpKey,
|
luisKey: nlpKey,
|
||||||
msaAppId: appId,
|
msaAppId: appId,
|
||||||
msaAppPassword: appPassword,
|
msaAppPassword: appPassword
|
||||||
enabledChannels: ['webchat', 'skype'], //, "facebook"],
|
|
||||||
configuredChannels: ['webchat', 'skype'] //, "facebook"]
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ export function createKoaHttpServer(
|
||||||
app.use(koaBody.koaBody({ multipart: true }));
|
app.use(koaBody.koaBody({ multipart: true }));
|
||||||
app.use(middleware);
|
app.use(middleware);
|
||||||
const server = app.listen(port);
|
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;
|
server.timeout = SERVER_TIMEOUT;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1230,7 +1230,9 @@ export class DialogKeywords {
|
||||||
GBLog.info(`BASIC: Markdown file ${filename} not found on database for ${min.instance.botId}.`);
|
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 {
|
} else {
|
||||||
const gbaiName = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
|
const gbaiName = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
|
||||||
|
|
||||||
|
@ -1246,12 +1248,12 @@ export class DialogKeywords {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
const imageData = await (await fetch(url)).arrayBuffer();
|
|
||||||
const ext = mime.extension(Path.extname(filename.localName));
|
const ext = mime.extension(Path.extname(filename.localName));
|
||||||
|
|
||||||
// Prepare a cache to be referenced by Bot Framework.
|
// 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 gbaiName = DialogKeywords.getGBAIPath(min.botId);
|
||||||
const localName = Path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.${ext}`);
|
const localName = Path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.${ext}`);
|
||||||
Fs.writeFileSync(localName, buf, { encoding: null });
|
Fs.writeFileSync(localName, buf, { encoding: null });
|
||||||
|
|
|
@ -89,6 +89,7 @@ export class GBVMService extends GBService {
|
||||||
const docxStat = Fs.statSync(urlJoin(folder, wordFile));
|
const docxStat = Fs.statSync(urlJoin(folder, wordFile));
|
||||||
const interval = 3000; // If compiled is older 30 seconds, then recompile.
|
const interval = 3000; // If compiled is older 30 seconds, then recompile.
|
||||||
let writeVBS = true;
|
let writeVBS = true;
|
||||||
|
|
||||||
if (Fs.existsSync(fullVbsFile)) {
|
if (Fs.existsSync(fullVbsFile)) {
|
||||||
const vbsStat = Fs.statSync(fullVbsFile);
|
const vbsStat = Fs.statSync(fullVbsFile);
|
||||||
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
|
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
|
||||||
|
@ -102,6 +103,8 @@ export class GBVMService extends GBService {
|
||||||
if (writeVBS) {
|
if (writeVBS) {
|
||||||
let text = await this.getTextFromWord(folder, wordFile);
|
let text = await this.getTextFromWord(folder, wordFile);
|
||||||
|
|
||||||
|
// Pre process SET SCHEDULE calls.
|
||||||
|
|
||||||
const schedule = GBVMService.getSetScheduleKeywordArgs(text);
|
const schedule = GBVMService.getSetScheduleKeywordArgs(text);
|
||||||
const s = new ScheduleServices();
|
const s = new ScheduleServices();
|
||||||
if (schedule) {
|
if (schedule) {
|
||||||
|
@ -110,10 +113,14 @@ export class GBVMService extends GBService {
|
||||||
await s.deleteScheduleIfAny(min, mainName);
|
await s.deleteScheduleIfAny(min, mainName);
|
||||||
}
|
}
|
||||||
text = text.replace(/^\s*SET SCHEDULE (.*)/gim, '');
|
text = text.replace(/^\s*SET SCHEDULE (.*)/gim, '');
|
||||||
|
|
||||||
|
// Write VBS file without pragma keywords.
|
||||||
|
|
||||||
Fs.writeFileSync(urlJoin(folder, vbsFile), text);
|
Fs.writeFileSync(urlJoin(folder, vbsFile), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process node_modules install.
|
// Process node_modules install.
|
||||||
|
|
||||||
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
|
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
|
||||||
if (!Fs.existsSync(node_modules)) {
|
if (!Fs.existsSync(node_modules)) {
|
||||||
const packageJson = `
|
const packageJson = `
|
||||||
|
@ -143,7 +150,7 @@ export class GBVMService extends GBService {
|
||||||
const fullFilename = urlJoin(folder, filename);
|
const fullFilename = urlJoin(folder, filename);
|
||||||
if (process.env.DEV_HOTSWAP) {
|
if (process.env.DEV_HOTSWAP) {
|
||||||
Fs.watchFile(fullFilename, async () => {
|
Fs.watchFile(fullFilename, async () => {
|
||||||
await this.translateBASIC(fullFilename, min);
|
await this.translateBASIC(mainName, fullFilename, min);
|
||||||
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
|
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
|
||||||
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
|
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
|
||||||
});
|
});
|
||||||
|
@ -156,17 +163,17 @@ export class GBVMService extends GBService {
|
||||||
const jsStat = Fs.statSync(jsfile);
|
const jsStat = Fs.statSync(jsfile);
|
||||||
const interval = 30000; // If compiled is older 30 seconds, then recompile.
|
const interval = 30000; // If compiled is older 30 seconds, then recompile.
|
||||||
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
|
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
|
||||||
await this.translateBASIC(fullFilename, min);
|
await this.translateBASIC(mainName, fullFilename, min);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.translateBASIC(fullFilename, min);
|
await this.translateBASIC(mainName, fullFilename, min);
|
||||||
}
|
}
|
||||||
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
|
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
|
||||||
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
|
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async translateBASIC(filename: any, min: GBMinInstance) {
|
public async translateBASIC(mainName, filename: any, min: GBMinInstance) {
|
||||||
// Converts General Bots BASIC into regular VBS
|
// Converts General Bots BASIC into regular VBS
|
||||||
|
|
||||||
let basicCode: string = Fs.readFileSync(filename, 'utf8');
|
let basicCode: string = Fs.readFileSync(filename, 'utf8');
|
||||||
|
@ -191,10 +198,16 @@ export class GBVMService extends GBService {
|
||||||
}
|
}
|
||||||
} while (include);
|
} 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`;
|
const mapFile = `${filename}.map`;
|
||||||
|
|
||||||
Fs.writeFileSync(mapFile, JSON.stringify(jsonMap));
|
Fs.writeFileSync(mapFile, JSON.stringify(map));
|
||||||
|
|
||||||
// Run JS into the GB context.
|
// Run JS into the GB context.
|
||||||
|
|
||||||
|
@ -211,14 +224,14 @@ export class GBVMService extends GBService {
|
||||||
|
|
||||||
// Unmarshalls Local variables from server VM.
|
// Unmarshalls Local variables from server VM.
|
||||||
|
|
||||||
let pid = this.pid;
|
const pid = this.pid;
|
||||||
let id = this.id;
|
let id = this.id;
|
||||||
let username = this.username;
|
let username = this.username;
|
||||||
let mobile = this.mobile;
|
let mobile = this.mobile;
|
||||||
let from = this.from;
|
let from = this.from;
|
||||||
let channel = this.channel;
|
const channel = this.channel;
|
||||||
let ENTER = this.ENTER;
|
const ENTER = this.ENTER;
|
||||||
let headers = this.headers;
|
const headers = this.headers;
|
||||||
let data = this.data;
|
let data = this.data;
|
||||||
let list = this.list;
|
let list = this.list;
|
||||||
let httpUsername = this.httpUsername;
|
let httpUsername = this.httpUsername;
|
||||||
|
@ -226,9 +239,9 @@ export class GBVMService extends GBService {
|
||||||
let today = this.today;
|
let today = this.today;
|
||||||
let now = this.now;
|
let now = this.now;
|
||||||
let page = null;
|
let page = null;
|
||||||
let files = [];
|
const files = [];
|
||||||
let col = 1;
|
let col = 1;
|
||||||
let index = 1
|
let index = 1;
|
||||||
|
|
||||||
// Makes objects in BASIC insensitive.
|
// Makes objects in BASIC insensitive.
|
||||||
|
|
||||||
|
@ -236,7 +249,7 @@ export class GBVMService extends GBService {
|
||||||
|
|
||||||
if (!listOrRow) {
|
if (!listOrRow) {
|
||||||
|
|
||||||
return listOrRow
|
return listOrRow;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lowercase = (oldKey) => typeof oldKey === 'string' ? oldKey.toLowerCase() : oldKey;
|
const lowercase = (oldKey) => typeof oldKey === 'string' ? oldKey.toLowerCase() : oldKey;
|
||||||
|
@ -342,13 +355,54 @@ export class GBVMService extends GBService {
|
||||||
return text;
|
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
|
* Converts General Bots BASIC
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param code 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.
|
// Start and End of VB2TS tags of processing.
|
||||||
|
|
||||||
code = process.env.ENABLE_AUTH ? `hear GBLogExin as login\n${code}` : code;
|
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();
|
const keywords = KeywordsExpressions.getKeywords();
|
||||||
let current = 41;
|
let current = 41;
|
||||||
const map = {};
|
const map = {};
|
||||||
|
let properties = [];
|
||||||
|
let description;
|
||||||
|
|
||||||
for (let i = 1; i <= lines.length; i++) {
|
for (let i = 1; i <= lines.length; i++) {
|
||||||
let line = lines[i - 1];
|
let line = lines[i - 1];
|
||||||
|
|
||||||
// Remove lines before statments.
|
// 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++) {
|
for (let j = 0; j < keywords.length; j++) {
|
||||||
line = line.replace(keywords[j][0], keywords[j][1]);
|
line = line.replace(keywords[j][0], keywords[j][1]);
|
||||||
|
@ -374,10 +430,27 @@ export class GBVMService extends GBService {
|
||||||
current = current + (add ? add : 0);
|
current = current + (add ? add : 0);
|
||||||
map[i] = current;
|
map[i] = current;
|
||||||
lines[i - 1] = line;
|
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`;
|
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,
|
step,
|
||||||
user: GuaribasUser,
|
user: GuaribasUser,
|
||||||
deployer: GBDeployer,
|
deployer: GBDeployer,
|
||||||
debug: boolean = false
|
debug: boolean = false,
|
||||||
|
params = []
|
||||||
) {
|
) {
|
||||||
// Creates a class DialogKeywords which is the *this* pointer
|
// Creates a class DialogKeywords which is the *this* pointer
|
||||||
// in BASIC.
|
// 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 botId = min.botId;
|
||||||
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
|
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
|
||||||
const gbdialogPath = urlJoin(process.cwd(), 'work', path);
|
const gbdialogPath = urlJoin(process.cwd(), 'work', path);
|
||||||
const scriptPath = urlJoin(gbdialogPath, `${text}.js`);
|
const scriptPath = urlJoin(gbdialogPath, `${text}.js`);
|
||||||
|
|
||||||
let code = min.sandBoxMap[text];
|
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 pid = GBVMService.createProcessInfo(user, min, channel);
|
||||||
const dk = new DialogKeywords();
|
const dk = new DialogKeywords();
|
||||||
const sys = new SystemKeywords();
|
const sys = new SystemKeywords();
|
||||||
await dk.setFilter ({pid: pid, value: null });
|
await dk.setFilter({ pid: pid, value: null });
|
||||||
|
|
||||||
sandbox['variables'] = variables;
|
sandbox['variables'] = variables;
|
||||||
sandbox['id'] = sys.getRandomId();
|
sandbox['id'] = sys.getRandomId();
|
||||||
|
@ -439,7 +520,7 @@ export class GBVMService extends GBService {
|
||||||
sandbox['httpPs'] = '';
|
sandbox['httpPs'] = '';
|
||||||
sandbox['pid'] = pid;
|
sandbox['pid'] = pid;
|
||||||
sandbox['contentLocale'] = contentLocale;
|
sandbox['contentLocale'] = contentLocale;
|
||||||
sandbox['callTimeout'] = 60 * 60 * 24 * 1000;
|
sandbox['callTimeout'] = 60 * 60 * 24 * 1000;
|
||||||
sandbox['channel'] = channel;
|
sandbox['channel'] = channel;
|
||||||
sandbox['today'] = await dk.getToday({ pid });
|
sandbox['today'] = await dk.getToday({ pid });
|
||||||
sandbox['now'] = await dk.getNow({ pid });
|
sandbox['now'] = await dk.getNow({ pid });
|
||||||
|
@ -489,7 +570,6 @@ export class GBVMService extends GBService {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
|
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -331,7 +331,7 @@ export class KeywordsExpressions {
|
||||||
/^\s*(.*)\=\s*(REWRITE)(\s*)(.*)/gim,
|
/^\s*(.*)\=\s*(REWRITE)(\s*)(.*)/gim,
|
||||||
($0, $1, $2, $3, $4) => {
|
($0, $1, $2, $3, $4) => {
|
||||||
const params = this.getParams($4, ['text']);
|
const params = this.getParams($4, ['text']);
|
||||||
return `await sys.rewrite ({pid: pid, ${params}})`;
|
return `${$1} = await sys.rewrite ({pid: pid, ${params}})`;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -311,7 +311,7 @@ export class GBConversationalService {
|
||||||
caption: string,
|
caption: string,
|
||||||
channel: string
|
channel: string
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return await this.sendFile(min, step, mobile, url , caption);
|
return await this.sendFile(min, step, mobile, url, caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendFile(
|
public async sendFile(
|
||||||
|
@ -393,23 +393,23 @@ export class GBConversationalService {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (min.instance.smsKey && min.instance.smsSecret) {
|
if (min.instance.smsKey && min.instance.smsSecret) {
|
||||||
return new Promise((resolve: any, reject: any): any => {
|
return new Promise((resolve: any, reject: any): any => {
|
||||||
const nexmo = new Nexmo({
|
const nexmo = new Nexmo({
|
||||||
apiKey: min.instance.smsKey,
|
apiKey: min.instance.smsKey,
|
||||||
apiSecret: min.instance.smsSecret
|
apiSecret: min.instance.smsSecret
|
||||||
|
});
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, {}, (err, data) => {
|
||||||
|
const message = data.messages ? data.messages[0] : {};
|
||||||
|
if (err || message['error-text']) {
|
||||||
|
GBLog.error(`BASIC: error sending SMS to ${mobile}: ${message['error-text']}`);
|
||||||
|
reject(message['error-text']);
|
||||||
|
} else {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
// tslint:disable-next-line:no-unsafe-any
|
}
|
||||||
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, {}, (err, data) => {
|
|
||||||
const message = data.messages ? data.messages[0] : {};
|
|
||||||
if (err || message['error-text']) {
|
|
||||||
GBLog.error(`BASIC: error sending SMS to ${mobile}: ${message['error-text']}`);
|
|
||||||
reject(message['error-text']);
|
|
||||||
} else {
|
|
||||||
resolve(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ export class GBConversationalService {
|
||||||
return new Promise<string>(async (resolve, reject) => {
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const oggFile = new Readable();
|
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(buffer);
|
||||||
oggFile.push(null);
|
oggFile.push(null);
|
||||||
|
|
||||||
|
@ -526,8 +526,12 @@ export class GBConversationalService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async playMarkdown(min: GBMinInstance, answer: string, channel: string, step: GBDialogStep, mobile: string) {
|
public async playMarkdown(min: GBMinInstance, answer: string, channel: string,
|
||||||
const user = step ? await min.userProfile.get(step.context, {}) : null;
|
step: GBDialogStep, mobile: string) {
|
||||||
|
|
||||||
|
const sec = new SecService();
|
||||||
|
const user = await sec.getUserFromSystemId(mobile ? mobile : step.context.activity.from.id);
|
||||||
|
|
||||||
let text = answer;
|
let text = answer;
|
||||||
|
|
||||||
// Calls language translator.
|
// Calls language translator.
|
||||||
|
@ -902,7 +906,7 @@ export class GBConversationalService {
|
||||||
const key = min.core.getParam<string>(min.instance, 'spellcheckerKey', null);
|
const key = min.core.getParam<string>(min.instance, 'spellcheckerKey', null);
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
text = text.charAt(0).toUpperCase() + text.slice(1);
|
text = text.charAt(0).toUpperCase() + text.slice(1);
|
||||||
const data = await AzureText.getSpelledText(key, text);
|
const data = await AzureText.getSpelledText(key, text);
|
||||||
if (data !== text) {
|
if (data !== text) {
|
||||||
GBLog.info(`Spelling>: ${data}`);
|
GBLog.info(`Spelling>: ${data}`);
|
||||||
|
@ -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();
|
const sec = new SecService();
|
||||||
|
|
||||||
text = text.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, '');
|
text = text.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, '');
|
||||||
|
@ -1059,7 +1063,13 @@ export class GBConversationalService {
|
||||||
// If it is a group, spells and sends them back.
|
// If it is a group, spells and sends them back.
|
||||||
|
|
||||||
const group = step.context.activity['group'];
|
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}`);
|
await min.whatsAppDirectLine.sendToDevice(group, `Spell: ${text}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,14 +1078,14 @@ export class GBConversationalService {
|
||||||
let locale = min.core.getParam(
|
let locale = min.core.getParam(
|
||||||
min.instance,
|
min.instance,
|
||||||
'Default User Language',
|
'Default User Language',
|
||||||
GBConfigService.get('DEFAULT_USER_LANGUAGE')
|
GBConfigService.get('DEFAULT_USER_LANGUAGE'));
|
||||||
);
|
|
||||||
const detectLanguage =
|
const detectLanguage =
|
||||||
min.core.getParam(
|
min.core.getParam(
|
||||||
min.instance,
|
min.instance,
|
||||||
'Language Detector',
|
'Language Detector',
|
||||||
GBConfigService.getBoolean('LANGUAGE_DETECTOR')
|
false) != false;
|
||||||
) === 'true';
|
|
||||||
locale = user.locale;
|
locale = user.locale;
|
||||||
if (text != '' && detectLanguage && !locale) {
|
if (text != '' && detectLanguage && !locale) {
|
||||||
locale = await min.conversationalService.getLanguage(min, text);
|
locale = await min.conversationalService.getLanguage(min, text);
|
||||||
|
@ -1125,7 +1135,7 @@ export class GBConversationalService {
|
||||||
return await step.prompt('attachmentPrompt', {});
|
return await step.prompt('attachmentPrompt', {});
|
||||||
} else {
|
} else {
|
||||||
await this.sendText(min, step, text);
|
await this.sendText(min, step, text);
|
||||||
return await step.prompt('textPrompt', {});
|
return await step.prompt('textPrompt', {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1221,8 +1231,8 @@ export class GBConversationalService {
|
||||||
*/
|
*/
|
||||||
public async sendOnConversation(min: GBMinInstance, user: GuaribasUser, message: any) {
|
public async sendOnConversation(min: GBMinInstance, user: GuaribasUser, message: any) {
|
||||||
if (message['buttons'] || message['sections']) {
|
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')) {
|
} else if (user.conversationReference.startsWith('spaces')) {
|
||||||
await min['googleDirectLine'].sendToDevice(user.userSystemId, null, user.conversationReference, message);
|
await min['googleDirectLine'].sendToDevice(user.userSystemId, null, user.conversationReference, message);
|
||||||
} else {
|
} else {
|
||||||
const ref = JSON.parse(user.conversationReference);
|
const ref = JSON.parse(user.conversationReference);
|
||||||
|
|
|
@ -433,6 +433,7 @@ ENDPOINT_UPDATE=true
|
||||||
await CollectionUtil.asyncForEach(instances, async instance => {
|
await CollectionUtil.asyncForEach(instances, async instance => {
|
||||||
GBLog.info(`Updating bot endpoint for ${instance.botId}...`);
|
GBLog.info(`Updating bot endpoint for ${instance.botId}...`);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await installationDeployer.updateBotProxy(
|
await installationDeployer.updateBotProxy(
|
||||||
instance.botId,
|
instance.botId,
|
||||||
GBConfigService.get('CLOUD_GROUP'),
|
GBConfigService.get('CLOUD_GROUP'),
|
||||||
|
@ -682,22 +683,20 @@ ENDPOINT_UPDATE=true
|
||||||
value = params ? params[name] : defaultValue;
|
value = params ? params[name] : defaultValue;
|
||||||
}
|
}
|
||||||
if (typeof defaultValue === 'boolean') {
|
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') {
|
if (typeof defaultValue === 'string') {
|
||||||
return value ? value : defaultValue;
|
return value ? value : defaultValue;
|
||||||
}
|
}
|
||||||
if (typeof defaultValue === 'number') {
|
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) {
|
if (instance['dataValues'] && !value) {
|
||||||
value = instance['dataValues'][name];
|
value = instance['dataValues'][name];
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
const minBoot = GBServer.globals.minBoot as any;
|
const minBoot = GBServer.globals.minBoot as any;
|
||||||
if (minBoot.instance && minBoot.instance.datavalues) {
|
value = minBoot.instance[name];
|
||||||
value = minBoot.instance.datavalues[name];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,10 +105,18 @@ export class GBDeployer implements IGBDeployer {
|
||||||
*/
|
*/
|
||||||
public static async internalGetDriveClient(min: GBMinInstance) {
|
public static async internalGetDriveClient(min: GBMinInstance) {
|
||||||
let token;
|
let token;
|
||||||
if (min['cacheToken']) {
|
|
||||||
|
// TODO: Add expiration logic.
|
||||||
|
|
||||||
|
if (min['cacheToken'] && null) {
|
||||||
return min['cacheToken'];
|
return min['cacheToken'];
|
||||||
} else {
|
} 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 siteId = process.env.STORAGE_SITE_ID;
|
||||||
const libraryId = process.env.STORAGE_LIBRARY;
|
const libraryId = process.env.STORAGE_LIBRARY;
|
||||||
|
@ -280,7 +288,6 @@ export class GBDeployer implements IGBDeployer {
|
||||||
`${publicAddress}/api/messages/${instance.botId}`
|
`${publicAddress}/api/messages/${instance.botId}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const botId = GBConfigService.get('BOT_ID');
|
|
||||||
|
|
||||||
// Internally create resources on cloud provider.
|
// Internally create resources on cloud provider.
|
||||||
|
|
||||||
|
@ -303,6 +310,7 @@ export class GBDeployer implements IGBDeployer {
|
||||||
// Makes available bot to the channels and .gbui interfaces.
|
// Makes available bot to the channels and .gbui interfaces.
|
||||||
|
|
||||||
await GBServer.globals.minService.mountBot(instance);
|
await GBServer.globals.minService.mountBot(instance);
|
||||||
|
await GBServer.globals.minService.ensureAPI();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves final instance object and returns it.
|
// Saves final instance object and returns it.
|
||||||
|
@ -553,7 +561,7 @@ export class GBDeployer implements IGBDeployer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public async deployPackage(min: GBMinInstance, localPath: string) {
|
public async deployPackage(min: GBMinInstance, localPath: string) {
|
||||||
// TODO: @alanperdomo: Adjust interface mismatch.
|
// TODO: Adjust interface mismatch.
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Deploys a folder into the bot storage.
|
* 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.
|
* 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);
|
const service = new KBService(this.core.sequelize);
|
||||||
rimraf.sync(localPath);
|
rimraf.sync(localPath);
|
||||||
|
|
||||||
return await service.undeployKbFromStorage(instance, this, p.packageId);
|
if (p){
|
||||||
|
await service.undeployKbFromStorage(instance, this, p.packageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
case '.gbui':
|
case '.gbui':
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -709,19 +744,29 @@ export class GBDeployer implements IGBDeployer {
|
||||||
public async rebuildIndex(instance: IGBInstance, searchSchema: any) {
|
public async rebuildIndex(instance: IGBInstance, searchSchema: any) {
|
||||||
// Prepares search.
|
// Prepares search.
|
||||||
let release;
|
let release;
|
||||||
try {
|
|
||||||
GBLogEx.info(instance.instanceId, `Acquiring rebuildIndex mutex...`);
|
// TODO: Semaphore logic.
|
||||||
release = await GBServer.globals.indexSemaphore.acquire();
|
//try {
|
||||||
GBLogEx.info(instance.instanceId, `Acquire rebuildIndex done.`);
|
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.
|
// Prepares search.
|
||||||
|
|
||||||
const search = new AzureSearch(
|
const search = new AzureSearch(
|
||||||
instance.searchKey,
|
key,
|
||||||
instance.searchHost,
|
host,
|
||||||
instance.searchIndex,
|
searchIndex,
|
||||||
instance.searchIndexer
|
searchIndexer
|
||||||
);
|
);
|
||||||
const connectionString = GBDeployer.getConnectionStringFromInstance(instance);
|
const connectionString = GBDeployer.getConnectionStringFromInstance(GBServer.globals.minBoot.instance);
|
||||||
const dsName = 'gb';
|
const dsName = 'gb';
|
||||||
|
|
||||||
// Removes any previous index.
|
// Removes any previous index.
|
||||||
|
@ -758,13 +803,13 @@ export class GBDeployer implements IGBDeployer {
|
||||||
}
|
}
|
||||||
await search.createIndex(searchSchema, dsName);
|
await search.createIndex(searchSchema, dsName);
|
||||||
|
|
||||||
release();
|
// release();
|
||||||
GBLogEx.info(instance.instanceId, `Released rebuildIndex mutex.`);
|
GBLogEx.info(instance.instanceId, `Released rebuildIndex mutex.`);
|
||||||
} catch {
|
//} catch {
|
||||||
if (release) {
|
// if (release) {
|
||||||
release();
|
// release();
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -60,10 +60,18 @@ export class GBImporter {
|
||||||
localPath: string,
|
localPath: string,
|
||||||
additionalInstance: IGBInstance = null
|
additionalInstance: IGBInstance = null
|
||||||
) {
|
) {
|
||||||
const settingsJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
|
const file = urlJoin(localPath, 'settings.json');
|
||||||
if (botId === undefined) {
|
|
||||||
botId = settingsJson.botId;
|
let settingsJson = {botId: botId};
|
||||||
|
if (Fs.existsSync(file)){
|
||||||
|
|
||||||
|
settingsJson = JSON.parse(Fs.readFileSync(file, 'utf8'));
|
||||||
|
if (botId === undefined) {
|
||||||
|
botId = settingsJson.botId;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let instance: IGBInstance;
|
let instance: IGBInstance;
|
||||||
if (botId === undefined) {
|
if (botId === undefined) {
|
||||||
botId = GBConfigService.get('BOT_ID');
|
botId = GBConfigService.get('BOT_ID');
|
||||||
|
@ -111,10 +119,10 @@ export class GBImporter {
|
||||||
localPath: string,
|
localPath: string,
|
||||||
settingsJson: any
|
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) {
|
if (botId !== undefined) {
|
||||||
fullSettingsJson.botId = botId;
|
fullSettingsJson.botId = botId;
|
||||||
|
|
|
@ -148,6 +148,11 @@ export class GBMinService {
|
||||||
this.deployer = deployer;
|
this.deployer = deployer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async enableAPI(min: GBMinInstance) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new minimal instance for each bot.
|
* 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));
|
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.
|
// Calls mountBot event to all bots.
|
||||||
|
|
||||||
let i = 1;
|
let i = 1;
|
||||||
|
|
||||||
if (instances.length > 1) {
|
if (instances.length > 1) {
|
||||||
|
@ -245,6 +207,10 @@ export class GBMinService {
|
||||||
this.bar1.stop();
|
this.bar1.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loads API.
|
||||||
|
|
||||||
|
await this.ensureAPI();
|
||||||
|
|
||||||
// Loads schedules.
|
// Loads schedules.
|
||||||
|
|
||||||
GBLog.info(`Loading SET SCHEDULE entries...`);
|
GBLog.info(`Loading SET SCHEDULE entries...`);
|
||||||
|
@ -292,7 +258,7 @@ export class GBMinService {
|
||||||
/**
|
/**
|
||||||
* Unmounts the bot web site (default.gbui) secure domain, if any.
|
* 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,
|
* Mount the instance by creating an BOT Framework bot object,
|
||||||
|
@ -542,7 +508,7 @@ export class GBMinService {
|
||||||
|
|
||||||
authenticationContext.acquireTokenWithAuthorizationCode(
|
authenticationContext.acquireTokenWithAuthorizationCode(
|
||||||
req.query.code,
|
req.query.code,
|
||||||
urlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
urlJoin(process.env.BOT_URL, min.instance.botId, '/token'),
|
||||||
resource,
|
resource,
|
||||||
instance.marketplaceId,
|
instance.marketplaceId,
|
||||||
instance.marketplacePassword,
|
instance.marketplacePassword,
|
||||||
|
@ -561,7 +527,7 @@ export class GBMinService {
|
||||||
|
|
||||||
// Inform the home for default .gbui after finishing token retrival.
|
// 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,
|
min.instance.authenticatorTenant,
|
||||||
'/oauth2/authorize'
|
'/oauth2/authorize'
|
||||||
);
|
);
|
||||||
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
|
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId
|
||||||
min.instance.marketplaceId
|
}&redirect_uri=${urlJoin(process.env.BOT_URL, min.instance.botId, 'token')}`;
|
||||||
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
|
|
||||||
GBLog.info(`HandleOAuthRequests: ${authorizationUrl}.`);
|
GBLog.info(`HandleOAuthRequests: ${authorizationUrl}.`);
|
||||||
res.redirect(authorizationUrl);
|
res.redirect(authorizationUrl);
|
||||||
});
|
});
|
||||||
|
@ -772,6 +737,8 @@ export class GBMinService {
|
||||||
|
|
||||||
WhatsappDirectLine.botGroups[min.botId] = group;
|
WhatsappDirectLine.botGroups[min.botId] = group;
|
||||||
|
|
||||||
|
const minBoot = GBServer.globals.minBoot as any;
|
||||||
|
|
||||||
// If there is WhatsApp configuration specified, initialize
|
// If there is WhatsApp configuration specified, initialize
|
||||||
// infrastructure objects.
|
// infrastructure objects.
|
||||||
|
|
||||||
|
@ -788,7 +755,6 @@ export class GBMinService {
|
||||||
|
|
||||||
await min.whatsAppDirectLine.setup(true);
|
await min.whatsAppDirectLine.setup(true);
|
||||||
} else {
|
} else {
|
||||||
const minBoot = GBServer.globals.minBoot as any;
|
|
||||||
if (min !== minBoot && minBoot.instance.whatsappServiceKey) {
|
if (min !== minBoot && minBoot.instance.whatsappServiceKey) {
|
||||||
min.whatsAppDirectLine = new WhatsappDirectLine(
|
min.whatsAppDirectLine = new WhatsappDirectLine(
|
||||||
min,
|
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.
|
// Setups default BOT Framework dialogs.
|
||||||
|
|
||||||
min.userProfile = conversationState.createProperty('userProfile');
|
min.userProfile = conversationState.createProperty('userProfile');
|
||||||
|
@ -1069,9 +1042,8 @@ export class GBMinService {
|
||||||
await this.processEventActivity(min, user, context, step);
|
await this.processEventActivity(min, user, context, step);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
|
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
|
||||||
error.error ? (error.error.stack ? error.error.stack : '') : ''
|
}`;
|
||||||
}`;
|
|
||||||
GBLog.error(msg);
|
GBLog.error(msg);
|
||||||
|
|
||||||
await min.conversationalService.sendText(
|
await min.conversationalService.sendText(
|
||||||
|
@ -1176,8 +1148,7 @@ export class GBMinService {
|
||||||
return utterance.match(Messages.global_quit);
|
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.
|
// Prepare Promises to download each attachment and then execute each Promise.
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1194,7 +1165,7 @@ export class GBMinService {
|
||||||
// In case of not having HEAR activated before, it is
|
// In case of not having HEAR activated before, it is
|
||||||
// a upload with no Dialog, so run Auto Save to .gbdrive.
|
// 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();
|
const t = new SystemKeywords();
|
||||||
GBLog.info(`BASIC (${min.botId}): Upload done for ${attachmentData.fileName}.`);
|
GBLog.info(`BASIC (${min.botId}): Upload done for ${attachmentData.fileName}.`);
|
||||||
const handle = WebAutomationServices.cyrb53(min.botId + attachmentData.fileName);
|
const handle = WebAutomationServices.cyrb53(min.botId + attachmentData.fileName);
|
||||||
|
@ -1326,11 +1297,12 @@ export class GBMinService {
|
||||||
context.activity.text
|
context.activity.text
|
||||||
);
|
);
|
||||||
|
|
||||||
const conversationReference = JSON.stringify(TurnContext.getConversationReference(context.activity));
|
|
||||||
await sec.updateConversationReferenceById(userId, conversationReference);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const conversationReference = JSON.stringify(TurnContext.getConversationReference(context.activity));
|
||||||
|
await sec.updateConversationReferenceById(userId, conversationReference);
|
||||||
|
|
||||||
if (GBMinService.userMobile(step)) {
|
if (GBMinService.userMobile(step)) {
|
||||||
const startDialog = user.hearOnDialog ? user.hearOnDialog : min.core.getParam(min.instance, 'Start Dialog', null);
|
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 .
|
// 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['originalText']
|
||||||
step.context.activity['text'] = text;
|
step.context.activity['text'] = text;
|
||||||
|
|
||||||
const notes = min.core.getParam(min.instance, 'Notes', null);
|
|
||||||
if (notes && text && text !== "") {
|
if (notes && text && text !== "") {
|
||||||
const sys = new SystemKeywords();
|
const sys = new SystemKeywords();
|
||||||
await sys.note({pid, text});
|
await sys.note({ pid, text });
|
||||||
await step.context.sendActivity('OK.');
|
await step.context.sendActivity('OK.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1459,9 +1432,8 @@ export class GBMinService {
|
||||||
try {
|
try {
|
||||||
await step.continueDialog();
|
await step.continueDialog();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
|
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
|
||||||
error.error ? (error.error.stack ? error.error.stack : '') : ''
|
}`;
|
||||||
}`;
|
|
||||||
GBLog.error(msg);
|
GBLog.error(msg);
|
||||||
await min.conversationalService.sendText(
|
await min.conversationalService.sendText(
|
||||||
min,
|
min,
|
||||||
|
@ -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
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -33,9 +33,57 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { GBMinInstance } from 'botlib';
|
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 {
|
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
|
* Generate text
|
||||||
*
|
*
|
||||||
|
@ -45,22 +93,25 @@ export class ChatServices {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static async continue(min: GBMinInstance, text: string, chatId) {
|
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) {
|
if (!key) {
|
||||||
throw new Error('Open AI Key not configured in .gbot.');
|
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,
|
// return chatCompletion.choices[0].message.content;
|
||||||
});
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,8 +108,14 @@ export class AskDialog extends IGBDialog {
|
||||||
text = Messages[locale].ask_first_time;
|
text = Messages[locale].ask_first_time;
|
||||||
} else if (step.options && step.options.isReturning && !step.context.activity.group) {
|
} else if (step.options && step.options.isReturning && !step.context.activity.group) {
|
||||||
const askForMore = min.core.getParam(min.instance, 'Ask For More', null);
|
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)) {
|
} else if (step.context.activity.group || (step.options && step.options.emptyPrompt)) {
|
||||||
return await step.next();
|
return await step.next();
|
||||||
} else if (user.subjects.length > 0) {
|
} else if (user.subjects.length > 0) {
|
||||||
|
|
|
@ -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.
|
// 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 {
|
interface SearchResults {
|
||||||
instanceId: number;
|
instanceId: number;
|
||||||
questionId: number;
|
questionId: number;
|
||||||
|
@ -303,11 +308,11 @@ export class KBService implements IGBKBService {
|
||||||
subject4: string;
|
subject4: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new SearchClient<any>('https://' + instance.searchHost, 'azuresql-index', {
|
const client = new SearchClient<any>('https://' + host, 'azuresql-index', {
|
||||||
key: instance.searchKey
|
key: key
|
||||||
} as any);
|
} 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`,
|
filter: `instanceId eq ${instance.instanceId} and skipIndex eq false`,
|
||||||
searchFields: ['content', 'subject1', 'subject2', 'subject3', 'subject4'],
|
searchFields: ['content', 'subject1', 'subject2', 'subject3', 'subject4'],
|
||||||
select: ['instanceId', 'questionId', 'answerId'],
|
select: ['instanceId', 'questionId', 'answerId'],
|
||||||
|
@ -662,6 +667,8 @@ export class KBService implements IGBKBService {
|
||||||
const subjectFile = urlJoin(localPath, 'subjects.json');
|
const subjectFile = urlJoin(localPath, 'subjects.json');
|
||||||
const menuFile = urlJoin(localPath, 'menu.xlsx');
|
const menuFile = urlJoin(localPath, 'menu.xlsx');
|
||||||
|
|
||||||
|
// Imports menu.xlsx if any.
|
||||||
|
|
||||||
if (Fs.existsSync(subjectFile) || Fs.existsSync(menuFile)) {
|
if (Fs.existsSync(subjectFile) || Fs.existsSync(menuFile)) {
|
||||||
await this.importSubjectFile(packageStorage.packageId, subjectFile, menuFile, instance);
|
await this.importSubjectFile(packageStorage.packageId, subjectFile, menuFile, instance);
|
||||||
}
|
}
|
||||||
|
@ -1012,11 +1019,13 @@ export class KBService implements IGBKBService {
|
||||||
const packageName = Path.basename(localPath);
|
const packageName = Path.basename(localPath);
|
||||||
const instance = await core.loadInstanceByBotId(min.botId);
|
const instance = await core.loadInstanceByBotId(min.botId);
|
||||||
GBLog.info(`[GBDeployer] Importing: ${localPath}`);
|
GBLog.info(`[GBDeployer] Importing: ${localPath}`);
|
||||||
|
|
||||||
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
|
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
|
||||||
await this.importKbPackage(min, localPath, p, instance);
|
await this.importKbPackage(min, localPath, p, instance);
|
||||||
GBDeployer.mountGBKBAssets(packageName, min.botId, localPath);
|
GBDeployer.mountGBKBAssets(packageName, min.botId, localPath);
|
||||||
const service = await AzureDeployerService.createInstance(deployer);
|
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);
|
min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
|
||||||
await KBService.RefreshNER(min);
|
await KBService.RefreshNER(min);
|
||||||
|
|
|
@ -51,6 +51,7 @@ import pkg from 'whatsapp-web.js';
|
||||||
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
|
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
|
||||||
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
|
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
|
||||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
|
||||||
|
import e from 'express';
|
||||||
const { List, Buttons, Client, MessageMedia } = pkg;
|
const { List, Buttons, Client, MessageMedia } = pkg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +59,7 @@ const { List, Buttons, Client, MessageMedia } = pkg;
|
||||||
*/
|
*/
|
||||||
export class WhatsappDirectLine extends GBService {
|
export class WhatsappDirectLine extends GBService {
|
||||||
public static conversationIds = {};
|
public static conversationIds = {};
|
||||||
|
public static botsByNumber = {};
|
||||||
public static mobiles = {};
|
public static mobiles = {};
|
||||||
public static phones = {};
|
public static phones = {};
|
||||||
public static chatIds = {};
|
public static chatIds = {};
|
||||||
|
@ -121,7 +123,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
const client = await new SwaggerClient({
|
const client = await new SwaggerClient({
|
||||||
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
|
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
|
||||||
requestInterceptor: req => {
|
requestInterceptor: req => {
|
||||||
req.headers['Authorization'] = `Bearer ${this.min.instance.webchatKey}`;
|
req.headers['Authorization'] = `Bearer ${this.min.instance.whatsappBotKey}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.directLineClient = client;
|
this.directLineClient = client;
|
||||||
|
@ -197,8 +199,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
};
|
};
|
||||||
if (setUrl) {
|
if (setUrl) {
|
||||||
createClient.bind(this)();
|
createClient.bind(this)();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.customClient = minBoot.whatsAppDirectLine.customClient;
|
this.customClient = minBoot.whatsAppDirectLine.customClient;
|
||||||
}
|
}
|
||||||
setUrl = false;
|
setUrl = false;
|
||||||
|
@ -303,7 +304,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
public async received(req, res) {
|
public async received(req, res) {
|
||||||
const provider = WhatsappDirectLine.providerFromRequest(req);
|
const provider = WhatsappDirectLine.providerFromRequest(req);
|
||||||
|
|
||||||
let message, from, fromName, text: string;
|
let message, to, from, fromName, text: string;
|
||||||
let group = '';
|
let group = '';
|
||||||
let answerText = null;
|
let answerText = null;
|
||||||
let attachments = null;
|
let attachments = null;
|
||||||
|
@ -311,6 +312,18 @@ export class WhatsappDirectLine extends GBService {
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case 'GeneralBots':
|
case 'GeneralBots':
|
||||||
message = req;
|
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;
|
text = message.body;
|
||||||
from = message.from.endsWith('@g.us') ? message.author.split('@')[0] : message.from.split('@')[0];
|
from = message.from.endsWith('@g.us') ? message.author.split('@')[0] : message.from.split('@')[0];
|
||||||
fromName = message._data.notifyName;
|
fromName = message._data.notifyName;
|
||||||
|
@ -318,9 +331,14 @@ export class WhatsappDirectLine extends GBService {
|
||||||
if (message.hasMedia) {
|
if (message.hasMedia) {
|
||||||
const base64Image = await message.downloadMedia();
|
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 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 });
|
Fs.writeFileSync(localName, buf, { encoding: null });
|
||||||
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName));
|
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.
|
// Bot name must be specified on config.
|
||||||
|
|
||||||
if (botGroupID === group) {
|
if (botGroupID === group) {
|
||||||
|
|
||||||
// Shortcut has been mentioned?
|
// Shortcut has been mentioned?
|
||||||
|
|
||||||
let found = false;
|
let found = false;
|
||||||
parts.forEach(e1 => {
|
parts.forEach(e1 => {
|
||||||
|
|
||||||
botShortcuts.forEach(e2 => {
|
botShortcuts.forEach(e2 => {
|
||||||
if (e1 === e2 && !found) {
|
if (e1 === e2 && !found) {
|
||||||
found = true;
|
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)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ignore group messages without the mention to Bot.
|
||||||
|
|
||||||
|
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) {
|
if (user.agentSystemId.indexOf('@') !== -1) {
|
||||||
|
|
||||||
// Agent is from Teams.
|
// Agent is from Teams.
|
||||||
await this.min.conversationalService['sendOnConversation'](
|
await this.min.conversationalService['sendOnConversation'](
|
||||||
this.min,
|
this.min,
|
||||||
|
@ -566,7 +586,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
|
|
||||||
const prompt = `the person said: ${text}. what can I tell her?`;
|
const prompt = `the person said: ${text}. what can I tell her?`;
|
||||||
const answer = await ChatServices.continue(this.min, prompt, 0);
|
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) {
|
if (user.agentSystemId.indexOf('@') !== -1) {
|
||||||
// Agent is from Teams or Google Chat.
|
// 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) {
|
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);
|
await this.sendToDeviceEx(id, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
|
||||||
|
|
||||||
if (user.agentSystemId.indexOf('@') !== -1) {
|
if (user.agentSystemId.indexOf('@') !== -1) {
|
||||||
|
|
||||||
// Agent is from Teams.
|
// Agent is from Teams.
|
||||||
|
|
||||||
await this.min.conversationalService['sendOnConversation'](
|
await this.min.conversationalService['sendOnConversation'](
|
||||||
|
@ -621,9 +639,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
agent,
|
agent,
|
||||||
Messages[this.locale].notify_end_transfer(this.min.instance.botId)
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId)
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
await this.sendToDeviceEx(
|
await this.sendToDeviceEx(
|
||||||
user.agentSystemId,
|
user.agentSystemId,
|
||||||
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
|
||||||
|
@ -712,15 +728,13 @@ export class WhatsappDirectLine extends GBService {
|
||||||
|
|
||||||
if (activity.attachments) {
|
if (activity.attachments) {
|
||||||
await CollectionUtil.asyncForEach(activity.attachments, async attachment => {
|
await CollectionUtil.asyncForEach(activity.attachments, async attachment => {
|
||||||
|
|
||||||
switch (attachment.contentType) {
|
switch (attachment.contentType) {
|
||||||
case 'application/vnd.microsoft.card.hero':
|
case 'application/vnd.microsoft.card.hero':
|
||||||
output += `\n${this.renderHeroCard(attachment)}`;
|
output += `\n${this.renderHeroCard(attachment)}`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'image/png':
|
case 'image/png':
|
||||||
await this.sendFileToDevice(to, attachment.contentUrl,
|
await this.sendFileToDevice(to, attachment.contentUrl, attachment.name, attachment.name, 0);
|
||||||
attachment.name, attachment.name, 0);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -742,6 +756,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
switch (this.provider) {
|
switch (this.provider) {
|
||||||
case 'GeneralBots':
|
case 'GeneralBots':
|
||||||
const attachment = await MessageMedia.fromUrl(url);
|
const attachment = await MessageMedia.fromUrl(url);
|
||||||
|
to = to.replace('+', '');
|
||||||
if (to.indexOf('@') == -1) {
|
if (to.indexOf('@') == -1) {
|
||||||
if (to.length == 18) {
|
if (to.length == 18) {
|
||||||
to = to + '@g.us';
|
to = to + '@g.us';
|
||||||
|
@ -885,6 +900,7 @@ export class WhatsappDirectLine extends GBService {
|
||||||
|
|
||||||
switch (this.provider) {
|
switch (this.provider) {
|
||||||
case 'GeneralBots':
|
case 'GeneralBots':
|
||||||
|
to = to.replace('+', '');
|
||||||
if (to.indexOf('@') == -1) {
|
if (to.indexOf('@') == -1) {
|
||||||
if (to.length == 18) {
|
if (to.length == 18) {
|
||||||
to = to + '@g.us';
|
to = to + '@g.us';
|
||||||
|
@ -963,11 +979,9 @@ export class WhatsappDirectLine extends GBService {
|
||||||
|
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case 'GeneralBots':
|
case 'GeneralBots':
|
||||||
|
// Ignore E2E messages and status updates.
|
||||||
|
|
||||||
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,16 +1032,25 @@ export class WhatsappDirectLine extends GBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const sec = new SecService();
|
const sec = new SecService();
|
||||||
|
|
||||||
let user = await sec.getUserFromSystemId(id);
|
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}.`);
|
GBLog.info(`A WhatsApp mobile requested instance for: ${botId}.`);
|
||||||
|
|
||||||
let urlMin: any = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
|
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;
|
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);
|
user = await sec.updateUserInstance(user.userSystemId, urlMin.instance.instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let activeMin;
|
let activeMin;
|
||||||
|
|
||||||
// Processes group behaviour.
|
// Processes group behaviour.
|
||||||
|
@ -1071,34 +1094,81 @@ export class WhatsappDirectLine extends GBService {
|
||||||
|
|
||||||
// Detects if the welcome message is enabled.
|
// Detects if the welcome message is enabled.
|
||||||
|
|
||||||
if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') {
|
if (process.env.WHATSAPP_WELCOME_DISABLED === 'true') {
|
||||||
// Tries to find if user wants to switch bots.
|
let minInstance = GBServer.globals.minInstances.filter(
|
||||||
|
p => p.instance.botId.toLowerCase() === botId.toLowerCase()
|
||||||
let toSwitchMin = GBServer.globals.minInstances.filter(
|
|
||||||
p => p.instance.botId.toLowerCase() === text.toLowerCase()
|
|
||||||
)[0];
|
)[0];
|
||||||
if (!toSwitchMin) {
|
|
||||||
toSwitchMin = GBServer.globals.minInstances.filter(p =>
|
// Just pass the message to the receiver.
|
||||||
p.instance.activationCode ? p.instance.activationCode.toLowerCase() === text.toLowerCase() : false
|
|
||||||
)[0];
|
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
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If bot has a fixed Find active bot instance.
|
||||||
|
|
||||||
|
activeMin = botNumber ? urlMin : toSwitchMin ? toSwitchMin : GBServer.globals.minBoot;
|
||||||
|
|
||||||
|
// If it is the first time for the user, tries to auto-execute
|
||||||
|
// start dialog if any is specified in Config.xlsx.
|
||||||
|
|
||||||
|
if (user === null || user.hearOnDialog) {
|
||||||
|
user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null);
|
||||||
|
|
||||||
|
const startDialog = user.hearOnDialog
|
||||||
|
? user.hearOnDialog
|
||||||
|
: activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
|
||||||
|
|
||||||
|
if (startDialog) {
|
||||||
|
GBLog.info(`Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`);
|
||||||
|
if (provider === 'chatapi') {
|
||||||
|
req.body.messages[0].body = `/start`;
|
||||||
|
} else if (provider === 'maytapi') {
|
||||||
|
req.body.message = `/start`;
|
||||||
|
} else {
|
||||||
|
req.body = `/start`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets HEAR ON DIALOG value to none and passes
|
||||||
|
// current dialog to the direct line.
|
||||||
|
|
||||||
|
await sec.updateUserHearOnDialog(user.userId, null);
|
||||||
|
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
||||||
|
} else {
|
||||||
|
await (activeMin as any).whatsAppDirectLine.sendToDevice(
|
||||||
|
id,
|
||||||
|
`Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.`,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
if (res) {
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// User wants to switch bots.
|
||||||
|
|
||||||
// If bot has a fixed Find active bot instance.
|
if (toSwitchMin) {
|
||||||
|
GBLog.info(`Switching bots from ${botId} to ${toSwitchMin.botId}...`);
|
||||||
|
|
||||||
activeMin = botNumber ? urlMin : toSwitchMin ? toSwitchMin : GBServer.globals.minBoot;
|
// So gets the new bot instance information and prepares to
|
||||||
|
// auto start dialog if any is specified.
|
||||||
|
|
||||||
// If it is the first time for the user, tries to auto-execute
|
activeMin = toSwitchMin;
|
||||||
// start dialog if any is specified in Config.xlsx.
|
const instance = await this.min.core.loadInstanceByBotId(activeMin.botId);
|
||||||
|
user = await sec.updateUserInstance(id, instance.instanceId);
|
||||||
if (user === null || user.hearOnDialog) {
|
await (activeMin as any).whatsAppDirectLine.resetConversationId(activeMin.botId, id, '');
|
||||||
user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null);
|
const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
|
||||||
|
|
||||||
const startDialog = user.hearOnDialog
|
|
||||||
? user.hearOnDialog
|
|
||||||
: activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
|
|
||||||
|
|
||||||
if (startDialog) {
|
if (startDialog) {
|
||||||
GBLog.info(`Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`);
|
|
||||||
|
GBLog.info(`Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`);
|
||||||
if (provider === 'chatapi') {
|
if (provider === 'chatapi') {
|
||||||
req.body.messages[0].body = `/start`;
|
req.body.messages[0].body = `/start`;
|
||||||
} else if (provider === 'maytapi') {
|
} else if (provider === 'maytapi') {
|
||||||
|
@ -1107,86 +1177,40 @@ export class WhatsappDirectLine extends GBService {
|
||||||
req.body = `/start`;
|
req.body = `/start`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets HEAR ON DIALOG value to none and passes
|
|
||||||
// current dialog to the direct line.
|
|
||||||
|
|
||||||
await sec.updateUserHearOnDialog(user.userId, null);
|
|
||||||
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
||||||
} else {
|
} else {
|
||||||
await (activeMin as any).whatsAppDirectLine.sendToDevice(
|
await (activeMin as any).whatsAppDirectLine.sendToDevice(
|
||||||
id,
|
id,
|
||||||
`Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.`,
|
`Agora falando com ${activeMin.instance.title}...`,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
if (res) {
|
}
|
||||||
res.end();
|
if (res) {
|
||||||
}
|
res.end();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// User wants to switch bots.
|
let t;
|
||||||
|
activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0];
|
||||||
if (toSwitchMin !== undefined) {
|
if (activeMin === undefined) {
|
||||||
// So gets the new bot instance information and prepares to
|
activeMin = GBServer.globals.minBoot;
|
||||||
// auto start dialog if any is specified.
|
t = (activeMin as any).whatsAppDirectLine;
|
||||||
|
await t.sendToDevice(
|
||||||
const instance = await this.min.core.loadInstanceByBotId(activeMin.botId);
|
id,
|
||||||
await sec.updateUserInstance(id, instance.instanceId);
|
`O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...`
|
||||||
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`;
|
|
||||||
} else if (provider === 'maytapi') {
|
|
||||||
req.body.message = `/start`;
|
|
||||||
} else {
|
|
||||||
req.body = `/start`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
|
||||||
} else {
|
|
||||||
await (activeMin as any).whatsAppDirectLine.sendToDevice(
|
|
||||||
id,
|
|
||||||
`Agora falando com ${activeMin.instance.title}...`,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (res) {
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let t;
|
if ((activeMin as any).whatsAppDirectLine) {
|
||||||
activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0];
|
|
||||||
if (activeMin === undefined) {
|
|
||||||
activeMin = GBServer.globals.minBoot;
|
|
||||||
t = (activeMin as any).whatsAppDirectLine;
|
t = (activeMin as any).whatsAppDirectLine;
|
||||||
await t.sendToDevice(
|
} else {
|
||||||
id,
|
t = (GBServer.globals.minBoot as any).whatsAppDirectLine;
|
||||||
`O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if ((activeMin as any).whatsAppDirectLine) {
|
|
||||||
t = (activeMin as any).whatsAppDirectLine;
|
|
||||||
} else {
|
|
||||||
t = (GBServer.globals.minBoot as any).whatsAppDirectLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
} catch (error) {
|
||||||
|
error = error['e'] ? error['e'] : error;
|
||||||
GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error} ${error.stack}`);
|
GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error} ${error.stack}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ export class RootData {
|
||||||
public publicAddress: string; // URI for BotServer.
|
public publicAddress: string; // URI for BotServer.
|
||||||
public server: any; // Express reference.
|
public server: any; // Express reference.
|
||||||
public httpsServer: any; // Express reference (HTTPS).
|
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 sysPackages: any[]; // Loaded system package list.
|
||||||
public appPackages: any[]; // Loaded .gbapp package list.
|
public appPackages: any[]; // Loaded .gbapp package list.
|
||||||
public minService: GBMinService; // Minimalist service core.
|
public minService: GBMinService; // Minimalist service core.
|
||||||
|
|
|
@ -59,7 +59,6 @@ import * as winston from 'winston-logs-display';
|
||||||
import { RootData } from './RootData.js';
|
import { RootData } from './RootData.js';
|
||||||
import { GBSSR } from '../packages/core.gbapp/services/GBSSR.js';
|
import { GBSSR } from '../packages/core.gbapp/services/GBSSR.js';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import { GBVMService } from '../packages/basic.gblib/services/GBVMService.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General Bots open-core entry point.
|
* General Bots open-core entry point.
|
||||||
|
@ -77,6 +76,8 @@ export class GBServer {
|
||||||
GBConfigService.init();
|
GBConfigService.init();
|
||||||
const port = GBConfigService.getServerPort();
|
const port = GBConfigService.getServerPort();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (process.env.TEST_SHELL) {
|
if (process.env.TEST_SHELL) {
|
||||||
GBLog.info(`Running TEST_SHELL: ${process.env.TEST_SHELL}...`);
|
GBLog.info(`Running TEST_SHELL: ${process.env.TEST_SHELL}...`);
|
||||||
try {
|
try {
|
||||||
|
@ -108,6 +109,7 @@ export class GBServer {
|
||||||
// Setups global error handlers.
|
// Setups global error handlers.
|
||||||
|
|
||||||
process.on('unhandledRejection', (err, p) => {
|
process.on('unhandledRejection', (err, p) => {
|
||||||
|
err = err['e'] ? err['e'] : err;
|
||||||
GBLog.error(`UNHANDLED_REJECTION(promises): ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`);
|
GBLog.error(`UNHANDLED_REJECTION(promises): ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`);
|
||||||
if (err['response']?.obj?.httpStatusCode === 404) {
|
if (err['response']?.obj?.httpStatusCode === 404) {
|
||||||
GBLog.warn(`Check reverse proxy: ${process.env.BOT_URL} as it seems to be invalid.`);
|
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) => {
|
process.on('uncaughtException', (err, p) => {
|
||||||
if (err !== null) {
|
if (err !== null) {
|
||||||
|
err = err['e'] ? err['e'] : err;
|
||||||
GBLog.error(`UNCAUGHT_EXCEPTION: ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`);
|
GBLog.error(`UNCAUGHT_EXCEPTION: ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`);
|
||||||
} else {
|
} else {
|
||||||
GBLog.error('UNCAUGHT_EXCEPTION: Unknown error (err is null)');
|
GBLog.error('UNCAUGHT_EXCEPTION: Unknown error (err is null)');
|
||||||
|
@ -136,6 +139,7 @@ export class GBServer {
|
||||||
|
|
||||||
const mainCallback = () => {
|
const mainCallback = () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GBLog.info(`Now accepting connections on ${port}...`);
|
GBLog.info(`Now accepting connections on ${port}...`);
|
||||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||||
|
|
Loading…
Add table
Reference in a new issue