This commit is contained in:
henrique 2024-08-23 17:21:53 -03:00
commit d319782679
185 changed files with 628075 additions and 1907 deletions

1
.gitignore vendored
View file

@ -29,3 +29,4 @@ package-lock.json
yarn-lock.json
logo.svg
screenshot.png
data.db

47
.test-init.ts Normal file
View file

@ -0,0 +1,47 @@
import { expect, test } from 'vitest';
import { GBServer } from './src/app';
import { RootData } from './src/RootData';
import { GBMinInstance } from 'botlib';
import { Mutex } from 'async-mutex';
export default function init() {
const min = {
packages: null,
appPackages: null,
botId: 'gbtest',
instance: {botId: 'gbtest'},
core: {},
conversationalService: {},
kbService: {},
adminService: {},
deployService: {},
textServices: {},
bot: {},
dialogs: {},
userState: {},
userProfile: {},
whatsAppDirectLine: {},
cbMap: {},
scriptMap: {},
sandBoxMap: {},
gbappServices: {}
}
GBServer.globals = new RootData();
GBServer.globals.server = null;
GBServer.globals.httpsServer = null;
GBServer.globals.webSessions = {};
GBServer.globals.processes = [0, { pid: 1, proc: {step: {}}}];
GBServer.globals.files = {};
GBServer.globals.appPackages = [];
GBServer.globals.sysPackages = [];
GBServer.globals.minInstances = [min];
GBServer.globals.minBoot = min;
GBServer.globals.wwwroot = null;
GBServer.globals.entryPointDialog = null;
GBServer.globals.debuggers = [];
GBServer.globals.indexSemaphore = new Mutex();
GBServer.globals.users = {1: {userId: 1}};
}

View file

@ -8,7 +8,7 @@ import pjson from './package.json' assert { type: 'json' };
// Displays version of Node JS being used at runtime and others attributes.
process.stdout.write(`General Bots. BotServer@${pjson.version}, botlib@${pjson.dependencies.botlib}, botbuilder@${pjson.dependencies.botbuilder}, node@${process.version.replace('v', '')}, ${process.platform} ${process.arch} `);
process.stdout.write(`General Bots. BotServer@${pjson.version}, botlib@${pjson.dependencies.botlib}, node@${process.version.replace('v', '')}, ${process.platform} ${process.arch} `);
console.log(`\nLoading virtual machine source code files...`);
var __dirname = process.env.PWD || process.cwd();

View file

@ -76,7 +76,7 @@
"@azure/keyvault-keys": "4.8.0",
"@azure/ms-rest-js": "2.7.0",
"@azure/msal-node": "2.8.1",
"@azure/openai": "^2.0.0-beta.1",
"@azure/openai": "2.0.0-beta.1",
"@azure/search-documents": "12.0.0",
"@azure/storage-blob": "12.18.0",
"@google-cloud/pubsub": "4.4.0",
@ -137,11 +137,12 @@
"google-libphonenumber": "3.2.34",
"googleapis": "126.0.1",
"hnswlib-node": "3.0.0",
"html-to-md": "^0.8.5",
"html-to-md": "0.8.5",
"http-proxy": "1.18.1",
"ibm-watson": "9.1.0",
"instagram-private-api": "^1.46.1",
"instagram-private-api": "1.46.1",
"iso-639-1": "3.1.2",
"isomorphic-fetch": "3.0.0",
"join-images-updated": "1.1.11",
"js-md5": "0.8.3",
"json-schema-to-zod": "2.1.0",
@ -210,7 +211,7 @@
"textract": "2.5.0",
"twilio": "5.1.0",
"twitter-api-v2": "1.17.0",
"typeorm": "^0.3.20",
"typeorm": "0.3.20",
"typescript": "5.4.5",
"url-join": "5.0.0",
"vhost": "3.0.2",
@ -218,6 +219,7 @@
"vm2-process": "2.1.5",
"walk-promise": "0.2.0",
"washyourmouthoutwithsoap": "1.0.2",
"webdav-server": "2.6.2",
"whatsapp-cloud-api": "0.3.1",
"whatsapp-web.js": "https://github.com/Julzk/whatsapp-web.js/tarball/jkr_hotfix_7",
"winston": "3.13.0",

View file

@ -313,7 +313,7 @@ export class AdminDialog extends IGBDialog {
}
if (packageName.indexOf('.') !== -1) {
cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${process.env.STORAGE_LIBRARY}/${botId}.gbai/${packageName}`;
cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${GBConfigService.get('STORAGE_LIBRARY')}/${botId}.gbai/${packageName}`;
} else {
cmd1 = `deployPackage ${packageName}`;
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -1,5 +1,3 @@
/*****************************************************************************\
| ® |
| |
@ -7,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -23,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -49,13 +47,14 @@ import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointS
import { GuaribasAdmin } from '../models/AdminModel.js';
import msRestAzure from 'ms-rest-azure';
import Path from 'path';
import { caseSensitive_Numbs_SpecialCharacters_PW, lowercase_PW } from 'super-strong-password-generator'
import { caseSensitive_Numbs_SpecialCharacters_PW, lowercase_PW } from 'super-strong-password-generator';
import crypto from 'crypto';
import Fs from 'fs';
import { GBServer } from '../../../src/app.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
/**
* Services for server administration.
@ -89,45 +88,40 @@ export class GBAdminService implements IGBAdminService {
}
public static async getADALCredentialsFromUsername(username: string, password: string) {
return await msRestAzure.loginWithUsernamePassword(username, password);
}
public static getMobileCode() {
return this.getNumberIdentifier(6);
}
public static getRndPassword(): string {
let password = caseSensitive_Numbs_SpecialCharacters_PW(15);
password = password.replace(/[\@\[\=\:\;\?\"\'\#]/gi, '*');
const removeRepeatedChars = (s, r) => {
let res = '', last = null, counter = 0;
let res = '',
last = null,
counter = 0;
s.split('').forEach(char => {
if (char == last)
counter++;
else {
counter = 0;
last = char;
}
if (counter < r)
res += char;
if (char == last) counter++;
else {
counter = 0;
last = char;
}
if (counter < r) res += char;
});
return res;
}
};
return removeRepeatedChars(password, 1);
}
public static getRndReadableIdentifier(): string {
return lowercase_PW(14);
}
public static getNumberIdentifier(digits: number = 14): string {
if (digits <= 0) {
throw new Error('Number of digits should be greater than 0.');
}
@ -155,7 +149,6 @@ export class GBAdminService implements IGBAdminService {
}
public static async undeployPackageCommand(text: string, min: GBMinInstance) {
const packageName = text.split(' ')[1];
const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer);
@ -167,7 +160,12 @@ export class GBAdminService implements IGBAdminService {
public static isSharePointPath(path: string) {
return path.indexOf('sharepoint.com') !== -1;
}
public static async deployPackageCommand(min: GBMinInstance, user: GuaribasUser, text: string, deployer: IGBDeployer) {
public static async deployPackageCommand(
min: GBMinInstance,
user: GuaribasUser,
text: string,
deployer: IGBDeployer
) {
const packageName = text.split(' ')[1];
if (!this.isSharePointPath(packageName)) {
@ -190,19 +188,21 @@ export class GBAdminService implements IGBAdminService {
await deployer['cleanupPackage'](min.instance, packageName);
}
await deployer['downloadFolder'](min,
Path.join('work', `${gbai}`),
Path.basename(localFolder));
if (!GBConfigService.get('STORAGE_NAME')) {
const path = Path.join(GBConfigService.get('STORAGE_LIBRARY'), gbaiPath);
GBUtil.copyIfNewerRecursive(path, localFolder);
} else {
await deployer['downloadFolder'](min, Path.join('work', `${gbai}`), Path.basename(localFolder));
}
await deployer['deployPackage2'](min, user, localFolder);
}
}
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
const service = await AzureDeployerService.createInstance(deployer);
const searchIndex = min.instance.searchIndex ? min.instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
await deployer.rebuildIndex(
min.instance,
service.getKBSearchSchema(searchIndex)
);
const searchIndex = min.instance.searchIndex
? min.instance.searchIndex
: GBServer.globals.minBoot.instance.searchIndex;
await deployer.rebuildIndex(min.instance, service.getKBSearchSchema(searchIndex));
}
public static async syncBotServerCommand(min: GBMinInstance, deployer: GBDeployer) {
@ -245,15 +245,15 @@ export class GBAdminService implements IGBAdminService {
return obj.value;
}
public async acquireElevatedToken(instanceId: number, root: boolean = false,
public async acquireElevatedToken(
instanceId: number,
root: boolean = false,
tokenName: string = '',
clientId: string = null,
clientSecret: string = null,
host: string = null,
tenant: string = null
): Promise<string> {
if (root) {
const minBoot = GBServer.globals.minBoot;
instanceId = minBoot.instance.instanceId;
@ -267,7 +267,6 @@ export class GBAdminService implements IGBAdminService {
throw new Error(`/setupSecurity is required before running /publish.`);
}
return new Promise<string>(async (resolve, reject) => {
const instance = await this.core.loadInstanceById(instanceId);
@ -276,14 +275,10 @@ export class GBAdminService implements IGBAdminService {
const accessToken = await this.getValue(instanceId, `${tokenName}accessToken`);
resolve(accessToken);
} else {
if (tokenName && !root) {
const refreshToken = await this.getValue(instanceId, `${tokenName}refreshToken`);
let url = urlJoin(
host,
tenant, 'oauth/token');
let url = urlJoin(host, tenant, 'oauth/token');
let buff = new Buffer(`${clientId}:${clientSecret}`);
const base64 = buff.toString('base64');
@ -295,8 +290,8 @@ export class GBAdminService implements IGBAdminService {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'grant_type': 'refresh_token',
'refresh_token': refreshToken
grant_type: 'refresh_token',
refresh_token: refreshToken
})
};
const result = await fetch(url, options);
@ -313,15 +308,15 @@ export class GBAdminService implements IGBAdminService {
await this.setValue(instanceId, `${tokenName}accessToken`, token['access_token']);
await this.setValue(instanceId, `${tokenName}refreshToken`, token['refresh_token']);
await this.setValue(instanceId, `${tokenName}expiresOn`,
new Date(Date.now() + (token['expires_in'] * 1000)).toString());
await this.setValue(
instanceId,
`${tokenName}expiresOn`,
new Date(Date.now() + token['expires_in'] * 1000).toString()
);
await this.setValue(instanceId, `${tokenName}AntiCSRFAttackState`, null);
resolve(token['access_token']);
}
else {
} else {
const oauth2 = tokenName ? 'oauth' : 'oauth2';
const authorizationUrl = urlJoin(
tokenName ? host : instance.authenticatorAuthorityHostUrl,
@ -359,5 +354,5 @@ export class GBAdminService implements IGBAdminService {
});
}
public async publish(min: GBMinInstance, packageName: string, republish: boolean): Promise<void> { }
public async publish(min: GBMinInstance, packageName: string, republish: boolean): Promise<void> {}
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -6,7 +6,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -22,7 +22,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -1,31 +1,29 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model,this program can be used either |
| under the terms of the GNU Affero General Public License,version 3, |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY,without even the implied warranty of |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights,title and interest in |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
@ -39,6 +37,7 @@ import SwaggerClient from 'swagger-client';
import { spawn } from 'child_process';
import { CodeServices } from '../../gpt.gblib/services/CodeServices.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
/**
* Web Automation services of conversation to be called by BASIC.
@ -156,12 +155,8 @@ export class DebuggerService {
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
GBServer.globals.debuggers[botId].client = client;
const response = await client.apis.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;

View file

@ -1,31 +1,29 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model,this program can be used either |
| under the terms of the GNU Affero General Public License,version 3, |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY,without even the implied warranty of |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights,title and interest in |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
@ -429,7 +427,7 @@ export class DialogKeywords {
* @example TALK TOLIST (array,member)
*
*/
public async getToLst(pid, array, member) {
public async getToLst({pid, array, member}) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
if (!array) {
@ -1350,12 +1348,7 @@ export class DialogKeywords {
const conversation = min['apiConversations'][pid];
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
conversation.client = client;
const response = await client.apis.Conversations.Conversations_StartConversation();
conversation.conversationId = response.obj.conversationId;
@ -1368,7 +1361,7 @@ export class DialogKeywords {
const step = proc.step;
const min = GBServer.globals.minInstances.filter(p => p.instance.instanceId == proc.instanceId)[0];
const sec = new SecService();
const user = await sec.getUserFromId(min.instance.instanceId, proc.userId);
const user = GBServer.globals.users [proc.userId];
const params = user ? JSON.parse(user.params) : {};
return {
min,

View file

@ -1,19 +0,0 @@
import { GBVMService } from './GBVMService';
import { expect, test } from 'vitest'
test('Default', () => {
const args = GBVMService.getSetScheduleKeywordArgs(`
SET SCHEDULE "0 0 */1 * * *"
SET SCHEDULE "0 0 */3 * * *"
SET SCHEDULE "0 0 */2 * * *"
SET SCHEDULE "0 0 */2 * * *"
SET SCHEDULE "0 0 */3 * * *"
`);
expect(args.length).toBe(5);
});

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -32,7 +32,7 @@
import { GBMinInstance, GBService, IGBCoreService, GBLog } from 'botlib';
import * as Fs from 'fs';
import * as ji from 'just-indent'
import * as ji from 'just-indent';
import { GBServer } from '../../../src/app.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { CollectionUtil } from 'pragmatismo-io-framework';
@ -52,8 +52,8 @@ import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { SystemKeywords } from './SystemKeywords.js';
import { Sequelize, QueryTypes } from '@sequelize/core';
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
/**
* @fileoverview Decision was to priorize security(isolation) and debugging,
@ -68,7 +68,8 @@ export class GBVMService extends GBService {
public static API_PORT = 1111;
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
const files = await walkPromise(folder);
const ignore = Path.join('work', DialogKeywords.getGBAIPath(min.botId, 'gbdialog'), 'node_modules');
const files = await walkPromise(folder, { ignore: [ignore] });
await CollectionUtil.asyncForEach(files, async file => {
if (!file) {
@ -77,9 +78,7 @@ export class GBVMService extends GBService {
let filename: string = file.name;
if (filename.endsWith('.docx')) {
filename = await this.loadDialog(filename, folder, min);
}
filename = await this.loadDialog(filename, folder, min);
});
}
@ -91,14 +90,11 @@ export class GBVMService extends GBService {
//check every key for being same
return Object.keys(obj1).every(function (key) {
//if object
if ((typeof obj1[key] == "object") && (typeof obj2[key] == "object")) {
if (typeof obj1[key] == 'object' && typeof obj2[key] == 'object') {
//recursively check
return GBVMService.compare(obj1[key], obj2[key]);
} else {
//do the normal compare
return obj1[key] === obj2[key];
}
@ -106,14 +102,27 @@ export class GBVMService extends GBService {
}
public async loadDialog(filename: string, folder: string, min: GBMinInstance) {
const isWord = filename.endsWith('.docx');
if (
!(
isWord ||
filename.endsWith('.vbs') ||
filename.endsWith('.vb') ||
filename.endsWith('.vba') ||
filename.endsWith('.bas') ||
filename.endsWith('.basic')
)
) {
return;
}
const wordFile = filename;
const vbsFile = filename.substr(0, filename.indexOf('docx')) + 'vbs';
const vbsFile = isWord ? filename.substr(0, filename.indexOf('docx')) + 'vbs' : filename;
const fullVbsFile = urlJoin(folder, vbsFile);
const docxStat = Fs.statSync(urlJoin(folder, wordFile));
const interval = 3000; // If compiled is older 30 seconds, then recompile.
let writeVBS = true;
// TODO: #412.
// const subscription = {
// changeType: 'created,updated',
@ -129,7 +138,6 @@ export class GBVMService extends GBService {
// await client.api('/subscriptions')
// .post(subscription);
if (Fs.existsSync(fullVbsFile)) {
const vbsStat = Fs.statSync(fullVbsFile);
if (docxStat['mtimeMs'] < vbsStat['mtimeMs'] + interval) {
@ -140,26 +148,9 @@ export class GBVMService extends GBService {
let mainName = GBVMService.getMethodNameFromVBSFilename(filename);
min.scriptMap[filename] = mainName;
if (writeVBS) {
if (writeVBS && GBConfigService.get('STORAGE_NAME')) {
let text = await this.getTextFromWord(folder, wordFile);
// Pre process SET SCHEDULE calls.
const schedules = GBVMService.getSetScheduleKeywordArgs(text);
const s = new ScheduleServices();
await s.deleteScheduleIfAny(min, mainName);
let i = 1;
await CollectionUtil.asyncForEach(schedules, async (syntax) => {
if (s) {
await s.createOrUpdateSchedule(min, syntax, `${mainName};${i++}`);
}
});
text = text.replace(/^\s*SET SCHEDULE (.*)/gim, '');
// Write VBS file without pragma keywords.
Fs.writeFileSync(urlJoin(folder, vbsFile), text);
@ -167,6 +158,41 @@ export class GBVMService extends GBService {
// Process node_modules install.
this.processNodeModules(folder, min);
// Hot swap for .vbs files.
const fullFilename = urlJoin(folder, filename);
if (process.env.DEV_HOTSWAP) {
Fs.watchFile(fullFilename, async () => {
await this.translateBASIC(mainName, fullFilename, min);
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
});
}
const compiledAt = Fs.statSync(fullFilename);
const jsfile = urlJoin(folder, `${filename}.js`);
if (Fs.existsSync(jsfile)) {
const jsStat = Fs.statSync(jsfile);
const interval = 1000; // If compiled is older 1 seconds, then recompile.
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
await this.translateBASIC(mainName, fullFilename, min);
}
} else {
await this.translateBASIC(mainName, fullFilename, min);
}
// Syncronizes Database Objects with the ones returned from "Word".
this.syncStorageFromTABLE(folder, filename, min, mainName);
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
return filename;
}
private processNodeModules(folder: string, min: GBMinInstance) {
const node_modules = urlJoin(process.env.PWD, folder, 'node_modules');
if (!Fs.existsSync(node_modules)) {
const packageJson = `
@ -193,33 +219,9 @@ export class GBVMService extends GBService {
const npmPath = urlJoin(process.env.PWD, 'node_modules', '.bin', 'npm');
child_process.execSync(`${npmPath} install`, { cwd: folder });
}
}
// Hot swap for .vbs files.
const fullFilename = urlJoin(folder, filename);
if (process.env.DEV_HOTSWAP) {
Fs.watchFile(fullFilename, async () => {
await this.translateBASIC(mainName, fullFilename, min);
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
});
}
const compiledAt = Fs.statSync(fullFilename);
const jsfile = urlJoin(folder, `${filename}.js`);
if (Fs.existsSync(jsfile)) {
const jsStat = Fs.statSync(jsfile);
const interval = 30000; // If compiled is older 30 seconds, then recompile.
if (compiledAt.isFile() && compiledAt['mtimeMs'] > jsStat['mtimeMs'] + interval) {
await this.translateBASIC(mainName, fullFilename, min);
}
} else {
await this.translateBASIC(mainName, fullFilename, min);
}
// Syncronizes Database Objects with the ones returned from "Word".
private syncStorageFromTABLE(folder: string, filename: string, min: GBMinInstance, mainName: string) {
const tablesFile = urlJoin(folder, `${filename}.tables.json`);
let sync = false;
@ -229,7 +231,6 @@ export class GBVMService extends GBService {
const tableDef = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')) as any;
const getTypeBasedOnCondition = (t, size) => {
if (1) {
switch (t) {
case 'string':
@ -253,7 +254,6 @@ export class GBVMService extends GBService {
default:
return { type: 'TABLE', name: t };
}
} else {
switch (t) {
case 'string':
@ -277,43 +277,34 @@ export class GBVMService extends GBService {
default:
return { key: 'TABLE', name: t };
}
}
};
const associations = [];
// Loads storage custom connections.
const path = DialogKeywords.getGBAIPath(min.botId, null);
const filePath = Path.join('work', path, 'connections.json');
let connections = null;
if (Fs.existsSync(filePath)) {
connections = JSON.parse(Fs.readFileSync(filePath, 'utf8'));
}
const shouldSync = min.core.getParam<boolean>(
min.instance,
'Synchronize Database',
false
);
const shouldSync = min.core.getParam<boolean>(min.instance, 'Synchronize Database', false);
tableDef.forEach(async t => {
const tableName = t.name.trim();
// Determines autorelationship.
Object.keys(t.fields).forEach(key => {
let obj = t.fields[key];
obj.type = getTypeBasedOnCondition(obj.type, obj.size);
if (obj.type.key === "TABLE") {
obj.type.key = "BIGINT"
if (obj.type.key === 'TABLE') {
obj.type.key = 'BIGINT';
associations.push({ from: tableName, to: obj.type.name });
}
});
// Cutom connection for TABLE.
const connectionName = t.connection;
let con;
@ -330,8 +321,8 @@ export class GBVMService extends GBService {
const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
GBLogEx.info(min, str);
}
GBLogEx.info(min, str);
}
: false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
@ -367,11 +358,12 @@ export class GBVMService extends GBService {
if (!min[connectionName]) {
GBLogEx.info(min, `Loading custom connection ${connectionName}...`);
min[connectionName] = new Sequelize(storageName, username, password, sequelizeOptions);
min[`llmconnection`] ={
min[`llmconnection`] = {
type: dialect,
username,
database: storageName, password};
database: storageName,
password
};
}
}
@ -380,27 +372,22 @@ export class GBVMService extends GBService {
}
// Field checking, syncs if there is any difference.
const seq = min[connectionName] ? min[connectionName]
: minBoot.core.sequelize;
const seq = min[connectionName] ? min[connectionName] : minBoot.core.sequelize;
if (seq) {
const model = seq.models[tableName];
if (model) {
// Except Id, checks if has same number of fields.
let equals = 0;
Object.keys(t.fields).forEach(key => {
let obj1 = t.fields[key];
let obj2 = model['fieldRawAttributesMap'][key];
if (key !== "id") {
if (key !== 'id') {
if (obj1 && obj2) {
equals++;
}
}
});
if (equals != Object.keys(t.fields).length) {
@ -414,19 +401,21 @@ export class GBVMService extends GBService {
let tables;
if (con.storageDriver === 'mssql') {
tables = await seq.query(`SELECT table_name, table_schema
tables = await seq.query(
`SELECT table_name, table_schema
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
ORDER BY table_name ASC`, {
type: QueryTypes.RAW
})[0]
}
else if (con.storageDriver === 'mariadb') {
ORDER BY table_name ASC`,
{
type: QueryTypes.RAW
}
)[0];
} else if (con.storageDriver === 'mariadb') {
tables = await seq.getQueryInterface().showAllTables();
}
let found = false;
tables.forEach((storageTable) => {
tables.forEach(storageTable => {
if (storageTable['table_name'] === tableName) {
found = true;
}
@ -441,15 +430,17 @@ export class GBVMService extends GBService {
try {
to.hasMany(from);
} catch (error) {
throw new Error(`BASIC: Invalid relationship in ${mainName}: from ${e.from} to ${e.to} (${min.botId})... ${error.message}`);
throw new Error(
`BASIC: Invalid relationship in ${mainName}: from ${e.from} to ${e.to} (${min.botId})... ${error.message}`
);
}
});
if (sync && shouldSync) {
GBLogEx.info(min, `BASIC: Syncing changes for TABLE ${connectionName} ${tableName} keyword (${min.botId})...`);
GBLogEx.info(
min,
`BASIC: Syncing changes for TABLE ${connectionName} ${tableName} keyword (${min.botId})...`
);
await seq.sync({
alter: true,
@ -460,18 +451,29 @@ export class GBVMService extends GBService {
}
});
}
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
return filename;
}
public async translateBASIC(mainName, filename: any, min: GBMinInstance) {
// Converts General Bots BASIC into regular VBS
let basicCode: string = Fs.readFileSync(filename, 'utf8');
// Pre process SET SCHEDULE calls.
const schedules = GBVMService.getSetScheduleKeywordArgs(basicCode);
const s = new ScheduleServices();
await s.deleteScheduleIfAny(min, mainName);
let i = 1;
await CollectionUtil.asyncForEach(schedules, async syntax => {
if (s) {
await s.createOrUpdateSchedule(min, syntax, `${mainName};${i++}`);
}
});
basicCode = basicCode.replace(/^\s*SET SCHEDULE (.*)/gim, '');
// Process INCLUDE keyword to include another
// dialog inside the dialog.
@ -712,7 +714,6 @@ export class GBVMService extends GBService {
Fs.writeFileSync(jsfile, code);
GBLogEx.info(min, `[GBVMService] Finished loading of ${filename}, JavaScript from Word: \n ${code}`);
}
private async executeTasks(min, tasks) {
@ -720,12 +721,10 @@ export class GBVMService extends GBService {
const task = tasks[i];
if (task.kind === 'writeTableDefinition') {
// Creates an empty object that will receive Sequelize fields.
const tablesFile = `${task.file}.tables.json`;
Fs.writeFileSync(tablesFile, JSON.stringify(task.tables));
}
}
}
@ -744,10 +743,10 @@ export class GBVMService extends GBService {
lines.forEach(line => {
if (line.trim()) {
console.log(line);
const keyword = /\s*SET SCHEDULE (.*)/gi;
const keyword = /^\s*SET SCHEDULE (.*)/gi;
let result: any = keyword.exec(line);
if (result) {
result = result[1].replace(/\`|\"|\'/, '')
result = result[1].replace(/\`|\"|\'/, '');
result = result.trim();
results.push(result);
}
@ -756,6 +755,7 @@ export class GBVMService extends GBService {
return results;
}
private async getTextFromWord(folder: string, filename: string) {
return new Promise<string>(async (resolve, reject) => {
const path = urlJoin(folder, filename);
@ -777,7 +777,6 @@ export class GBVMService extends GBService {
}
public static normalizeQuotes(text: any) {
text = text.replace(/\"/gm, '`');
text = text.replace(/\¨/gm, '`');
text = text.replace(/\“/gm, '`');
@ -791,27 +790,22 @@ export class GBVMService extends GBService {
public static getMetadata(mainName: string, propertiesText, description) {
let properties = {};
if (!propertiesText || !description) {
return {}
return {};
}
const getType = asClause => {
asClause = asClause.trim().toUpperCase();
if (asClause.indexOf('STRING') !== -1) {
return 'string';
}
else if (asClause.indexOf('OBJECT') !== -1) {
} else if (asClause.indexOf('OBJECT') !== -1) {
return 'object';
}
else if (asClause.indexOf('INTEGER') !== -1 || asClause.indexOf('NUMBER') !== -1) {
} else if (asClause.indexOf('INTEGER') !== -1 || asClause.indexOf('NUMBER') !== -1) {
return 'number';
} else {
return 'enum';
}
};
for (let i = 0; i < propertiesText.length; i++) {
const propertiesExp = propertiesText[i];
const t = getType(propertiesExp[2]);
@ -834,21 +828,19 @@ export class GBVMService extends GBService {
properties[propertiesExp[1].trim()] = element;
}
let json = {
type: "function",
type: 'function',
function: {
name: `${mainName}`,
description: description ? description : '',
parameters: zodToJsonSchema(z.object(properties))
}
}
};
return json;
}
public async parseField(line) {
let required = line.indexOf('*') !== -1;
let unique = /\bunique\b/gi.test(line);
let primaryKey = /\bkey\b/gi.test(line);
@ -870,7 +862,8 @@ export class GBVMService extends GBService {
let definition = {
allowNull: !required,
unique: unique, primaryKey: primaryKey,
unique: unique,
primaryKey: primaryKey,
autoIncrement: autoIncrement
};
definition['type'] = t;
@ -889,7 +882,6 @@ export class GBVMService extends GBService {
* @param code General Bots BASIC
*/
public async convert(filename: string, mainName: string, code: string) {
// Start and End of VB2TS tags of processing.
code = process.env.ENABLE_AUTH ? `hear GBLogExin as login\n${code}` : code;
@ -910,7 +902,6 @@ export class GBVMService extends GBService {
const outputLines = [];
let emmitIndex = 1;
for (let i = 1; i <= lines.length; i++) {
let line = lines[i - 1];
// Remove lines before statements.
@ -961,12 +952,12 @@ export class GBVMService extends GBService {
const endTableKeyword = /^\s*END TABLE\s*/gim;
let endTableReg = endTableKeyword.exec(line);
if (endTableReg && table) {
tables.push({
name: table, fields: fields, connection: connection
name: table,
fields: fields,
connection: connection
});
fields = {};
table = null;
connection = null;
@ -1006,14 +997,14 @@ export class GBVMService extends GBService {
const talkKeyword = /^\s*BEGIN TALK\s*/gim;
let talkReg = talkKeyword.exec(line);
if (talkReg && !talk) {
talk = "await dk.talk ({pid: pid, text: `";
talk = 'await dk.talk ({pid: pid, text: `';
emmit = false;
}
const systemPromptKeyword = /^\s*BEGIN SYSTEM PROMPT\s*/gim;
let systemPromptReg = systemPromptKeyword.exec(line);
if (systemPromptReg && !systemPrompt) {
systemPrompt = "await sys.setSystemPrompt ({pid: pid, text: `";
systemPrompt = 'await sys.setSystemPrompt ({pid: pid, text: `';
emmit = false;
}
@ -1031,9 +1022,10 @@ export class GBVMService extends GBService {
if (tables) {
tasks.push({
kind: 'writeTableDefinition', file: filename, tables
kind: 'writeTableDefinition',
file: filename,
tables
});
}
code = `${outputLines.join('\n')}\n`;
@ -1046,14 +1038,7 @@ export class GBVMService extends GBService {
/**
* Executes the converted JavaScript from BASIC code inside execution context.
*/
public static async callVM(
text: string,
min: GBMinInstance,
step,
pid,
debug: boolean = false,
params = []
) {
public static async callVM(text: string, min: GBMinInstance, step, pid, debug: boolean = false, params = []) {
// Creates a class DialogKeywords which is the *this* pointer
// in BASIC.
@ -1069,8 +1054,7 @@ export class GBVMService extends GBService {
// These variables will be automatically be available as normal BASIC variables.
try {
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken']
(min.instance.instanceId, false);
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, false);
} catch (error) {
variables['aadToken'] = 'ERROR: Configure /setupSecurity before using aadToken variable.';
}
@ -1111,12 +1095,10 @@ export class GBVMService extends GBService {
let code = min.sandBoxMap[text];
const channel = step ? step.context.activity.channelId : 'web';
const dk = new DialogKeywords();
const sys = new SystemKeywords();
await dk.setFilter({ pid: pid, value: null });
// Find all tokens in .gbot Config.
const strFind = ' Client ID';
@ -1152,7 +1134,7 @@ export class GBVMService extends GBService {
return await new Promise((resolve, reject) => {
sandbox['resolve'] = resolve;
// TODO: #411 sandbox['reject'] = reject;
sandbox['reject'] = ()=>{};
sandbox['reject'] = () => {};
const vm1 = new NodeVM({
allowAsync: true,
@ -1169,7 +1151,6 @@ export class GBVMService extends GBService {
const s = new VMScript(code, { filename: scriptPath });
result = vm1.run(s);
});
})();
} else {
const runnerPath = urlJoin(
@ -1200,10 +1181,15 @@ export class GBVMService extends GBService {
} catch (error) {
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
}
}
public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any, executable: string, step = null) {
public static createProcessInfo(
user: GuaribasUser,
min: GBMinInstance,
channel: any,
executable: string,
step = null
) {
const pid = GBAdminService.getNumberIdentifier();
GBServer.globals.processes[pid] = {
pid: pid,

View file

@ -1,31 +1,29 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model,this program can be used either |
| under the terms of the GNU Affero General Public License,version 3, |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY,without even the implied warranty of |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights,title and interest in |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
@ -35,13 +33,13 @@
import Path from 'path';
import { GBLog, GBMinInstance } from 'botlib';
import { DialogKeywords } from './DialogKeywords.js';
import sharp from 'sharp';
import joinImages from 'join-images-updated';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import urlJoin from 'url-join';
import { GBServer } from '../../../src/app.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import sharp from 'sharp';
/**
* Image processing services of conversation to be called by BASIC.

View file

@ -1,35 +1,34 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ __ __ _ _ | ,_)(_) __ __ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(___/(_) (_)`\__/' |
| | | ( )_) | |
| (_) \__/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model,this program can be used either |
| under the terms of the GNU Affero General Public License,version 3, |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY,without even the implied warranty of |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights,title and interest in |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -47,33 +47,30 @@ import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
* Basic services for BASIC manipulation.
*/
export class ScheduleServices extends GBService {
public async deleteScheduleIfAny(min: GBMinInstance, name: string) {
let i = 1;
while (i <= 10) {
const task = min['scheduleMap'] ? min['scheduleMap'][name + i] : null;
if (task) {
task.destroy();
const id = `${name};${i}`;
delete min['scheduleMap'][id];
const count = await GuaribasSchedule.destroy({
where: {
instanceId: min.instance.instanceId,
name: id
}
});
if (count > 0) {
GBLogEx.info(min, `BASIC: Removed ${name} SET SCHEDULE and ${count} rows from storage on: ${min.botId}...`);
}
}
const id = `${name};${i}`;
delete min['scheduleMap'][id];
const count = await GuaribasSchedule.destroy({
where: {
instanceId: min.instance.instanceId,
name: id
}
});
if (count > 0) {
GBLogEx.info(min, `BASIC: Removed ${name} SET SCHEDULE and ${count} rows from storage on: ${min.botId}...`);
}
i++;
}
}
/**
@ -113,12 +110,10 @@ export class ScheduleServices extends GBService {
let i = 0;
let lastName = '';
await CollectionUtil.asyncForEach(schedules, async (item) => {
await CollectionUtil.asyncForEach(schedules, async item => {
if (item.name === lastName) {
item.name = item.name + ++i;
}
else {
} else {
i = 0;
}
@ -169,7 +164,6 @@ export class ScheduleServices extends GBService {
},
options
);
} catch (error) {
GBLogEx.error(min, `Running .gbdialog word ${item.name} : ${error}...`);
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -92,6 +92,7 @@ export class SystemKeywords {
}
public async append({ pid, args }) {
if (!args) return [];
let array = [].concat(...args);
return array.filter(function (item, pos) {
return item;

View file

@ -1,31 +1,29 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model,this program can be used either |
| under the terms of the GNU Affero General Public License,version 3, |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY,without even the implied warranty of |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights,title and interest in |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/

View file

@ -0,0 +1,16 @@
import { expect, test } from 'vitest';
import { DialogKeywords } from '../services/DialogKeywords';
import init from '../../../.test-init'
init();
const dk = new DialogKeywords();
const pid = 1;
test('TOLIST', async () => {
const obj = [{a:1, b:2}, {a:2, b:4}];
expect(await dk.getToLst({ pid, array: obj, member:'a' }))
.toBe("1,2");
});

View file

@ -0,0 +1,43 @@
import { GBVMService } from '../services/GBVMService';
import { expect, test } from 'vitest'
test('Default', () => {
const args = GBVMService.getSetScheduleKeywordArgs(`
SET SCHEDULE "0 0 */1 * * *"
SET SCHEDULE "0 0 */3 * * *"
SET SCHEDULE "0 0 */2 * * *"
SET SCHEDULE "0 0 */2 * * *"
SET SCHEDULE "0 0 */3 * * *"
`);
expect(args.length).toBe(5);
});
test('Compare', () => {
expect(GBVMService.compare(1,1)).toBeTruthy();
expect(GBVMService.compare({a:1},{a:1})).toBeTruthy();
expect(GBVMService.compare({a:1},{a:2})).toBeFalsy();
expect(GBVMService.compare({a:1, b:2},{a:1, b:2})).toBeTruthy();
});
test('Parse Storage Field', async () => {
const s = new GBVMService();
expect(await s.parseField('name STRING(30)')).toStrictEqual({name: 'name', definition: {
allowNull: true,
unique: false, primaryKey: false,
size: 30,
autoIncrement: false,
type:"STRING"
}});
});

View file

@ -0,0 +1,36 @@
import { GBVMService } from '../services/GBVMService';
import { expect, test } from 'vitest';
import { SystemKeywords } from '../services/SystemKeywords';
const s = new SystemKeywords();
const pid = 1;
test('APPEND', async () => {
expect(await s.append({ pid, args: [1, 1, 1, 1] })).toStrictEqual([1, 1, 1, 1]);
expect(await s.append({ pid, args: [1] })).toStrictEqual([1]);
expect(await s.append({ pid, args: [] })).toStrictEqual([]);
expect(await s.append({ pid, args: null })).toStrictEqual([]);
});
test('COMPARE', () => {
expect(GBVMService.compare(1, 1)).toBeTruthy();
expect(GBVMService.compare({ a: 1 }, { a: 1 })).toBeTruthy();
expect(GBVMService.compare({ a: 1 }, { a: 2 })).toBeFalsy();
expect(GBVMService.compare({ a: 1, b: 2 }, { a: 1, b: 2 })).toBeTruthy();
});
test('Parse Storage Field', async () => {
const s = new GBVMService();
expect(await s.parseField('name STRING(30)')).toStrictEqual({
name: 'name',
definition: {
allowNull: true,
unique: false,
primaryKey: false,
size: 30,
autoIncrement: false,
type: 'STRING'
}
});
});

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -36,11 +36,11 @@
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
import { GBMinInstance, IGBDialog } from 'botlib';
import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings.js';
import { GBLogEx } from '../services/GBLogEx.js';
import { GBConfigService } from '../services/GBConfigService.js';
/**
* Dialog for Welcoming people.
@ -65,7 +65,7 @@ export class WelcomeDialog extends IGBDialog {
async step => {
if (
GBServer.globals.entryPointDialog !== null &&
min.instance.botId === process.env.BOT_ID &&
min.instance.botId === GBConfigService.get('BOT_ID') &&
step.context.activity.channelId === 'webchat'
) {
return step.replaceDialog(GBServer.globals.entryPointDialog);

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -42,7 +42,7 @@ import * as en from 'dotenv-extended';
*/
export class GBConfigService {
public static getBoolean(value: string): boolean {
return (this.get(value) as unknown) as boolean;
return this.get(value) as unknown as boolean;
}
public static getServerPort(): string {
if (process.env.PORT) {
@ -79,13 +79,19 @@ export class GBConfigService {
public static get(key: string): string | undefined {
let value = GBConfigService.tryGet(key);
if (value === undefined) {
if (!value) {
switch (key) {
case 'STORAGE_NAME':
value = null;
break;
case 'CLOUD_USERNAME':
value = undefined;
break;
case 'STORAGE_LIBRARY':
value = `${process.env.HOME}/gbpackages`;
break;
case 'BOT_ID':
value = undefined;
value = 'default';
break;
case 'CLOUD_PASSWORD':
value = undefined;
@ -103,10 +109,10 @@ export class GBConfigService {
value = undefined;
break;
case 'STORAGE_DIALECT':
value = undefined;
value = 'sqlite';
break;
case 'STORAGE_FILE':
value = './guaribas.sqlite';
value = './data.db';
break;
case 'GBKB_AUTO_DEPLOY':
value = false;
@ -160,7 +166,7 @@ export class GBConfigService {
value = true;
break;
case 'BOT_URL':
value = undefined;
value = 'http://localhost:4242';
break;
case 'STORAGE_SERVER':
value = undefined;

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -341,6 +341,7 @@ export class GBConversationalService {
}
public async sendEvent(min: GBMinInstance, step: GBDialogStep, name: string, value: Object): Promise<any> {
if (step.context.activity.channelId !== 'msteams' && step.context.activity.channelId !== 'omnichannel') {
GBLogEx.info(
min,

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -50,6 +50,7 @@ import { GBSecurityPackage } from '../../security.gbapp/index.js';
import { GBWhatsappPackage } from '../../whatsapp.gblib/index.js';
import { GuaribasApplications, GuaribasInstance, GuaribasLog } from '../models/GBModel.js';
import { GBConfigService } from './GBConfigService.js';
import mkdirp from 'mkdirp';
import { GBAzureDeployerPackage } from '../../azuredeployer.gbapp/index.js';
import { GBSharePointPackage } from '../../sharepoint.gblib/index.js';
import { CollectionUtil } from 'pragmatismo-io-framework';
@ -109,7 +110,7 @@ export class GBCoreService implements IGBCoreService {
constructor() {
this.adminService = new GBAdminService(this);
}
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) { }
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {}
/**
* Gets database config and connect to storage. Currently two databases
@ -131,6 +132,12 @@ export class GBCoreService implements IGBCoreService {
password = GBConfigService.get('STORAGE_PASSWORD');
} else if (this.dialect === 'sqlite') {
storage = GBConfigService.get('STORAGE_FILE');
if (!Fs.existsSync(storage)){
process.env.STORAGE_SYNC = 'true';
}
} else {
throw new Error(`Unknown dialect: ${this.dialect}.`);
}
@ -138,8 +145,8 @@ export class GBCoreService implements IGBCoreService {
const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
GBLogEx.info(0, str);
}
GBLogEx.info(0, str);
}
: false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
@ -231,12 +238,11 @@ export class GBCoreService implements IGBCoreService {
return out;
}
/**
* Loads all items to start several listeners.
*/
public async loadInstances(): Promise<IGBInstance[]> {
if (process.env.LOAD_ONLY !== undefined) {
if (process.env.LOAD_ONLY) {
const bots = process.env.LOAD_ONLY.split(`;`);
const and = [];
await CollectionUtil.asyncForEach(bots, async e => {
@ -426,12 +432,11 @@ ENDPOINT_UPDATE=true
let instances: IGBInstance[];
try {
instances = await core.loadInstances();
const group = GBConfigService.get('CLOUD_GROUP')??GBConfigService.get('BOT_ID');
if (process.env.ENDPOINT_UPDATE === 'true') {
const group = GBConfigService.get('CLOUD_GROUP') ?? GBConfigService.get('BOT_ID');
await CollectionUtil.asyncForEach(instances, async instance => {
GBLogEx.info(instance.instanceId, `Updating bot endpoint for ${instance.botId}...`);
try {
await installationDeployer.updateBotProxy(
instance.botId,
group,
@ -459,7 +464,10 @@ ENDPOINT_UPDATE=true
Try setting STORAGE_SYNC to true in .env file. Error: ${error.message}.`
);
} else {
GBLogEx.info(0, `Storage is empty. After collecting storage structure from all .gbapps it will get synced.`);
GBLogEx.info(
0,
`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`
);
}
} else {
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
@ -512,15 +520,8 @@ ENDPOINT_UPDATE=true
* before starting the server.
*/
public ensureAdminIsSecured() {
const password = GBConfigService.get('ADMIN_PASS');
if (!GBAdminService.StrongRegex.test(password)) {
throw new Error(
'Please, define a really strong password in ADMIN_PASS environment variable before running the server.'
);
}
}
public async createBootInstance(
core: GBCoreService,
installationDeployer: IGBInstallationDeployer,
@ -529,9 +530,10 @@ ENDPOINT_UPDATE=true
return await this.createBootInstanceEx(
core,
installationDeployer,
proxyAddress, null,
GBConfigService.get('FREE_TIER'));
proxyAddress,
null,
GBConfigService.get('FREE_TIER')
);
}
/**
* Creates the first bot instance (boot instance) used to "boot" the server.
@ -548,8 +550,10 @@ ENDPOINT_UPDATE=true
) {
GBLogEx.info(0, `Deploying cognitive infrastructure (on the cloud / on premises)...`);
try {
const { instance, credentials, subscriptionId, installationDeployer }
= await StartDialog.createBaseInstance(deployer, freeTier);
const { instance, credentials, subscriptionId, installationDeployer } = await StartDialog.createBaseInstance(
deployer,
freeTier
);
installationDeployer['core'] = this;
const changedInstance = await installationDeployer['deployFarm2'](
proxyAddress,
@ -668,27 +672,26 @@ ENDPOINT_UPDATE=true
}
public async setConfig(min, name: string, value: any): Promise<any> {
// Handles calls for BASIC persistence on sheet files.
GBLog.info( `Defining Config.xlsx variable ${name}= '${value}'...`);
GBLog.info(`Defining Config.xlsx variable ${name}= '${value}'...`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const maxLines = 512;
const file = "Config.xlsx";
const path = DialogKeywords.getGBAIPath(min.botId, `gbot`);;
const file = 'Config.xlsx';
const path = DialogKeywords.getGBAIPath(min.botId, `gbot`);
let document = await (new SystemKeywords()).internalGetDocument(client, baseUrl, path, file);
let document = await new SystemKeywords().internalGetDocument(client, baseUrl, path, file);
// Creates workbook session that will be discarded.
// Creates book session that will be discarded.
let sheets = await client
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
.get();
let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
let results = await client
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`)
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`
)
.get();
const rows = results.text;
@ -708,12 +711,12 @@ ENDPOINT_UPDATE=true
body.values[0][0] = value;
await client
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`)
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`
)
.patch(body);
}
/**
* Get a dynamic param from instance. Dynamic params are defined in Config.xlsx
* and loaded into the work folder from comida command.
@ -729,8 +732,7 @@ ENDPOINT_UPDATE=true
// Gets .gbot Params from specified bot.
if (instance.params) {
params = typeof (instance.params) === 'object' ? instance.params : JSON.parse(instance.params);
params = typeof instance.params === 'object' ? instance.params : JSON.parse(instance.params);
params = GBUtil.caseInsensitive(params);
value = params ? params[name] : defaultValue;
}
@ -740,7 +742,6 @@ ENDPOINT_UPDATE=true
params = GBUtil.caseInsensitive(instance['dataValues']);
if (params && !value) {
// Retrieves the value from specified bot instance (no params collection).
value = instance['dataValues'][name];
@ -749,27 +750,23 @@ ENDPOINT_UPDATE=true
const minBoot = GBServer.globals.minBoot as any;
if (
minBoot.instance &&
!value && instance.botId != minBoot.instance.botId) {
if (minBoot.instance && !value && instance.botId != minBoot.instance.botId) {
instance = minBoot.instance;
if(instance.params){
params = typeof (instance.params) === 'object' ? instance.params : JSON.parse(instance.params);
if (instance.params) {
params = typeof instance.params === 'object' ? instance.params : JSON.parse(instance.params);
params = GBUtil.caseInsensitive(params);
value = params ? params[name] : defaultValue;
}
// If still did not found in boot bot params, try instance fields.
if (!value){
if (!value) {
value = instance['dataValues'][name];
}
if (!value){
if (!value) {
value = instance[name];
}
}
}
@ -787,7 +784,7 @@ ENDPOINT_UPDATE=true
return new Number(value ? value : defaultValue ? defaultValue : 0).valueOf();
}
const ret = value ?? defaultValue;
const ret = value ?? defaultValue;
return ret;
}
@ -798,7 +795,7 @@ ENDPOINT_UPDATE=true
let params = null;
const list = [];
if (instance.params) {
params = typeof (instance.params) === 'object' ? instance.params : JSON.parse(instance.params);
params = typeof instance.params === 'object' ? instance.params : JSON.parse(instance.params);
}
Object.keys(params).forEach(e => {
@ -810,5 +807,79 @@ ENDPOINT_UPDATE=true
return list;
}
public async ensureFolders(instances, deployer: GBDeployer) {
let libraryPath = GBConfigService.get('STORAGE_LIBRARY');
if (!Fs.existsSync(libraryPath)) {
mkdirp.sync(libraryPath);
}
await this.syncBotStorage(instances, 'default', deployer, libraryPath);
const files = Fs.readdirSync(libraryPath);
await CollectionUtil.asyncForEach(files, async file => {
if (file.trim().toLowerCase() !== 'default.gbai'){
let botId = file.replace(/\.gbai/, '');
await this.syncBotStorage(instances, botId, deployer, libraryPath);
}
});
}
private async syncBotStorage(instances: any, botId: any, deployer: GBDeployer, libraryPath: string) {
let instance = instances.find(p => p.botId.toLowerCase().trim() === botId.toLowerCase().trim());
if (!instance) {
GBLog.info(`Importing package ${botId}...`);
// Creates a bot.
let mobile = null,
email = null;
instance = await deployer.deployBlankBot(botId, mobile, email);
const gbaiPath = Path.join(libraryPath, `${botId}.gbai`);
if (!Fs.existsSync(gbaiPath)) {
Fs.mkdirSync(gbaiPath, { recursive: true });
const base = Path.join(process.env.PWD, 'templates', 'default.gbai');
Fs.cpSync(Path.join(base, `default.gbkb`), Path.join(gbaiPath,`default.gbkb`), {
errorOnExist: false,
force: true,
recursive: true
});
Fs.cpSync(Path.join(base, `default.gbot`), Path.join(gbaiPath, `default.gbot`), {
errorOnExist: false,
force: true,
recursive: true
});
Fs.cpSync(Path.join(base, `default.gbtheme`), Path.join(gbaiPath, `default.gbtheme`), {
errorOnExist: false,
force: true,
recursive: true
});
Fs.cpSync(Path.join(base, `default.gbdata`), Path.join(gbaiPath, `default.gbdata`), {
errorOnExist: false,
force: true,
recursive: true
});
Fs.cpSync(Path.join(base, `default.gbdialog`), Path.join(gbaiPath, `default.gbdialog`), {
errorOnExist: false,
force: true,
recursive: true
});
Fs.cpSync(Path.join(base, `default.gbdrive`), Path.join(gbaiPath, `default.gbdrive`), {
errorOnExist: false,
force: true,
recursive: true
});
}
}
}
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -45,6 +45,8 @@ import { AzureSearch } from 'pragmatismo-io-framework';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBServer } from '../../../src/app.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import Excel from 'exceljs';
import asyncPromise from 'async-promises';
import { GuaribasPackage } from '../models/GBModel.js';
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService.js';
@ -118,7 +120,7 @@ export class GBDeployer implements IGBDeployer {
);
const siteId = process.env.STORAGE_SITE_ID;
const libraryId = process.env.STORAGE_LIBRARY;
const libraryId = GBConfigService.get('STORAGE_LIBRARY');
const client = MicrosoftGraph.Client.init({
authProvider: done => {
@ -220,22 +222,24 @@ export class GBDeployer implements IGBDeployer {
const instance = await this.importer.createBotInstance(botId);
const bootInstance = GBServer.globals.bootInstance;
// Gets the access token to perform service operations.
if (GBConfigService.get('STORAGE_NAME')) {
// Gets the access token to perform service operations.
const accessToken = await (GBServer.globals.minBoot.adminService as any)['acquireElevatedToken'](
bootInstance.instanceId,
true
);
const accessToken = await (GBServer.globals.minBoot.adminService as any)['acquireElevatedToken'](
bootInstance.instanceId,
true
);
// Creates the MSFT application that will be associated to the bot.
// Creates the MSFT application that will be associated to the bot.
const service = await AzureDeployerService.createInstance(this);
const application = await service.createApplication(accessToken, botId);
const service = await AzureDeployerService.createInstance(this);
const application = await service.createApplication(accessToken, botId);
// Fills new instance base information and get App secret.
// Fills new instance base information and get App secret.
instance.marketplaceId = (application as any).appId;
instance.marketplacePassword = await service.createApplicationSecret(accessToken, (application as any).id);
}
instance.marketplaceId = (application as any).appId;
instance.marketplacePassword = await service.createApplicationSecret(accessToken, (application as any).id);
instance.adminPass = GBAdminService.getRndPassword();
instance.title = botId;
instance.activationCode = instance.botId.substring(0, 15);
@ -247,10 +251,17 @@ export class GBDeployer implements IGBDeployer {
// Saves bot information to the store.
await this.core.saveInstance(instance);
if (GBConfigService.get('STORAGE_NAME')) {
await this.deployBotOnAzure(instance, GBServer.globals.publicAddress);
}
// Makes available bot to the channels and .gbui interfaces.
await GBServer.globals.minService.mountBot(instance);
// Creates remaining objects on the cloud and updates instance information.
return await this.deployBotFull(instance, GBServer.globals.publicAddress);
return instance;
}
/**
@ -265,7 +276,7 @@ export class GBDeployer implements IGBDeployer {
/**
* Performs all tasks of deploying a new bot on the cloud.
*/
public async deployBotFull(instance: IGBInstance, publicAddress: string): Promise<IGBInstance> {
public async deployBotOnAzure(instance: IGBInstance, publicAddress: string): Promise<IGBInstance> {
// Reads base configuration from environent file.
const service = await AzureDeployerService.createInstance(this);
@ -286,7 +297,6 @@ export class GBDeployer implements IGBDeployer {
`${publicAddress}/api/messages/${instance.botId}`
);
} else {
// Internally create resources on cloud provider.
instance = await service.internalDeployBot(
@ -305,10 +315,6 @@ export class GBDeployer implements IGBDeployer {
subscriptionId
);
// Makes available bot to the channels and .gbui interfaces.
await GBServer.globals.minService.mountBot(instance);
await GBServer.globals.minService.ensureAPI();
}
// Saves final instance object and returns it.
@ -326,7 +332,6 @@ export class GBDeployer implements IGBDeployer {
let embedding;
if (!azureOpenAIEmbeddingModel) {
return;
}
@ -344,7 +349,6 @@ export class GBDeployer implements IGBDeployer {
vectorStore = new HNSWLib(embedding, {
space: 'cosine'
});
}
return vectorStore;
}
@ -412,70 +416,50 @@ export class GBDeployer implements IGBDeployer {
public async deployBotFromLocalPath(localPath: string, publicAddress: string): Promise<void> {
const packageName = Path.basename(localPath);
const instance = await this.importer.importIfNotExistsBotPackage(undefined, packageName, localPath);
await this.deployBotFull(instance, publicAddress);
await this.deployBotOnAzure(instance, publicAddress);
}
/**
* Loads all para from tabular file Config.xlsx.
*/
public async loadParamsFromTabular(min: GBMinInstance): Promise<any> {
const siteId = process.env.STORAGE_SITE_ID;
const libraryId = process.env.STORAGE_LIBRARY;
public async loadParamsFromTabular(min: GBMinInstance, path): Promise<any> {
const workbook = new Excel.Workbook();
const data = await workbook.xlsx.readFile(Path.join(path, 'Config.xlsx'));
GBLogEx.info(min, `Connecting to Config.xslx (siteId: ${siteId}, libraryId: ${libraryId})...`);
let worksheet: any;
for (let t = 0; t < data.worksheets.length; t++) {
worksheet = data.worksheets[t];
if (worksheet) {
break;
}
}
const rows = worksheet._rows;
GBLogEx.info(min, `Processing ${rows.length} rows from Config file ${path}...`);
let list = [];
// Connects to MSFT storage.
// Skips the header lines.
const token = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, true);
for (let index = 0; index < 6; index++) {
rows.shift();
}
const client = MicrosoftGraph.Client.init({
authProvider: done => {
done(null, token);
let obj = {};
await asyncPromise.eachSeries(rows, async line => {
if (line && line._cells[0] && line._cells[1] && line._cells[0].text) {
// Extracts values from columns in the current line.
obj[line._cells[0].text] = line._cells[1].text;
}
});
// Retrieves all files in .bot folder.
const botId = min.instance.botId;
const path = DialogKeywords.getGBAIPath(botId, 'gbot');
let url = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:/${path}:/children`;
GBLogEx.info(min, `Loading .gbot from Excel: ${url}`);
const res = await client.api(url).get();
// Finds Config.xlsx.
const document = res.value.filter(m => {
return m.name === 'Config.xlsx';
});
if (document === undefined || document.length === 0) {
GBLogEx.info(min, `Config.xlsx not found on .bot folder, check the package.`);
return null;
}
// Reads all rows in Config.xlsx that contains a pair of name/value
// and fills an object that is returned to be saved in params instance field.
const results = await client
.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/items/${document[0].id}/workbook/worksheets('General')/range(address='A7:B100')`
)
.get();
let index = 0,
obj = {};
for (; index < results.text.length; index++) {
if (results.text[index][0] === '') {
return obj;
}
obj[results.text[index][0]] = results.text[index][1];
}
GBLogEx.info(min, GBUtil.toYAML(list));
return obj;
}
/**
* Loads all para from tabular file Config.xlsx.
*/
public async downloadFolder(
min: GBMinInstance,
@ -632,14 +616,7 @@ export class GBDeployer implements IGBDeployer {
case '.gbot':
// Extracts configuration information from .gbot files.
if (process.env.ENABLE_PARAMS_ONLINE === 'false') {
if (Fs.existsSync(localPath)) {
GBLogEx.info(min, `Loading .gbot from ${localPath}.`);
await this.deployBotFromLocalPath(localPath, GBServer.globals.publicAddress);
}
} else {
min.instance.params = await this.loadParamsFromTabular(min);
}
min.instance.params = await this.loadParamsFromTabular(min, localPath);
let connections = [];

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -1,13 +1,11 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ _ _ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/ \ /`\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| |*| |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -23,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -44,7 +44,9 @@ import Fs from 'fs';
import arrayBufferToBuffer from 'arraybuffer-to-buffer';
import { NlpManager } from 'node-nlp';
import Koa from 'koa';
import { v2 as webdav } from 'webdav-server';
import { createRpcServer } from '@push-rpc/core';
import { start as startRouter } from '../../../packages/core.gbapp/services/router/bridge.js';
import wash from 'washyourmouthoutwithsoap';
import {
AutoSaveStateMiddleware,
@ -140,10 +142,7 @@ export class GBMinService {
this.deployer = deployer;
}
public async enableAPI(min: GBMinInstance) {
}
public async enableAPI(min: GBMinInstance) {}
/**
* Constructs a new minimal instance for each bot.
@ -168,25 +167,20 @@ export class GBMinService {
let i = 1;
if (instances.length > 1) {
}
await CollectionUtil.asyncForEach(
instances,
(async instance => {
try {
GBLog.info(`Mounting ${instance.botId}...`)
GBLog.info(`Mounting ${instance.botId}...`);
await this['mountBot'](instance);
} catch (error) {
GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`);
}
}).bind(this)
);
// Loads API.
await this.ensureAPI();
// Loads schedules.
GBLogEx.info(0, `Loading SET SCHEDULE entries...`);
@ -196,6 +190,37 @@ export class GBMinService {
GBLogEx.info(0, `All Bot instances loaded.`);
}
public async startSimpleTest(min) {
if (process.env.TEST_MESSAGE && min['isDefault']) {
GBLogEx.info(min, `Starting auto test with '${process.env.TEST_MESSAGE}'.`);
const client = await GBUtil.getDirectLineClient(min);
const response = await client.apis.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;
GBServer.globals.debugConversationId = conversationId;
const steps = process.env.TEST_MESSAGE.split(';');
await CollectionUtil.asyncForEach(steps, async (step) => {
client.apis.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: step,
type: 'message',
from: {
id: 'test',
name: 'test'
}
}
});
await GBUtil.sleep(3000);
});
}
}
/**
* Removes bot endpoint from web listeners and remove bot instance
* from list of global server bot instances.
@ -234,7 +259,7 @@ export class GBMinService {
/**
* Unmounts the bot web site (default.gbui) secure domain, if any.
*/
public async unloadDomain(instance: IGBInstance) { }
public async unloadDomain(instance: IGBInstance) {}
/**
* Mount the instance by creating an BOT Framework bot object,
@ -243,6 +268,7 @@ export class GBMinService {
*/
public async mountBot(instance: IGBInstance) {
// Build bot adapter.
const { min, adapter, conversationState } = await this.buildBotAdapter(
@ -254,10 +280,14 @@ export class GBMinService {
// https://github.com/GeneralBots/BotServer/issues/286
// min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
min['isDefault'] = GBServer.globals.minInstances.length === 0;
GBServer.globals.minInstances.push(min);
const user = null; // No user context.
await this.deployer['deployPackage2'](min, user, 'packages/default.gbtheme');
// Serves individual URL for each bot conversational interface.
await this.deployer['deployPackage2'](min, user, 'templates/default.gbai/default.gbtheme');
// Install per bot deployed packages.
@ -318,21 +348,23 @@ export class GBMinService {
mkdirp.sync(dir);
}
// Loads Named Entity data for this bot.
if (!GBConfigService.get('STORAGE_NAME')) {
dir = Path.join(GBConfigService.get('STORAGE_LIBRARY'), 'work', gbai);
// TODO: await KBService.RefreshNER(min);
const server = GBServer.globals.webDavServer;
server.setFileSystem(`/${botId}`, new webdav.PhysicalFileSystem(dir), success => {
GBLogEx.info(1, `WebDav for ${botId} loaded.`);
});
}
// Calls the loadBot context.activity for all packages.
await this.invokeLoadBot(min.appPackages, GBServer.globals.sysPackages, min);
// Serves individual URL for each bot conversational interface.
const receiver = async (req, res) => {
await this.receiver(req, res, conversationState, min, instance, GBServer.globals.appPackages);
};
const url = `/api/messages/${instance.botId}`;
GBServer.globals.server.post(url, receiver);
let url = `/api/messages/${instance.botId}`;
GBServer.globals.server.get(url, (req, res) => {
if (req.query['hub.mode'] === 'subscribe') {
if (req.query['hub.verify_token'] === process.env.FACEBOOK_VERIFY_TOKEN) {
@ -347,39 +379,6 @@ export class GBMinService {
});
GBLog.verbose(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
// Test code.
if (process.env.TEST_MESSAGE) {
GBLogEx.info(min, `Starting auto test with '${process.env.TEST_MESSAGE}'.`);
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const response = await client.apis.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;
GBServer.globals.debugConversationId = conversationId;
const steps = process.env.TEST_MESSAGE.split(';');
await CollectionUtil.asyncForEach(steps, async step => {
client.apis.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: step,
type: 'message',
from: {
id: 'test',
name: 'test'
}
}
});
await GBUtil.sleep(3000);
});
// Generates MS Teams manifest.
@ -390,7 +389,6 @@ export class GBMinService {
const data = await this.deployer.getBotManifest(instance);
Fs.writeFileSync(packageTeams, data);
}
}
// Serves individual URL for each bot user interface.
@ -426,37 +424,44 @@ export class GBMinService {
// Setups official handler for WhatsApp.
GBServer.globals.server.all(`/${min.instance.botId}/whatsapp`, async (req, res) => {
GBServer.globals.server
.all(`/${min.instance.botId}/whatsapp`, async (req, res) => {
if (req.query['hub.mode'] === 'subscribe') {
const val = req.query['hub.verify_token'];
if (val === process.env.META_CHALLENGE) {
res.send(req.query['hub.challenge']);
res.status(200);
GBLogEx.info(min, `Meta callback OK. ${JSON.stringify(req.query)}`);
} else {
res.status(401);
}
res.end();
if (req.query['hub.mode'] === 'subscribe') {
const val = req.query['hub.verify_token'];
if (val === process.env.META_CHALLENGE) {
res.send(req.query['hub.challenge']);
res.status(200);
GBLogEx.info(min, `Meta callback OK. ${JSON.stringify(req.query)}`);
} else {
res.status(401);
return;
}
res.end();
return;
}
let whatsAppDirectLine = min.whatsAppDirectLine;
let whatsAppDirectLine = min.whatsAppDirectLine;
// Not meta, multiples bots on root bot.
// Not meta, multiples bots on root bot.
if (!req.body.object) {
const to = req.body.To.replace(/whatsapp\:\+/gi, '');
whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
}
if (!req.body.object) {
const to = req.body.To.replace(/whatsapp\:\+/gi, '');
whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
}
await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
}).bind(min);
if (whatsAppDirectLine) {
await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
}
})
.bind(min);
GBDeployer.mountGBKBAssets(`${botId}.gbkb`, botId, `${botId}.gbkb`);
// Loads API.
await this.ensureAPI();
}
public static getProviderName(req: any, res: any) {
@ -510,9 +515,7 @@ export class GBMinService {
* on https://<gbhost>/<BotId>/token URL.
*/
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) {
server.get(`/${min.instance.botId}/token`, async (req, res) => {
let tokenName = req.query['value'];
if (!tokenName) {
tokenName = '';
@ -535,9 +538,7 @@ export class GBMinService {
if (tokenName) {
const code = req?.query?.code;
let url = urlJoin(
host,
tenant, 'oauth/token');
let url = urlJoin(host, tenant, 'oauth/token');
let buff = new Buffer(`${clientId}:${clientSecret}`);
const base64 = buff.toString('base64');
@ -549,14 +550,14 @@ export class GBMinService {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'grant_type': 'authorization_code',
'code': code
grant_type: 'authorization_code',
code: code
})
};
const result = await fetch(url, options);
if (result.status != 200) {
throw new Error(`handleOAuthTokenRequests error: ${result.status}: ${result.statusText}.`)
throw new Error(`handleOAuthTokenRequests error: ${result.status}: ${result.statusText}.`);
}
const text = await result.text();
@ -564,24 +565,31 @@ export class GBMinService {
// Saves token to the database.
await this.adminService.setValue(instance.instanceId,
`${tokenName}accessToken`, token['accessToken'] ? token['accessToken'] : token['access_token']);
await this.adminService.setValue(instance.instanceId,
`${tokenName}refreshToken`, token['refreshToken'] ? token['refreshToken'] : token['refresh_token']);
await this.adminService.setValue(
instance.instanceId,
`${tokenName}accessToken`,
token['accessToken'] ? token['accessToken'] : token['access_token']
);
await this.adminService.setValue(
instance.instanceId,
`${tokenName}refreshToken`,
token['refreshToken'] ? token['refreshToken'] : token['refresh_token']
);
await this.adminService.setValue(instance.instanceId,
`${tokenName}expiresOn`, token['expiresOn'] ?
token['expiresOn'].toString() :
new Date(Date.now() + (token['expires_in'] * 1000)).toString());
await this.adminService.setValue(
instance.instanceId,
`${tokenName}expiresOn`,
token['expiresOn']
? token['expiresOn'].toString()
: new Date(Date.now() + token['expires_in'] * 1000).toString()
);
await this.adminService.setValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`, null);
}
else {
} else {
const authenticationContext = new AuthenticationContext.AuthenticationContext(
urlJoin(
tokenName ? host : min.instance.authenticatorAuthorityHostUrl,
tokenName ? tenant : min.instance.authenticatorTenant)
tokenName ? tenant : min.instance.authenticatorTenant
)
);
const resource = 'https://graph.microsoft.com';
@ -595,26 +603,24 @@ export class GBMinService {
tokenName ? clientSecret : instance.marketplacePassword,
async (err, token) => {
if (err) {
const msg = `handleOAuthTokenRequests: Error acquiring token: ${err}`;
GBLog.error(msg);
res.send(msg);
} else {
// Saves token to the database.
await this.adminService.setValue(instance.instanceId, `${tokenName}accessToken`, token['accessToken']);
await this.adminService.setValue(instance.instanceId, `${tokenName}refreshToken`, token['refreshToken']);
await this.adminService.setValue(instance.instanceId, `${tokenName}expiresOn`, token['expiresOn'].toString());
await this.adminService.setValue(
instance.instanceId,
`${tokenName}expiresOn`,
token['expiresOn'].toString()
);
await this.adminService.setValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`, null);
}
}
);
}
// Inform the home for default .gbui after finishing token retrival.
@ -633,8 +639,9 @@ export class GBMinService {
min.instance.authenticatorTenant,
'/oauth2/authorize'
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId
}&redirect_uri=${urlJoin(process.env.BOT_URL, min.instance.botId, 'token')}`;
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.marketplaceId
}&redirect_uri=${urlJoin(process.env.BOT_URL, min.instance.botId, 'token')}`;
GBLogEx.info(min, `HandleOAuthRequests: ${authorizationUrl}.`);
res.redirect(authorizationUrl);
});
@ -651,7 +658,6 @@ export class GBMinService {
botId = GBConfigService.get('BOT_ID');
}
// Loads by the botId itself or by the activationCode field.
let instance = await this.core.loadInstanceByBotId(botId);
@ -663,7 +669,6 @@ export class GBMinService {
if (instance !== null) {
// Gets the webchat token, speech token and theme.
const webchatTokenContainer = await this.getWebchatToken(instance);
const speechToken = instance.speechKey != undefined ? await this.getSTSToken(instance) : null;
let theme = instance.theme;
@ -673,29 +678,36 @@ export class GBMinService {
theme = `default.gbtheme`;
}
res.send(
JSON.stringify({
instanceId: instance.instanceId,
botId: botId,
theme: theme,
webchatToken: webchatTokenContainer.token,
speechToken: speechToken,
conversationId: webchatTokenContainer.conversationId,
authenticatorTenant: instance.authenticatorTenant,
authenticatorClientId: instance.marketplaceId,
paramLogoImageUrl: this.core.getParam(instance, 'Logo Image Url', null),
paramLogoImageAlt: this.core.getParam(instance, 'Logo Image Alt', null),
paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', null),
paramLogoImageHeight: this.core.getParam(instance, 'Logo Image Height', null),
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null),
logo: this.core.getParam(instance, 'Logo', null),
color1: this.core.getParam(instance, 'Color1', null),
color2: this.core.getParam(instance, 'Color2', null),
let logo = this.core.getParam(instance, 'Logo', null);
logo = logo ? urlJoin(instance.botId, 'cache', logo) : 'images/logo-gb.png';
let config = {
instanceId: instance.instanceId,
botId: botId,
theme: theme,
speechToken: speechToken,
authenticatorTenant: instance.authenticatorTenant,
authenticatorClientId: instance.marketplaceId,
paramLogoImageUrl: this.core.getParam(instance, 'Logo Image Url', null),
paramLogoImageAlt: this.core.getParam(instance, 'Logo Image Alt', null),
paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', null),
paramLogoImageHeight: this.core.getParam(instance, 'Logo Image Height', null),
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null),
logo: logo,
color1: this.core.getParam(instance, 'Color1', null),
color2: this.core.getParam(instance, 'Color2', null)
};
})
);
if (!GBConfigService.get('STORAGE_NAME')) {
config['domain'] = `http://localhost:${process.env.PORT}/directline/${botId}`;
} else {
const webchatTokenContainer = await this.getWebchatToken(instance);
(config['conversationId'] = webchatTokenContainer.conversationId),
(config['webchatToken'] = webchatTokenContainer.token);
}
res.send(JSON.stringify(config));
} else {
const error = `Instance not found while retrieving from .gbui web client: ${botId}.`;
res.sendStatus(error);
@ -753,12 +765,18 @@ export class GBMinService {
private async buildBotAdapter(instance: any, sysPackages: IGBPackage[], appPackages: IGBPackage[]) {
// MSFT stuff.
const adapter = new BotFrameworkAdapter({
let config = {
appId: instance.marketplaceId ? instance.marketplaceId : GBConfigService.get('MARKETPLACE_ID'),
appPassword: instance.marketplacePassword
? instance.marketplacePassword
: GBConfigService.get('MARKETPLACE_SECRET')
});
};
if (!GBConfigService.get('STORAGE_NAME')) {
startRouter(GBServer.globals.server, instance.botId);
config['clientOptions'] = { baseUri: `http://localhost:${process.env.PORT}` };
}
const adapter = new BotFrameworkAdapter(config);
const storage = new MemoryStorage();
const conversationState = new ConversationState(storage);
const userState = new UserState(storage);
@ -771,6 +789,28 @@ export class GBMinService {
// The minimal bot is built here.
const min = new GBMinInstance();
// Setups default BOT Framework dialogs.
min.userProfile = conversationState.createProperty('userProfile');
const dialogState = conversationState.createProperty('dialogState');
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt('textPrompt'));
min.dialogs.add(new AttachmentPrompt('attachmentPrompt'));
min.dialogs.add(new ConfirmPrompt('confirmPrompt'));
if (process.env.ENABLE_AUTH) {
min.dialogs.add(
new OAuthPrompt('oAuthPrompt', {
connectionName: 'OAuth2',
text: 'Please sign in to General Bots.',
title: 'Sign in',
timeout: 300000
})
);
}
min.botId = instance.botId;
min.bot = adapter;
min.userState = userState;
@ -793,6 +833,15 @@ export class GBMinService {
min['apiConversations'] = {};
min.packages = sysPackages;
const receiver = async (req, res) => {
await this.receiver(req, res, conversationState, min, instance, GBServer.globals.appPackages);
};
let url = `/api/messages/${instance.botId}`;
GBServer.globals.server.post(url, receiver);
url = `/api/messages`;
GBServer.globals.server.post(url, receiver);
// NLP Manager.
const manager = new NlpManager({ languages: ['pt'], forceNER: true });
@ -802,7 +851,6 @@ export class GBMinService {
GBServer.globals.minBoot = min;
GBServer.globals.minBoot.instance.marketplaceId = GBConfigService.get('MARKETPLACE_ID');
GBServer.globals.minBoot.instance.marketplacePassword = GBConfigService.get('MARKETPLACE_SECRET');
}
if (min.instance.facebookWorkplaceVerifyToken) {
@ -860,8 +908,7 @@ export class GBMinService {
await min.whatsAppDirectLine.setup(true);
} else {
if (min !== minBoot && minBoot.instance.whatsappServiceKey
&& min.instance.webchatKey) {
if (min !== minBoot && minBoot.instance.whatsappServiceKey && min.instance.webchatKey) {
min.whatsAppDirectLine = new WhatsappDirectLine(
min,
min.botId,
@ -882,26 +929,6 @@ export class GBMinService {
WhatsappDirectLine.botsByNumber[botNumber] = min.whatsAppDirectLine;
}
// Setups default BOT Framework dialogs.
min.userProfile = conversationState.createProperty('userProfile');
const dialogState = conversationState.createProperty('dialogState');
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt('textPrompt'));
min.dialogs.add(new AttachmentPrompt('attachmentPrompt'));
min.dialogs.add(new ConfirmPrompt('confirmPrompt'));
if (process.env.ENABLE_AUTH) {
min.dialogs.add(
new OAuthPrompt('oAuthPrompt', {
connectionName: 'OAuth2',
text: 'Please sign in to General Bots.',
title: 'Sign in',
timeout: 300000
})
);
}
return { min, adapter, conversationState };
}
@ -1087,7 +1114,10 @@ export class GBMinService {
const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
if (startDialog) {
await sec.setParam(userId, 'welcomed', 'true');
GBLogEx.info(min, `Auto start (teams) dialog is now being called: ${startDialog} for ${min.instance.botId}...`);
GBLogEx.info(
min,
`Auto start (teams) dialog is now being called: ${startDialog} for ${min.instance.botId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
}
}
@ -1095,7 +1125,8 @@ export class GBMinService {
// Required for F0 handling of persisted conversations.
GBLogEx.info(min,
GBLogEx.info(
min,
`Input> ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})`
);
@ -1104,7 +1135,6 @@ export class GBMinService {
const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
if (context.activity.type === 'installationUpdate') {
GBLogEx.info(min, `Bot installed on Teams.`);
} else if (context.activity.type === 'conversationUpdate' && context.activity.membersAdded.length > 0) {
@ -1133,7 +1163,8 @@ export class GBMinService {
) {
min['conversationWelcomed'][step.context.activity.conversation.id] = true;
GBLogEx.info(min,
GBLogEx.info(
min,
`Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
@ -1147,7 +1178,6 @@ export class GBMinService {
} else if (context.activity.type === 'message') {
// Processes messages activities.
await this.processMessageActivity(context, min, step, pid);
} else if (context.activity.type === 'event') {
// Processes events activities.
@ -1155,8 +1185,9 @@ export class GBMinService {
await this.processEventActivity(min, user, context, step);
}
} catch (error) {
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
GBLog.error(msg);
await min.conversationalService.sendText(
@ -1170,7 +1201,17 @@ export class GBMinService {
};
try {
await adapter['processActivity'](req, res, handler);
if (!GBConfigService.get('STORAGE_NAME')) {
const context = adapter['createContext'](req);
context['_activity'] = context.activity.body;
await handler(context);
// Return status
res.status(200);
res.end();
} else {
await adapter['processActivity'](req, res, handler);
}
} catch (error) {
if (error.code === 401) {
GBLog.error('Calling processActivity due to Signing Key could not be retrieved error.');
@ -1209,7 +1250,10 @@ export class GBMinService {
const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
if (startDialog && !min['conversationWelcomed'][step.context.activity.conversation.id]) {
user.welcomed = true;
GBLogEx.info(min, `Auto start (web 2) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`);
GBLogEx.info(
min,
`Auto start (web 2) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
}
} else if (context.activity.name === 'updateToken') {
@ -1263,7 +1307,6 @@ export class GBMinService {
}
private async handleUploads(min, step, user, params, autoSave) {
// Prepare Promises to download each attachment and then execute each Promise.
if (
@ -1294,7 +1337,6 @@ export class GBMinService {
GBServer.globals.files[handle] = gbfile;
if (!min.cbMap[user.userId] && autoSave) {
const result = await t['internalAutoSave']({ min: min, handle: handle });
await min.conversationalService.sendText(
min,
@ -1303,12 +1345,9 @@ export class GBMinService {
);
return;
}
else {
} else {
return gbfile;
}
} else {
await this.sendActivity('Error uploading file. Please,start again.');
}
@ -1367,7 +1406,8 @@ export class GBMinService {
context.activity.text = context.activity.text.trim();
const member = context.activity.from;
let memberId, email;
let memberId = null,
email = null;
// Processes e-mail from id in case of Teams messages.
@ -1399,10 +1439,8 @@ export class GBMinService {
const userId = user.userId;
const params = user.params ? JSON.parse(user.params) : {};
let message: GuaribasConversationMessage;
if (process.env.PRIVACY_STORE_MESSAGES === 'true') {
// Adds message to the analytics layer.
const analytics = new AnalyticsService();
@ -1420,7 +1458,6 @@ export class GBMinService {
userId,
context.activity.text
);
}
}
@ -1439,7 +1476,8 @@ export class GBMinService {
) {
await sec.setParam(userId, 'welcomed', 'true');
min['conversationWelcomed'][step.context.activity.conversation.id] = true;
GBLogEx.info(min,
GBLogEx.info(
min,
`Auto start (4) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
@ -1500,21 +1538,18 @@ export class GBMinService {
} else {
// Removes unwanted chars in input text.
step.context.activity['originalText'] = context.activity.text;
const text = await GBConversationalService.handleText(min, user, step, context.activity.text);
step.context.activity['originalText']
step.context.activity['originalText'];
step.context.activity['text'] = text;
if (notes && text && text !== "") {
if (notes && text && text !== '') {
const sys = new SystemKeywords();
await sys.note({ pid, text });
await step.context.sendActivity('OK.');
return;
}
// Checks for bad words on input text.
const hasBadWord = wash.check(step.context.activity.locale, text);
@ -1549,7 +1584,7 @@ export class GBMinService {
}
} else {
if (min.cbMap[userId] && min.cbMap[userId].promise === '!GBHEAR') {
min.cbMap[userId].promise = step.context.activity['originalText'];;
min.cbMap[userId].promise = step.context.activity['originalText'];
}
// If there is a dialog in course, continue to the next step.
@ -1557,8 +1592,9 @@ export class GBMinService {
try {
await step.continueDialog();
} catch (error) {
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
GBLog.error(msg);
await min.conversationalService.sendText(
min,
@ -1601,7 +1637,6 @@ export class GBMinService {
}
public async ensureAPI() {
const mins = GBServer.globals.minInstances;
function getRemoteId(ctx: Koa.Context) {
@ -1611,14 +1646,11 @@ export class GBMinService {
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 {
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...');
}
@ -1629,11 +1661,9 @@ export class GBMinService {
let proxies = {};
await CollectionUtil.asyncForEach(mins, async min => {
let dialogs = {};
await CollectionUtil.asyncForEach(Object.values(min.scriptMap), async script => {
dialogs[script] = async (data) => {
dialogs[script] = async data => {
let sec = new SecService();
const user = await sec.ensureUser(
min,
@ -1649,13 +1679,7 @@ export class GBMinService {
if (script === 'start') {
pid = GBVMService.createProcessInfo(user, min, 'api', null);
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
const response = await client.apis.Conversations.Conversations_StartConversation();
min['apiConversations'][pid] = { conversation: response.obj, client: client };
@ -1668,7 +1692,7 @@ export class GBMinService {
ret = pid;
}
return ret;
}
};
});
const proxy = {
@ -1686,10 +1710,10 @@ export class GBMinService {
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 { },
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();
},
@ -1699,16 +1723,8 @@ export class GBMinService {
}
};
GBServer.globals.server.apiServer = createKoaHttpServer(
GBVMService.API_PORT,
getRemoteId, { prefix: `api/v3` });
createRpcServer(
proxies,
GBServer.globals.server.apiServer,
opts
);
GBServer.globals.server.apiServer = createKoaHttpServer(GBVMService.API_PORT, getRemoteId, { prefix: `api/v3` });
createRpcServer(proxies, GBServer.globals.server.apiServer, opts);
}
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -0,0 +1,360 @@
import bodyParser from 'body-parser';
import express from 'express';
import fetch from 'isomorphic-fetch';
import moment from 'moment';
import * as uuidv4 from 'uuid';
import { IActivity, IBotData, IConversation, IConversationUpdateActivity, IMessageActivity } from './types';
import { GBConfigService } from '../GBConfigService.js';
const expiresIn = 1800;
const conversationsCleanupInterval = 10000;
const conversations: { [key: string]: IConversation } = {};
const botDataStore: { [key: string]: IBotData } = {};
export const getRouter = (
serviceUrl: string,
botUrl: string,
conversationInitRequired = true,
botId
): express.Router => {
const router = express.Router();
router.use(bodyParser.json()); // for parsing application/json
router.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
router.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, PATCH, OPTIONS');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, Authorization, x-ms-bot-agent'
);
next();
});
// CLIENT ENDPOINT
router.options(`/directline/${botId}/`, (req, res) => {
res.status(200).end();
});
// Creates a conversation
const reqs = (req, res) => {
const conversationId: string = uuidv4.v4().toString();
conversations[conversationId] = {
conversationId,
history: []
};
console.log('Created conversation with conversationId: ' + conversationId);
const activity = createConversationUpdateActivity(serviceUrl, conversationId);
fetch(botUrl, {
method: 'POST',
body: JSON.stringify(activity),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
res.status(response.status).send({
conversationId,
expiresIn
});
});
};
router.post('/v3/directline/conversations', reqs);
router.post(`/directline/${botId}/conversations`, reqs);
router.post(`/directline/conversations`, reqs);
// Reconnect API
router.get('/v3/directline/conversations/:conversationId', (req, res) => {
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
res.status(200).send(conversation);
} else {
// Conversation was never initialized
res.status(400).send();
}
console.warn('/v3/directline/conversations/:conversationId not implemented');
});
// Gets activities from store (local history array for now)
router.get(`/directline/${botId}/conversations/:conversationId/activities`, (req, res) => {
const watermark = req.query.watermark && req.query.watermark !== 'null' ? Number(req.query.watermark) : 0;
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
// If the bot has pushed anything into the history array
if (conversation.history.length > watermark) {
const activities = conversation.history.slice(watermark);
res.status(200).json({
activities,
watermark: watermark + activities.length
});
} else {
res.status(200).send({
activities: [],
watermark
});
}
} else {
// Conversation was never initialized
res.status(400).send();
}
});
// Sends message to bot. Assumes message activities
router.post(`/directline/${botId}/conversations/:conversationId/activities`, (req, res) => {
const incomingActivity = req.body;
// Make copy of activity. Add required fields
const activity = createMessageActivity(incomingActivity, serviceUrl, req.params.conversationId);
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
conversation.history.push(activity);
fetch(botUrl, {
method: 'POST',
body: JSON.stringify(activity),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
res.status(response.status).json({ id: activity.id });
});
} else {
// Conversation was never initialized
res.status(400).send();
}
});
router.post('/v3/directline/conversations/:conversationId/upload', (req, res) => {
console.warn('/v3/directline/conversations/:conversationId/upload not implemented');
});
router.get('/v3/directline/conversations/:conversationId/stream', (req, res) => {
console.warn('/v3/directline/conversations/:conversationId/stream not implemented');
});
// BOT CONVERSATION ENDPOINT
router.post('/v3/conversations', (req, res) => {
console.warn('/v3/conversations not implemented');
});
router.post('/v3/conversations/:conversationId/activities', (req, res) => {
let activity: IActivity;
activity = req.body;
activity.id = uuidv4.v4();
activity.from = { id: 'id', name: 'Bot' };
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
conversation.history.push(activity);
res.status(200).send();
} else {
// Conversation was never initialized
res.status(400).send();
}
});
router.post('/v3/conversations/:conversationId/activities/:activityId', (req, res) => {
let activity: IActivity;
activity = req.body;
activity.id = uuidv4.v4();
activity.from = { id: 'id', name: 'Bot' };
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
conversation.history.push(activity);
res.status(200).send();
} else {
// Conversation was never initialized
res.status(400).send();
}
});
router.get('/v3/conversations/:conversationId/members', (req, res) => {
console.warn('/v3/conversations/:conversationId/members not implemented');
});
router.get('/v3/conversations/:conversationId/activities/:activityId/members', (req, res) => {
console.warn('/v3/conversations/:conversationId/activities/:activityId/members');
});
// BOTSTATE ENDPOINT
router.get('/v3/botstate/:channelId/users/:userId', (req, res) => {
console.log('Called GET user data');
getBotData(req, res);
});
router.get('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
console.log('Called GET conversation data');
getBotData(req, res);
});
router.get('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
console.log('Called GET private conversation data');
getBotData(req, res);
});
router.post('/v3/botstate/:channelId/users/:userId', (req, res) => {
console.log('Called POST setUserData');
setUserData(req, res);
});
router.post('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
console.log('Called POST setConversationData');
setConversationData(req, res);
});
router.post('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
setPrivateConversationData(req, res);
});
router.delete('/v3/botstate/:channelId/users/:userId', (req, res) => {
console.log('Called DELETE deleteStateForUser');
deleteStateForUser(req, res);
});
return router;
};
/**
* @param app The express app where your offline-directline endpoint will live
* @param port The port where your offline-directline will be hosted
* @param botUrl The url of the bot (e.g. http://127.0.0.1:3978/api/messages)
* @param conversationInitRequired Requires that a conversation is initialized before it is accessed, returning a 400
* when not the case. If set to false, a new conversation reference is created on the fly. This is true by default.
*/
export const initializeRoutes = (
app: express.Express,
port: number,
botUrl: string,
conversationInitRequired = true,
botId
) => {
conversationsCleanup();
const directLineEndpoint = `http://127.0.0.1:${port}`;
const router = getRouter(directLineEndpoint, botUrl, conversationInitRequired, botId);
app.use(router);
};
const getConversation = (conversationId: string, conversationInitRequired: boolean) => {
// Create conversation on the fly when needed and init not required
if (!conversations[conversationId] && !conversationInitRequired) {
conversations[conversationId] = {
conversationId,
history: []
};
}
return conversations[conversationId];
};
const getBotDataKey = (channelId: string, conversationId: string, userId: string) => {
return `$${channelId || '*'}!${conversationId || '*'}!${userId || '*'}`;
};
const setBotData = (channelId: string, conversationId: string, userId: string, incomingData: IBotData): IBotData => {
const key = getBotDataKey(channelId, conversationId, userId);
const newData: IBotData = {
eTag: new Date().getTime().toString(),
data: incomingData.data
};
if (incomingData) {
botDataStore[key] = newData;
} else {
delete botDataStore[key];
newData.eTag = '*';
}
return newData;
};
const getBotData = (req: express.Request, res: express.Response) => {
const key = getBotDataKey(req.params.channelId, req.params.conversationId, req.params.userId);
console.log('Data key: ' + key);
res.status(200).send(botDataStore[key] || { data: null, eTag: '*' });
};
const setUserData = (req: express.Request, res: express.Response) => {
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
};
const setConversationData = (req: express.Request, res: express.Response) => {
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
};
const setPrivateConversationData = (req: express.Request, res: express.Response) => {
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
};
export const start = (server, botId) => {
const port = GBConfigService.getServerPort();
initializeRoutes(server, Number(port), `http://127.0.0.1:${port}/api/messages/${botId}`, null, botId);
if (botId === 'default') {
initializeRoutes(server, Number(port), `http://127.0.0.1:${port}/api/messages`, null, botId);
}
};
const deleteStateForUser = (req: express.Request, res: express.Response) => {
Object.keys(botDataStore).forEach(key => {
if (key.endsWith(`!{req.query.userId}`)) {
delete botDataStore[key];
}
});
res.status(200).send();
};
// CLIENT ENDPOINT HELPERS
const createMessageActivity = (
incomingActivity: IMessageActivity,
serviceUrl: string,
conversationId: string
): IMessageActivity => {
return {
...incomingActivity,
channelId: 'emulator',
serviceUrl,
conversation: { id: conversationId },
id: uuidv4.v4()
};
};
const createConversationUpdateActivity = (serviceUrl: string, conversationId: string): IConversationUpdateActivity => {
const activity: IConversationUpdateActivity = {
type: 'conversationUpdate',
channelId: 'emulator',
serviceUrl,
conversation: { id: conversationId },
id: uuidv4.v4(),
membersAdded: [],
membersRemoved: [],
from: { id: 'offline-directline', name: 'Offline Directline Server' }
};
return activity;
};
const conversationsCleanup = () => {
setInterval(() => {
const expiresTime = moment().subtract(expiresIn, 'seconds');
Object.keys(conversations).forEach(conversationId => {
if (conversations[conversationId].history.length > 0) {
const lastTime = moment(
conversations[conversationId].history[conversations[conversationId].history.length - 1].localTimestamp
);
if (lastTime < expiresTime) {
delete conversations[conversationId];
console.log('deleted cId: ' + conversationId);
}
}
});
}, conversationsCleanupInterval);
};

View file

@ -0,0 +1,66 @@
export interface IUser {
id: string,
name: string
}
export interface IChannelAccount {
id?: string,
name?: string,
}
export interface IConversationAccount extends IChannelAccount {
isGroup?: boolean,
}
export interface IAttachment {
contentType?: string,
contentUrl?: string,
content?: any,
name?: string,
thumbnailUrl?: string,
}
export interface IEntity {
type?: string,
}
export interface IActivity {
type?: string,
id?: string,
serviceUrl?: string,
timestamp?: string,
localTimestamp?: string,
channelId?: string,
from?: IChannelAccount,
conversation?: IConversationAccount,
recipient?: IChannelAccount,
replyToId?: string,
channelData?: any,
}
export interface IMessageActivity extends IActivity {
locale?: string,
text?: string,
summary?: string,
textFormat?: string,
attachmentLayout?: string,
attachments?: IAttachment[],
entities?: IEntity[],
}
export interface IBotData {
eTag: string;
data: any;
}
export interface IConversation {
conversationId: string,
history?: IActivity[]
}
export interface IConversationUpdateActivity extends IActivity {
membersAdded?: IChannelAccount[],
membersRemoved?: IChannelAccount[],
topicName?: string,
historyDisclosed?: boolean,
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |

View file

@ -1,77 +0,0 @@
' General Bots Copyright (c) pragmatismo.cloud. All rights reserved. Licensed under the AGPL-3.0.
' Rules from http://jsfiddle.net/roderick/dym05hsy
talk "How many installments do you want to pay your Credit?"
hear installments
if installments > 60 then
talk "The maximum number of payments is 60"
else
talk "What is the amount requested?"
hear amount
if amount >100000 then
talk "We are sorry, we can only accept proposals bellow 100k"
else
talk "What is the best due date?"
hear dueDate
interestRate = 0
adjustment = 0
if installments < 12 then
interestRate = 1.60
adjustment = 0.09748
end if
if installments > 12 and installments < 18 then
interestRate = 1.66
adjustment = 0.06869
end if
if installments > 18 and installments < 36 then
interestRate = 1.64
adjustment = 0.05397
end if
if installments > 36 and installments < 48 then
interestRate = 1.62
adjustment = 0.03931
end if
if installments > 48 and installments < 60 then
interestRate = 1.70
adjustment = 0.03270
end if
if installments = 60 then
interestRate = 1.79
adjustment = 0.02916
end if
if installments > 60 then
talk "The maximum number of payments is 60"
end if
nInstallments = parseInt(installments)
vamount = parseFloat(amount)
initialPayment = vamount * 0.3 ' 30% of the value
tac = 800
adjustment = 1.3
totalValue = amount - initialPayment + tac
paymentValue = totalValue * adjustment
finalValue = paymentValue * nInstallments + initialPayment
talk "Congratulations! Your credit analysis is **done**:"
talk "First payment: **" + initialPayment + "**"
talk "Payment value: **" + paymentValue + "**"
talk "Interest Rate: **" + interestRate + "%**"
talk "Total Value: **" + totalValue + "**"
talk "Final Value: **" + finalValue + "**"
end if
end if

View file

@ -1,50 +0,0 @@
' General Bots Copyright (c) pragmatismo.cloud. All rights reserved. Licensed under the AGPL-3.0.
talk "Quer pagar quanto?"
hear amount
talk "Para onde?"
hear address
if amount < 5 then
talk "O mínimo que vendo este produto é 5."
else
if address is in "Rio" then
get payment amount
delivery to address
else
talk "Vou ver se tenho um parceiro para entregar aí e te falo. Eu só entrego no Rio."
end if
end if
talk "Valeu!"
Falar "Qual seu nome?"
Ouvir nome
Falar "Informe seu CEP, por favor:"
Ouvir CEP
Address = CEP
Confira seu endereço:
Address.Street
Address.Number
Falar "Manda sua localização para eu pedir a alguém para sair agora com o seu pedido"
Hear Location
SAve "Pedidos.xlsx", Nome, From, Location.Street, Location.Number
Falar "Manda sua localização que eu encontro o posto mais próximo"
Hear Location
Find "Postos.xlsx", "Endereço=" + Location

View file

@ -1,29 +0,0 @@
if consulta = "cpf" then
talk "Qual seu CPF?"
hear cpf
talk "Aguarde alguns instantes que eu localizo seu cadastro..."
row = find "Cadastro.xlsx", "CPF=" + cpf
if row != null then
talk "Oi, " + row.Nome + "Tudo bem? "
talk "Seu código de cliente é " + row.Cod
talk "Vamos te enviar o pedido para seu endereço em: " + row.Endereço
send file "boleta.pdf", "Pague já e evite multas!"
else
talk "Tente novamente."
end if
else
talk "Qual seria seu código?"
hear cod
talk "Aguarde alguns instantes que eu localizo seu cadastro..."
row = find "Cadastro.xlsx", "Cod=" + cod
if row != null then
talk "Oi, " + row.Nome + "Tudo bem? "
talk "Seu CPF é " + row.CPF
talk "Vamos te enviar o pedido para seu endereço em: " + row.Endereço
send file "boleta.pdf", "Pague já e evite multas!"
else
talk "Tente novamente."
end if
end if

View file

@ -1,5 +0,0 @@
talk “Olá! Seja bem vinda(o)!”
X = find “campanhas.xlsx”, “nome=1239” OR TALK “Desculpe-me, não localizei seu nome.”
talk “opa, vamos lá!” + x.nome

View file

@ -1,12 +0,0 @@
' General Bots Copyright (c) pragmatismo.cloud. All rights reserved. Licensed under the AGPL-3.0.
talk "Quer pagar quanto?"
hear amount
if amount < 5 then
talk "O mínimo que vendo este produto é 5."
else
get payment amount
end if
talk "Valeu!"

View file

@ -1,9 +0,0 @@
value = get "list.xslx", "A1:A1"
set "list.xslx", "A1:A1", "value"
myVar = find "chamadosbug748.xlsx", "CHAMADO=" + "5521979047667-44129-10"
status="alterado"
set "chamadosbug748.xlsx", "E" + myVar.line + ":E" + myVar.line, status
res = get "chamadosbug748.xlsx", "E" + myVar.line + ":E" + myVar.line
talk "Obrigado e até a próxima e veja bem, o resultado é esse: " + res

View file

@ -1,6 +0,0 @@
user = get "http://server/path"
user = post "http://server/path", "data"
Talk “ The user area is” + user.area, user.validuser, user.user

View file

@ -1,26 +0,0 @@
talk "Qual seu pedido?"
hear pedido
talk "Qual seu e-mail?"
hear email
talk "Obrigado, seu pedido será processado e retornamos."
ask payment
talk "Qual seu pedido?"
hear pedido
talk "Obrigado. Agora informe seu nome:"
Hear nome
Talk "Obrigado" + nome + "! Agora falta saber onde vamos entregar. Qual seu endereço?"
Hear endereço
Save "BistroBot.gbdata\pedidos.xlsx", nome, pedido, endereço, phone
Talk "Aguarde, enquanto eu calculo o valor total do seu pedido. Volto em alguns minutos, aguarde!"
Total = Ask "+5521999995555", "Qual o valor para o pedido da(o)" + nome +"?"
ask payment Total
Talk "Qual seunome? "
Hear nome
Talk "Qual o valor pedido?"
Hear pedido
Save "Contoso.gbdata\ Aprovações.xlsx", nome, pedido, phone
Ask "+5521999995555", "Aprove por favor o pedido tal "
Talk "Sua aprovação foi enviada, aguarde."

View file

@ -1,11 +0,0 @@
talk "O seu nome, por favor?"
hear nome
talk "Qual seu pedido?"
hear details
talk "Valeu " + nome + "! Agora falta saber onde vamos entregar. \nQual seu endereço?"
hear address
save "Pedidos.xlsx", id, nome, from, address, details, today

View file

@ -1,37 +0,0 @@
' General Bots Copyright (c) pragmatismo.cloud. All rights reserved. Licensed under the AGPL-3.0.
talk "Please, tell me what is the Bot name?"
hear title
talk "If you tell me your username/password, I can show service subscription list to you."
talk "What is your Username (eg.: human@domain.bot)"
hear email
talk "What is your Password"
hear password
talk "Your password? (Will be discarded after sigining process)"
talk "Can you describe in a few words what the bot is about?"
hear description
talk "Please, paste the Subscription ID (Azure):"
hear subscriptionId
talk "Please, provide the cloud location just like 'westus'?"
hear location
talk "Please, provide the Authoring Key for NLP service (LUIS)?"
hear authoringKey
talk "Sorry, this part cannot be automated yet due to Microsoft schedule, please go to https://apps.dev.microsoft.com/portal/register-app to generate manually an App ID and App Secret."
wait 5
talk "Please, provide the App ID you just generated:"
hear appId
talk "Please, provide the Generated Password:"
hear appPassword
talk "Now, I am going to create a Bot farm... Wait 5 minutes or more..."
create a bot farm using title, email, password, location, authoringKey, appId, appPassword, subscriptionId

View file

@ -1,25 +0,0 @@
talk "qual seu nome?"
hear nome
talk "qual seu e-mail?"
hear email
documento = "meutemplate.docx", nome, email,
send file documento
save file documento, "curriculos/nome" + ".docx"
' $name
'$trabalho
'Experiência
'$ano1
'$oquefoifeitoAno1
'$ano2
'$oquefoifeitoAno2
'$ano3
'$oquefoifeitoAno1

View file

@ -1,13 +0,0 @@
rem hi
talk "Qual seu nome?"
hear name
talk "Qual seu CPF?"
hear CPF
talk "Por que você abrirá este chamado?"
hear translated motivo
talk "Seu nome: " + name
talk "Você disse em Português " + motivo.

View file

@ -1,3 +0,0 @@
Últimas notícias
Contato
Ofertas

View file

@ -1,6 +0,0 @@
{
"botId":"pragmatismo-ai-prd",
"version": "1.0.0",
"description": "Bot pragmatismo.",
"license": "Private"
}

View file

@ -1,58 +0,0 @@
{
"children": [
{
"title": "Bots & AI",
"description": "Bots & inteligência artificial.",
"id": "bots-ai",
"children": [
{
"title": "General Bots",
"description": "Plataforma de bots da pragmatismo.cloud.",
"id": "general-bots"
},
{
"title": "Cortana Intelligence Suite",
"description": "Suite de Big Data da Microsoft.",
"id": "cortana"
}
]
},
{
"title": "Produtividade",
"description": "Artigos sobre sistemas Internos.",
"id": "produtividade",
"children": [
{
"title": "Microsoft Project Online",
"description": "Artigos sobre o Microsoft Project Online.",
"id": "msproject"
},
{
"title": "SharePoint",
"description": "SharePoint, sites e serviços.",
"id": "sharepoint"
},
{
"title": "Office 365",
"description": "Plataforma colaborativa moderna da Microsoft.",
"id": "office365"
},
{
"title": "Microsoft Dynamics",
"description": "Artigos sobre plataforma de CRM da Microsoft.",
"id": "msdynamics"
},
{
"title": "Power BI",
"description": "Dashboards modernos e intuitivos.",
"id": "powerbi"
}
]
},
{
"title": "Sobre",
"description": "Artigos sobre o Bot da pragmatismo.cloud",
"id": "sobre"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.
1 subjects from to question answer
2 geral menu answer GB oi

View file

@ -1,171 +0,0 @@
body {
background-color: #dadada !important;
}
.loader {
opacity: 1 !important;
filter: opacity(100%);
}
.gb-quality-button-yes {
width: 54px;
text-decoration: none;
text-transform: uppercase;
background-color: green;
color: white;
padding: 2px;
cursor: pointer;
transition: 0.9s;
transition-delay: 0.3s;
border: none;
}
.gb-quality-button-no {
width: 54px;
text-decoration: none;
text-transform: uppercase;
background-color: red;
color: white;
padding: 2px;
cursor: pointer;
transition: 0.9s;
transition-delay: 0.3s;
border: none;
}
.gb-markdown-player-quality {
background-color: #f5e4a8;
padding: 4px;
position: absolute;
bottom: 14px;
left: -9px;
width: 100%;
border-radius: 5px;
color: #52514e;
border: 1px solid #b2a46e;
text-align: center;
}
.media-player {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif !important;
}
.media-player-container {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.media-player-link {
cursor: pointer !important;
}
.gb-bullet-player {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif !important;
background: white;
height: 95%;
overflow-y: scroll;
}
.gb-bullet-player-item {
cursor: pointer;
}
.gb-image-player-outter-div {}
.gb-image-player-img {}
.gb-bullet-player-outter-div {}
.gb-video-player-wrapper {
width: 100%;
height: 100%;
}
.gb-video-react-player {
position: relative;
left: 50%;
transform: translateX(-50%);
}
body {
display: flex;
}
.App {
min-height: 100vh;
}
.App .body {
display: flex;
flex-direction: row;
}
.body {
flex-basis: 12em;
/* Default value of the element before distribuing the remaing space */
flex-grow: 0;
/* Defined the ability to groe */
flex-shrink: 0;
/* Defines the ability to shrink */
max-width: 12em;
order: -1;
}
body {
margin: 0;
overflow: hidden;
}
.media-player-container {
overflow: auto;
max-height: 90%;
font-family: "Open Sans", sans-serif;
background: white;
}
.media-player-scroll {
height: 1500px;
}
@media screen and (max-width: 1000px) {
.media-player-scroll h1 {
font-size: 15px;
}
.media-player-scroll p {
font-size: 12px;
}
.media-player-scroll li {
font-size: 12px;
}
}
@media screen and (max-width: 451px) {
.media-player {
position: relative;
zoom: 90%;
height: 94% !important;
width: 95% !important;
background-repeat: no-repeat;
margin-top: 10px;
margin-left: 10px;
margin-right: -40px;
}
.gb-markdown-player-quality {
bottom: -1px;
left: -3px;
}
}
@media screen and (min-width: 451px) {
.media-player {
position: relative;
zoom: 90%;
height: 100% !important;
width: 95% !important;
background-repeat: no-repeat;
margin-top: 10px;
margin-left: 20px;
margin-right: -40px;
}
}

View file

@ -1,47 +0,0 @@
.webchat > div {
height: 100%;
width: 100%;
}
.webchat {
background-color: white !important;
left: 57%;
right: 0%;
top: 0;
bottom: 0;
overflow: auto !important;
}
@media screen and (max-width: 1000px) {
.webchat {
display: inline-block !important;
width: 96% !important;
height: 57% !important;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
left: 50%;
top: 41%;
position: absolute;
margin-left: -48%;
}
}
@media screen and (min-width: 1000px) {
.webchat {
display: inline-block !important;
width: 50% !important;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
top: 1% !important;
height: 96%;
position: absolute;
right: 0;
margin-left: -8%;
position: absolute;
top: 0;
bottom: 0;
border-bottom: 4px solid #4f4f4f;
}
}

View file

@ -1,18 +0,0 @@
.body .container { padding: 1em;width: 100%;height: 100% }
.body .ms-Breadcrumb {
margin-bottom: 1em;
margin-top: 0;
}
.body .selection {
height: calc(100vh - 16.5em);
overflow-x: auto;
}
.body .selection .selection-item {
display: flex;
padding: 0.5em;
}
.body .selection .selection-item .name { margin-left: 1em; }

View file

@ -1,8 +0,0 @@
.footer {
align-items: center;
background-color: #450a64;
display: flex;
justify-content: center;
}
.footer-container { color: white; }

View file

@ -1,31 +0,0 @@
@media screen and (max-width: 1000px) {
.player {
width: 93% !important;
height: 26% !important;
border: 7px solid #272727;
position: absolute;
top: 9%;
left: 50%;
margin-left: -48%;
background: url(../images/general-bot-background.jpg), WHITE;
background-repeat: no-repeat;
background-size: contain;
background-position: center;
}
}
@media screen and (min-width: 1000px) {
.player {
display: inline-block;
width: 46% !important;
height: 81% !important;
border: 7px solid #272727;
background: url(../images/general-bot-background.jpg), WHITE;
background-repeat: no-repeat;
background-size: contain;
background-position: center;
position: absolute;
left: 1%;
top: 15%;
}
}

View file

@ -1,7 +0,0 @@
.media {
margin-top: 20px;
height: 280px !important;
width: 200px !important;
}

View file

@ -1,22 +0,0 @@
.NavBar {
align-items: center;
display: flex;
justify-content: space-between;
padding: 0.2em 0.5em;
border-bottom-width: 1px;
color:black;
height: 100%;
}
/*
.logo {
padding-top: 4em;
}
*/
.NavBar .searchbox { width: 20em; }
.NavBar .searchbox .ms-SearchBox {
background-color: white;
margin-bottom: 0;
}

View file

@ -1,199 +0,0 @@
.ms-Nav {
background: #222;
color: white;
margin-top: 20px;
}
.ms-Nav-link {
color: white !important;
background-color: #222222 !important;
}
.ms-Nav-link a:active {
border-right: 2px solid white;
}
.ms-Nav-compositeLink .ms-Nav-chevronButton.ms-Nav-chevronButton--link {
background: #222222 !important;
}
.ms-Nav-compositeLink.is-selected .ms-Nav-chevronButton,
.ms-Nav-compositeLink.is-selected a {
padding-left: 70px !important;
}
html[dir="ltr"] .ms-Nav-compositeLink.is-selected .ms-Nav-chevronButton:after,
html[dir="ltr"] .ms-Nav-compositeLink.is-selected a:after {
border-left: none !important;
}
@media screen and (max-width: 419px) {
.sidebar {
display: inline-block !important;
background-color: #3f3f3f !important;
height: 8%;
width: 100% !important;
position: absolute;
top: 0;
left: 0;
}
.tittleSideBarMenu {
display: none;
}
.iconMenu {
color: #d1d1d1;
font-size: 13px;
display: inline;
margin-right: 20px;
}
.iconMenu:hover {
color: white;
}
.IconsMenu {
position: absolute;
top: 50%;
margin-top: -23px;
height: 22px;
width: 300px;
left: 50%;
margin-left: -150px;
text-align: center;
font-family: "Open Sans", sans-serif;
}
.iconText {
cursor: pointer;
}
}
@media screen and (min-width: 520px) and (max-width:1000px) {
.tittleSideBarMenu {
display: none;
}
.sidebar {
display: inline-block !important;
background-color: #3f3f3f !important;
height: 8%;
width: 100% !important;
position: absolute;
top: 0;
left: 0;
background-image: url(../images/bot-logo.png);
background-position: 2px 2px;
background-repeat: no-repeat;
background-size: contain;
}
.IconsMenu {
position: absolute;
top: 50%;
margin-top: -11px;
height: 22px;
width: 416px;
left: 100px !important;
margin-left: 0px !important;
text-align: center;
font-family: "Open Sans", sans-serif;
}
.iconMenu {
color: #d1d1d1;
}
.iconMenu:hover {
color: white;
}
}
@media screen and (min-width: 420px) and (max-width: 1000px) {
.sidebar {
display: inline-block !important;
background-color: #3f3f3f !important;
height: 8%;
width: 100% !important;
position: absolute;
top: 0;
left: 0;
}
.tittleSideBarMenu {
display: none;
}
.iconMenu {
color: #d1d1d1;
font-size: 14px;
display: inline;
margin-right: 20px;
}
.iconMenu:hover {
color: white;
}
.IconsMenu {
position: absolute;
top: 50%;
margin-top: -11px;
height: 22px;
width: 416px;
left: 50%;
margin-left: -208px;
text-align: center;
font-family: "Open Sans", sans-serif;
}
.iconText {
cursor: pointer;
}
}
@media screen and (min-width: 1000px) {
.sidebar {
display: inline-block !important;
background-color: #3f3f3f !important;
height: 15%;
position: absolute;
top: 1%;
left: 1%;
width: 46% !important;
border-right: 14px solid #3f3f3f !important;
}
.tittleSideBarMenu {
color: white;
text-align: center;
}
.iconMenu {
color: #d1d1d1;
font-size: 14px;
text-align: center;
margin-right: 20px;
margin-left: 20px;
}
.iconMenu:hover {
color: white;
}
.IconsMenu {
width: 520px;
display: inline-flex;
position: absolute;
left: 50%;
margin-left: -249px;
bottom: 10px;
height: 22px;
font-family: "Open Sans", sans-serif;
}
.iconText {
cursor: pointer;
}
}
.iconText:hover {
cursor: pointer;
}

View file

@ -1,41 +0,0 @@
body {
font-family: 'Open Sans', sans-serif;
font-size: 14px;
margin: 0;
padding: 0;
}
/** Main Layout rules */
.App { min-height: 100vh; }
.App {
display: flex;
flex: 1;
flex-direction: column;
}
.App .body {
display: flex;
flex: 1;
flex-direction: row;
}
.body .sidebar { order: -1; }
.body .content { flex: 1; }
.body .sidebar {
flex: 0 0 12em;
max-width: 12em;
}
.App .header { height: 4em; }
.App .footer { height: 4em; }
/** Text */
.text-red { color: red; }

View file

@ -1,50 +0,0 @@
{
"accent": "Red",
"avatarSize": 40,
"backgroundColor": "White",
"bubbleBackground": "White",
"bubbleBorder": "solid 1px #E6E6E6",
"bubbleBorderRadius": 2,
"bubbleFromUserBackground": "White",
"bubbleFromUserBorder": "solid 1px #E6E6E6",
"bubbleFromUserBorderRadius": 2,
"bubbleFromUserTextColor": "Black",
"bubbleImageHeight": 240,
"bubbleMaxWidth": 480,
"bubbleMinHeight": 40,
"bubbleMinWidth": 250,
"bubbleTextColor": "Black",
"hideSendBox": false,
"microphoneButtonColorOnDictate": "#F33",
"paddingRegular": 10,
"paddingWide": 20,
"sendBoxButtonColor": "#999",
"sendBoxButtonColorOnDisabled": "#CCC",
"sendBoxButtonColorOnFocus": "#333",
"sendBoxButtonColorOnHover": "#333",
"sendBoxHeight": 40,
"showSpokenText": false,
"suggestedActionBackground": "White",
"suggestedActionBorder": "solid 2px",
"suggestedActionBorderRadius": 0,
"suggestedActionDisabledBackground": "White",
"suggestedActionDisabledBorder": "solid 2px #E6E6E6",
"suggestedActionHeight": 40,
"transcriptOverlayButtonBackground": "rgba(0, 0, 0, .6)",
"transcriptOverlayButtonBackgroundOnFocus": "rgba(0, 0, 0, .8)",
"transcriptOverlayButtonBackgroundOnHover": "rgba(0, 0, 0, .8)",
"transcriptOverlayButtonColor": "White",
"transcriptOverlayButtonColorOnFocus": "White",
"transcriptOverlayButtonColorOnHover": "White",
"videoHeight": 270
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 B

Some files were not shown because too many files have changed in this diff Show more