Merge branch 'main' of https://github.com/GeneralBots/BotServer
1
.gitignore
vendored
|
@ -29,3 +29,4 @@ package-lock.json
|
|||
yarn-lock.json
|
||||
logo.svg
|
||||
screenshot.png
|
||||
data.db
|
||||
|
|
47
.test-init.ts
Normal 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}};
|
||||
}
|
2
boot.mjs
|
@ -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();
|
||||
|
|
10
package.json
|
@ -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",
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
/*****************************************************************************\
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
|
@ -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++;
|
||||
if (char == last) counter++;
|
||||
else {
|
||||
counter = 0;
|
||||
last = char;
|
||||
}
|
||||
if (counter < r)
|
||||
res += 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> {}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| 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 |
|
||||
|
@ -19,13 +17,13 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| 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;
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| 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 |
|
||||
|
@ -19,13 +17,13 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| 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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
});
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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`, {
|
||||
ORDER BY table_name ASC`,
|
||||
{
|
||||
type: QueryTypes.RAW
|
||||
})[0]
|
||||
}
|
||||
else if (con.storageDriver === 'mariadb') {
|
||||
)[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,
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| 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 |
|
||||
|
@ -19,13 +17,13 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| 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.
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ __ __ _ _ | ,_)(_) __ __ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(___/(_) (_)`\__/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \__/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| 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 |
|
||||
|
@ -19,17 +17,18 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| 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';
|
||||
|
|
|
@ -47,15 +47,14 @@ 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];
|
||||
|
@ -69,11 +68,9 @@ export class ScheduleServices extends GBService {
|
|||
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}...`);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| 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 |
|
||||
|
@ -19,13 +17,13 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| |
|
||||
\*****************************************************************************/
|
||||
|
|
16
packages/basic.gblib/tests/DialogKeywords.test.ts
Normal 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");
|
||||
});
|
43
packages/basic.gblib/tests/GBVMService.test.ts
Normal 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"
|
||||
}});
|
||||
|
||||
});
|
36
packages/basic.gblib/tests/SystemKeywords.test.ts
Normal 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'
|
||||
}
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}.`);
|
||||
}
|
||||
|
@ -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,14 +520,7 @@ 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,
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +222,7 @@ export class GBDeployer implements IGBDeployer {
|
|||
const instance = await this.importer.createBotInstance(botId);
|
||||
const bootInstance = GBServer.globals.bootInstance;
|
||||
|
||||
if (GBConfigService.get('STORAGE_NAME')) {
|
||||
// Gets the access token to perform service operations.
|
||||
|
||||
const accessToken = await (GBServer.globals.minBoot.adminService as any)['acquireElevatedToken'](
|
||||
|
@ -231,11 +234,12 @@ export class GBDeployer implements IGBDeployer {
|
|||
|
||||
const service = await AzureDeployerService.createInstance(this);
|
||||
const application = await service.createApplication(accessToken, botId);
|
||||
|
||||
// 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.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 = [];
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ _ _ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/ \ /`\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| |*| |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
|
||||
| Licensed under the AGPL-3.0. |
|
||||
|
|
|
@ -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,9 +424,8 @@ 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'];
|
||||
|
||||
|
@ -453,10 +450,18 @@ export class GBMinService {
|
|||
whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
|
||||
}
|
||||
|
||||
if (whatsAppDirectLine) {
|
||||
await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
|
||||
}).bind(min);
|
||||
}
|
||||
})
|
||||
.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,7 +639,8 @@ export class GBMinService {
|
|||
min.instance.authenticatorTenant,
|
||||
'/oauth2/authorize'
|
||||
);
|
||||
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId
|
||||
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,14 +678,15 @@ export class GBMinService {
|
|||
theme = `default.gbtheme`;
|
||||
}
|
||||
|
||||
res.send(
|
||||
JSON.stringify({
|
||||
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,
|
||||
webchatToken: webchatTokenContainer.token,
|
||||
speechToken: speechToken,
|
||||
conversationId: webchatTokenContainer.conversationId,
|
||||
authenticatorTenant: instance.authenticatorTenant,
|
||||
authenticatorClientId: instance.marketplaceId,
|
||||
paramLogoImageUrl: this.core.getParam(instance, 'Logo Image Url', null),
|
||||
|
@ -688,14 +694,20 @@ export class GBMinService {
|
|||
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),
|
||||
logo: logo,
|
||||
color1: this.core.getParam(instance, 'Color1', null),
|
||||
color2: this.core.getParam(instance, 'Color2', 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,7 +1185,8 @@ 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);
|
||||
|
||||
|
@ -1170,7 +1201,17 @@ export class GBMinService {
|
|||
};
|
||||
|
||||
try {
|
||||
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,7 +1592,8 @@ 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(
|
||||
|
@ -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 => {
|
||||
GBServer.globals.server.apiServer.close(cb => {
|
||||
resolve(true);
|
||||
GBLogEx.info(0, 'Reloading General Bots API...');
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
});
|
||||
} 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);
|
||||
}
|
||||
|
||||
}
|
360
packages/core.gbapp/services/router/bridge.ts
Normal 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);
|
||||
};
|
66
packages/core.gbapp/services/router/types.ts
Normal 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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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!"
|
|
@ -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
|
|
@ -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
|
|
@ -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."
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -1,3 +0,0 @@
|
|||
Últimas notícias
|
||||
Contato
|
||||
Ofertas
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"botId":"pragmatismo-ai-prd",
|
||||
"version": "1.0.0",
|
||||
"description": "Bot pragmatismo.",
|
||||
"license": "Private"
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 2 KiB |
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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; }
|
|
@ -1,8 +0,0 @@
|
|||
.footer {
|
||||
align-items: center;
|
||||
background-color: #450a64;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-container { color: white; }
|
|
@ -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%;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
.media {
|
||||
margin-top: 20px;
|
||||
height: 280px !important;
|
||||
width: 200px !important;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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; }
|
|
@ -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
|
||||
}
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 958 B |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 247 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"version": "1.0.0",
|
||||
"description": "Default General Bots theme.",
|
||||
"authors": "pragmatismo.cloud",
|
||||
"license": "AGPL-3.0"
|
||||
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
<!--
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
/*****************************************************************************\
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
|
||||
| Licensed under the AGPL-3.0. |
|
||||
|
@ -19,7 +18,7 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| |
|
||||
|
@ -28,6 +27,7 @@
|
|||
| trademark license. Therefore any rights, title and interest in |
|
||||
| our trademarks remain entirely with us. |
|
||||
| |
|
||||
\*****************************************************************************/
|
||||
-->
|
||||
|
||||
<!doctype html>
|
||||
|
@ -48,7 +48,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="/themes/{theme}/css/App.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/themes/{theme}/css/SideBarMenu.css" />
|
||||
|
||||
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
|
||||
<script src="./js/webchat.js"></script>
|
||||
<title>{title} - General Bots Community Edition</title>
|
||||
|
||||
<style>
|
||||
|
|
2
packages/default.gbui/public/js/webchat.js
Normal file
|
@ -168,9 +168,18 @@ class GBUIApp extends React.Component {
|
|||
let _this_ = this;
|
||||
window['botchatDebug'] = true;
|
||||
|
||||
const line = new DirectLine({
|
||||
const line = instanceClient.webchatToken ?
|
||||
new DirectLine({
|
||||
token: instanceClient.webchatToken
|
||||
}):
|
||||
new DirectLine({
|
||||
domain: instanceClient.domain,
|
||||
secret: null,
|
||||
token: null,
|
||||
webSocket: false // defaults to true
|
||||
});
|
||||
|
||||
;
|
||||
_this_.setState({ line: line });
|
||||
|
||||
line.connectionStatus$.subscribe(connectionStatus => {
|
||||
|
|
|
@ -36,6 +36,7 @@ class ChatPane extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<Chat
|
||||
|
||||
ref={(chat) => { this.chat = chat; }}
|
||||
botConnection={this.props.botConnection}
|
||||
user={{ id: "webUser@gb", name: "You" }}
|
||||
|
|
|
@ -49,7 +49,7 @@ class SideBarMenu extends React.Component {
|
|||
<div className="tittleSideBarMenu">
|
||||
<img
|
||||
className="pragmatismoLogo"
|
||||
src={this.props.instance.botId + "/cache/" + this.props.instance.logo}
|
||||
src={this.props.instance.logo}
|
||||
alt="General Bots Logo" />
|
||||
|
||||
</div>
|
||||
|
|
|
@ -36,6 +36,7 @@ import { GBLog, GBMinInstance, GBService } from 'botlib';
|
|||
import { GBServer } from '../../../src/app.js';
|
||||
import { SecService } from '../../security.gbapp/services/SecService.js';
|
||||
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
|
||||
import { GBUtil } from '../../../src/util.js';
|
||||
|
||||
/**
|
||||
* Support for Google Chat.
|
||||
|
@ -90,10 +91,7 @@ export class GoogleChatDirectLine extends GBService {
|
|||
}
|
||||
|
||||
public async setup (setUrl) {
|
||||
this.directLineClient = new Swagger({
|
||||
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
|
||||
usePromise: true
|
||||
});
|
||||
this.directLineClient = await GBUtil.getDirectLineClient(this.min);
|
||||
const client = await this.directLineClient;
|
||||
|
||||
client.clientAuthorizations.add(
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
/*****************************************************************************\
|
||||
| ( )_ _ |
|
||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
|
||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||
| | | ( )_) | |
|
||||
| (_) \___/' |
|
||||
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
||||
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
||||
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
||||
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
||||
| |
|
||||
| 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 |
|
||||
|
@ -19,13 +17,13 @@
|
|||
| 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 |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| |
|
||||
\*****************************************************************************/
|
||||
|
@ -33,7 +31,8 @@
|
|||
'use strict';
|
||||
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { OpenAIClient } from '@azure/openai';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
import { AzureKeyCredential } from '@azure/core-auth';
|
||||
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords';
|
||||
import Path from 'path';
|
||||
|
@ -47,7 +46,6 @@ import { GBLogEx } from '../../core.gbapp/services/GBLogEx';
|
|||
* Image processing services of conversation to be called by BASIC.
|
||||
*/
|
||||
export class ImageServices {
|
||||
|
||||
public async getImageFromPrompt({ pid, prompt }) {
|
||||
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
|
||||
|
||||
|
@ -57,11 +55,12 @@ export class ImageServices {
|
|||
|
||||
if (azureOpenAIKey) {
|
||||
// Initialize the Azure OpenAI client
|
||||
const client = new OpenAIClient(azureOpenAIEndpoint, new AzureKeyCredential(azureOpenAIKey));
|
||||
|
||||
const client = new OpenAI({ apiKey: azureOpenAIKey, baseURL: azureOpenAIEndpoint });
|
||||
|
||||
// Make a request to the image generation endpoint
|
||||
|
||||
const response = await client.getImageGeneration(azureOpenAIImageModel, {
|
||||
const response = await client.images.generate({
|
||||
prompt: prompt,
|
||||
n: 1,
|
||||
size: '1024x1024'
|
||||
|
@ -77,7 +76,7 @@ export class ImageServices {
|
|||
|
||||
GBLogEx.info(min, `BASIC: DALL-E image generated at ${url}.`);
|
||||
|
||||
return {localName, url};
|
||||
return { localName, url };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -746,7 +746,7 @@ export class KBService implements IGBKBService {
|
|||
let doc = await loader.load();
|
||||
let content = doc[0].pageContent;
|
||||
|
||||
if (file.name.endsWith('zap.docx')){
|
||||
if (file.name.endsWith('zap.docx')) {
|
||||
await min.whatsAppDirectLine.createOrUpdateTemplate(min, file.name, content);
|
||||
}
|
||||
|
||||
|
@ -1218,7 +1218,7 @@ export class KBService implements IGBKBService {
|
|||
instance: IGBInstance
|
||||
): Promise<any> {
|
||||
let subjectsLoaded;
|
||||
if (menuFile) {
|
||||
if (Fs.existsSync(menuFile)) {
|
||||
// Loads menu.xlsx and finds worksheet.
|
||||
|
||||
const workbook = new Excel.Workbook();
|
||||
|
@ -1369,10 +1369,12 @@ export class KBService implements IGBKBService {
|
|||
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
|
||||
await this.importKbPackage(min, localPath, p, instance);
|
||||
GBDeployer.mountGBKBAssets(packageName, min.botId, localPath);
|
||||
|
||||
if (GBConfigService.get('STORAGE_NAME')) {
|
||||
const service = await AzureDeployerService.createInstance(deployer);
|
||||
const searchIndex = instance.searchIndex ? instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
|
||||
await deployer.rebuildIndex(instance, service.getKBSearchSchema(searchIndex));
|
||||
|
||||
}
|
||||
min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
|
||||
await KBService.RefreshNER(min);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as Fs from 'fs';
|
|||
import mkdirp from 'mkdirp';
|
||||
import urlJoin from 'url-join';
|
||||
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
|
||||
import { GBServer } from '../../../src/app.js';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -52,8 +53,11 @@ export class SecService extends GBService {
|
|||
user.displayName = displayName;
|
||||
user.email = email;
|
||||
user.defaultChannel = channelName;
|
||||
|
||||
return await user.save();
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
if(user.changed()){
|
||||
await user.save();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,6 +78,7 @@ export class SecService extends GBService {
|
|||
const user = await GuaribasUser.findOne(options);
|
||||
|
||||
user.conversationReference = conversationReference;
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
await user.save();
|
||||
}
|
||||
|
||||
|
@ -82,6 +87,7 @@ export class SecService extends GBService {
|
|||
const user = await GuaribasUser.findOne(options);
|
||||
|
||||
user.conversationReference = conversationReference;
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
await user.save();
|
||||
}
|
||||
|
||||
|
@ -92,7 +98,7 @@ export class SecService extends GBService {
|
|||
}
|
||||
});
|
||||
user.locale = locale;
|
||||
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
return await user.save();
|
||||
}
|
||||
|
||||
|
@ -103,7 +109,7 @@ export class SecService extends GBService {
|
|||
}
|
||||
});
|
||||
user.hearOnDialog = dialogName;
|
||||
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
return await user.save();
|
||||
}
|
||||
|
||||
|
@ -114,7 +120,7 @@ export class SecService extends GBService {
|
|||
}
|
||||
});
|
||||
user.instanceId = instanceId;
|
||||
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
return await user.save();
|
||||
}
|
||||
|
||||
|
@ -160,9 +166,11 @@ export class SecService extends GBService {
|
|||
agent.instanceId = user.instanceId;
|
||||
agent.agentMode = 'self';
|
||||
agent.agentSystemId = null;
|
||||
GBServer.globals.users [agent.userId] = user;
|
||||
await agent.save();
|
||||
}
|
||||
|
||||
GBServer.globals.users [user.userId] = user;
|
||||
await user.save();
|
||||
|
||||
return user;
|
||||
|
@ -306,6 +314,7 @@ export class SecService extends GBService {
|
|||
}
|
||||
obj[name] = value;
|
||||
user.params = JSON.stringify(obj);
|
||||
GBServer.globals.users [userId] = user;
|
||||
return await user.save();
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
import mime from 'mime-types';
|
||||
import urlJoin from 'url-join';
|
||||
import SwaggerClient from 'swagger-client';
|
||||
import Path from 'path';
|
||||
import Fs from 'fs';
|
||||
import { GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib';
|
||||
|
@ -46,7 +45,7 @@ import qrcode from 'qrcode-terminal';
|
|||
import express from 'express';
|
||||
import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
|
||||
import pkg from 'whatsapp-web.js';
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import fetch from 'node-fetch';
|
||||
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
|
||||
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
|
||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
|
||||
|
@ -85,12 +84,10 @@ export class WhatsappDirectLine extends GBService {
|
|||
public botId: string;
|
||||
public botNumber: string;
|
||||
public min: GBMinInstance;
|
||||
private directLineSecret: string;
|
||||
private locale: string = 'pt-BR';
|
||||
provider: any;
|
||||
INSTANCE_URL = 'https://api.maytapi.com/api';
|
||||
private customClient: any;
|
||||
private groupId;
|
||||
|
||||
constructor(
|
||||
min: GBMinInstance,
|
||||
|
@ -105,12 +102,11 @@ export class WhatsappDirectLine extends GBService {
|
|||
|
||||
this.min = min;
|
||||
this.botId = botId;
|
||||
this.directLineSecret = directLineSecret;
|
||||
this.whatsappServiceKey = whatsappServiceKey;
|
||||
this.whatsappServiceNumber = whatsappServiceNumber;
|
||||
this.whatsappServiceUrl = whatsappServiceUrl;
|
||||
this.provider = whatsappServiceKey === 'internal' ? 'GeneralBots' : 'meta';
|
||||
this.groupId = groupId;
|
||||
|
||||
}
|
||||
|
||||
public static async asyncForEach(array, callback) {
|
||||
|
@ -120,18 +116,8 @@ export class WhatsappDirectLine extends GBService {
|
|||
}
|
||||
|
||||
public async setup(setUrl: boolean) {
|
||||
const client = await new SwaggerClient({
|
||||
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
|
||||
requestInterceptor: req => {
|
||||
req.headers['Authorization'] = `Bearer ${this.min.instance.webchatKey}`;
|
||||
}
|
||||
});
|
||||
|
||||
this.directLineClient = client;
|
||||
|
||||
// Warms up MSBF.
|
||||
|
||||
await client.apis.Conversations.Conversations_StartConversation();
|
||||
this.directLineClient = GBUtil.getDirectLineClient(this.min);
|
||||
|
||||
let url: string;
|
||||
let options: any;
|
||||
|
@ -822,29 +808,25 @@ export class WhatsappDirectLine extends GBService {
|
|||
const templateExists = templates.data.find(template => template.name === name);
|
||||
|
||||
if (templateExists) {
|
||||
// Step 2: Update the template
|
||||
const updateTemplateEndpoint = `${baseUrl}/${templateExists.id}`;
|
||||
// // Step 2: Update the template
|
||||
// const updateTemplateEndpoint = `${baseUrl}/${templateExists.id}`;
|
||||
|
||||
// Removes the first HEADER element.
|
||||
// const updateResponse = await fetch(updateTemplateEndpoint, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${accessToken}`,
|
||||
// 'Content-Type': 'application/json'
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// components: data.components
|
||||
// })
|
||||
// });
|
||||
|
||||
data.components.shift();
|
||||
// if (!updateResponse.ok) {
|
||||
// throw new Error(`Failed to update template: ${name} ${await updateResponse.text()}`);
|
||||
// }
|
||||
|
||||
const updateResponse = await fetch(updateTemplateEndpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
components: data.components
|
||||
})
|
||||
});
|
||||
|
||||
if (!updateResponse.ok) {
|
||||
throw new Error(`Failed to update template: ${name} ${await updateResponse.text()}`);
|
||||
}
|
||||
|
||||
GBLogEx.info(min, `Template updated: ${name}`);
|
||||
GBLogEx.info(min, `Template update skiped: ${name}`);
|
||||
} else {
|
||||
// Step 3: Create the template
|
||||
const createTemplateEndpoint = `${baseUrl}/${businessAccountId}/message_templates`;
|
||||
|
|
|
@ -58,10 +58,12 @@ export class RootData {
|
|||
public debugConversationId: any; // Used to self-message during debug.
|
||||
public debuggers: any[]; // Client of attached Debugger instances by botId.
|
||||
public chatGPT: any; // ChatGPT API handle (shared Browser).
|
||||
public users: any[]; // Loaded users.
|
||||
public dk;
|
||||
public wa;
|
||||
public sys;
|
||||
public dbg;
|
||||
public img;
|
||||
indexSemaphore: any;
|
||||
public webDavServer;
|
||||
}
|
||||
|
|
46
src/app.ts
|
@ -40,6 +40,7 @@ import bodyParser from 'body-parser';
|
|||
import { GBLog, GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
|
||||
import child_process from 'child_process';
|
||||
import express from 'express';
|
||||
import { v2 as webdav } from 'webdav-server';
|
||||
import fs from 'fs';
|
||||
import http from 'http';
|
||||
import httpProxy from 'http-proxy';
|
||||
|
@ -89,6 +90,7 @@ export class GBServer {
|
|||
this.initEndpointsDocs(server);
|
||||
|
||||
GBServer.globals.server = server;
|
||||
|
||||
GBServer.globals.httpsServer = null;
|
||||
GBServer.globals.webSessions = {};
|
||||
GBServer.globals.processes = {};
|
||||
|
@ -100,7 +102,10 @@ export class GBServer {
|
|||
GBServer.globals.wwwroot = null;
|
||||
GBServer.globals.entryPointDialog = null;
|
||||
GBServer.globals.debuggers = [];
|
||||
GBServer.globals.users = [];
|
||||
GBServer.globals.indexSemaphore = new Mutex();
|
||||
GBServer.globals.webDavServer = new webdav.WebDAVServer();
|
||||
GBServer.globals.webDavServer.start();
|
||||
|
||||
server.use(bodyParser.json());
|
||||
server.use(bodyParser.json({ limit: '1mb' }));
|
||||
|
@ -117,21 +122,20 @@ export class GBServer {
|
|||
});
|
||||
|
||||
process.on('uncaughtException', (err, p) => {
|
||||
GBLogEx.error(0, `GBEXCEPTION: ${GBUtil.toYAML(err)} ${GBUtil.toYAML(p)}`);
|
||||
GBLogEx.error(0, `GBEXCEPTION: ${GBUtil.toYAML(err)}`);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (err, p) => {
|
||||
|
||||
let bypass = false;
|
||||
let res = err['response'];
|
||||
if (res) {
|
||||
if (res?.body?.error?.message?.startsWith('Failed to send activity: bot timed out')){
|
||||
if (res?.body?.error?.message?.startsWith('Failed to send activity: bot timed out')) {
|
||||
bypass = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bypass){
|
||||
GBLogEx.error(0,`GBREJECTION: ${GBUtil.toYAML(err)} ${GBUtil.toYAML(p)}`);
|
||||
if (!bypass) {
|
||||
GBLogEx.error(0, `GBREJECTION: ${GBUtil.toYAML(err)}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -147,6 +151,7 @@ export class GBServer {
|
|||
(async () => {
|
||||
try {
|
||||
GBLogEx.info(0, `Now accepting connections on ${port}...`);
|
||||
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
|
||||
// Reads basic configuration, initialize minimal services.
|
||||
|
@ -175,12 +180,12 @@ export class GBServer {
|
|||
|
||||
// Creates a boot instance or load it from storage.
|
||||
|
||||
let runOnce = false;
|
||||
if (GBConfigService.get('STORAGE_SERVER')) {
|
||||
azureDeployer = await AzureDeployerService.createInstance(deployer);
|
||||
await core.initStorage();
|
||||
} else if (!GBConfigService.get('STORAGE_NAME')) {
|
||||
await core.initStorage();
|
||||
} else {
|
||||
runOnce = true;
|
||||
[GBServer.globals.bootInstance, azureDeployer] = await core['createBootInstanceEx'](
|
||||
core,
|
||||
null,
|
||||
|
@ -188,10 +193,9 @@ export class GBServer {
|
|||
deployer,
|
||||
GBConfigService.get('FREE_TIER')
|
||||
);
|
||||
await core.saveInstance(GBServer.globals.bootInstance);
|
||||
}
|
||||
|
||||
core.ensureAdminIsSecured();
|
||||
|
||||
// Deploys system and user packages.
|
||||
|
||||
GBLogEx.info(0, `Deploying System packages...`);
|
||||
|
@ -201,10 +205,6 @@ export class GBServer {
|
|||
await deployer.deployPackages(core, server, GBServer.globals.appPackages);
|
||||
await core.syncDatabaseStructure();
|
||||
|
||||
if (runOnce) {
|
||||
await core.saveInstance(GBServer.globals.bootInstance);
|
||||
}
|
||||
|
||||
// Deployment of local applications for the first time.
|
||||
|
||||
if (GBConfigService.get('DISABLE_WEB') !== 'true') {
|
||||
|
@ -219,6 +219,7 @@ export class GBServer {
|
|||
);
|
||||
|
||||
if (instances.length === 0) {
|
||||
if (GBConfigService.get('STORAGE_NAME')) {
|
||||
const instance = await importer.importIfNotExistsBotPackage(
|
||||
GBConfigService.get('BOT_ID'),
|
||||
'boot.gbot',
|
||||
|
@ -229,21 +230,28 @@ export class GBServer {
|
|||
instances.push(instance);
|
||||
GBServer.globals.minBoot.instance = instances[0];
|
||||
GBServer.globals.bootInstance = instances[0];
|
||||
await deployer.deployBotFull(instance, GBServer.globals.publicAddress);
|
||||
await deployer.deployBotOnAzure(instance, GBServer.globals.publicAddress);
|
||||
|
||||
// Runs the search even with empty content to create structure.
|
||||
|
||||
await azureDeployer['runSearch'](instance);
|
||||
}
|
||||
|
||||
GBServer.globals.bootInstance = instances[0];
|
||||
|
||||
// Builds minimal service infrastructure.
|
||||
}
|
||||
|
||||
const conversationalService: GBConversationalService = new GBConversationalService(core);
|
||||
const adminService: GBAdminService = new GBAdminService(core);
|
||||
const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer);
|
||||
GBServer.globals.minService = minService;
|
||||
|
||||
// Just sync if not using LOAD_ONLY.
|
||||
|
||||
if (!GBConfigService.get('STORAGE_NAME') && !process.env.LOAD_ONLY) {
|
||||
await core['ensureFolders'](instances, deployer);
|
||||
}
|
||||
GBServer.globals.bootInstance = instances[0];
|
||||
|
||||
// Builds minimal service infrastructure.
|
||||
|
||||
await minService.buildMin(instances);
|
||||
|
||||
server.all('*', async (req, res, next) => {
|
||||
|
@ -279,6 +287,8 @@ export class GBServer {
|
|||
|
||||
GBLogEx.info(0, `The Bot Server is in RUNNING mode...`);
|
||||
|
||||
await minService.startSimpleTest(GBServer.globals.minBoot);
|
||||
|
||||
// Opens Navigator.
|
||||
|
||||
if (process.env.DEV_OPEN_BROWSER) {
|
||||
|
|
80
src/util.ts
|
@ -34,6 +34,10 @@
|
|||
|
||||
'use strict';
|
||||
import * as YAML from 'yaml';
|
||||
import SwaggerClient from 'swagger-client';
|
||||
import Fs from 'fs';
|
||||
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService.js';
|
||||
import path from 'path';
|
||||
|
||||
export class GBUtil {
|
||||
public static repeat(chr, count) {
|
||||
|
@ -64,10 +68,36 @@ export class GBUtil {
|
|||
return (value + GBUtil.repeat(pad, length)).substr(0, width);
|
||||
}
|
||||
|
||||
public static toYAML(json) {
|
||||
const doc = new YAML.Document();
|
||||
doc.contents = json;
|
||||
return doc.toString();
|
||||
public static async getDirectLineClient(min) {
|
||||
|
||||
let config = {
|
||||
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
|
||||
requestInterceptor: req => {
|
||||
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
|
||||
}
|
||||
};
|
||||
if (!GBConfigService.get('STORAGE_NAME')) {
|
||||
config['spec'].url = `http://127.0.0.1:${GBConfigService.getServerPort()}/api/messages/${min.botId}`,
|
||||
config['spec'].servers = [{ url: `http://127.0.0.1:${GBConfigService.getServerPort()}/api/messages/${min.botId}` }];
|
||||
config['spec'].openapi = '3.0.0';
|
||||
delete config['spec'].host;
|
||||
delete config['spec'].swagger;
|
||||
}
|
||||
|
||||
return await new SwaggerClient(config);
|
||||
}
|
||||
|
||||
public static toYAML(data) {
|
||||
const extractProps = obj => {
|
||||
return Object.getOwnPropertyNames(obj).reduce((acc, key) => {
|
||||
const value = obj[key];
|
||||
acc[key] = value && typeof value === 'object' && !Array.isArray(value) ? extractProps(value) : value;
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const extractedError = extractProps(data);
|
||||
return YAML.stringify(extractedError);
|
||||
}
|
||||
|
||||
public static sleep(ms) {
|
||||
|
@ -94,4 +124,46 @@ export class GBUtil {
|
|||
return createCaseInsensitiveProxy(listOrRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static copyIfNewerRecursive(src, dest) {
|
||||
if (!Fs.existsSync(src)) {
|
||||
console.error(`Source path "${src}" does not exist.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the source is a directory
|
||||
if (Fs.statSync(src).isDirectory()) {
|
||||
// Create the destination directory if it doesn't exist
|
||||
if (!Fs.existsSync(dest)) {
|
||||
Fs.mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
|
||||
// Read all files and directories in the source directory
|
||||
const entries = Fs.readdirSync(src);
|
||||
|
||||
for (let entry of entries) {
|
||||
const srcEntry = path.join(src, entry);
|
||||
const destEntry = path.join(dest, entry);
|
||||
|
||||
// Recursively copy each entry
|
||||
this.copyIfNewerRecursive(srcEntry, destEntry);
|
||||
}
|
||||
} else {
|
||||
// Source is a file, check if we need to copy it
|
||||
if (Fs.existsSync(dest)) {
|
||||
const srcStat = Fs.statSync(src);
|
||||
const destStat = Fs.statSync(dest);
|
||||
|
||||
// Copy only if the source file is newer than the destination file
|
||||
if (srcStat.mtime > destStat.mtime) {
|
||||
Fs.cpSync(src, dest, { force: true });
|
||||
}
|
||||
} else {
|
||||
// Destination file doesn't exist, so copy it
|
||||
Fs.cpSync(src, dest, { force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
21
src/webapp.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { GBConfigService } from "../packages/core.gbapp/services/GBConfigService";
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
|
||||
export function runUI() {
|
||||
|
||||
// Create the browser window.
|
||||
const win = new BrowserWindow({ width: 800, height: 600, title: 'General Bots Studio' });
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadURL(
|
||||
url.format({
|
||||
pathname: path.join(__dirname, `http://localhost:${GBConfigService.get('PORT')}`),
|
||||
protocol: 'file:',
|
||||
slashes: true
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
app.on('ready', runUI);
|
121
templates/api-client.gbai/api-client.gbdialog/partner-center.vbs
Normal file
|
@ -0,0 +1,121 @@
|
|||
REM SET SCHEDULE "1 * * * * *"
|
||||
|
||||
REM Obtém token do Partner Center via token do AD.
|
||||
|
||||
SET HEADER "return-client-request-id" AS "true"
|
||||
SET HEADER "Content-Type" As "application/x-www-form-urlencoded; charset=utf-8"
|
||||
|
||||
pcToken = POST "https://login.microsoftonline.com/" + tenantId + "/oauth2/token", "resource=https%3A%2F%2Fgraph.windows.net&client_id=" + clientId + "&client_secret=" + clientSecret + "&grant_type=client_credentials"
|
||||
|
||||
REM repara chamada de Billing.
|
||||
|
||||
SET HEADER "Authorization" AS "Bearer " + pcToken.access_token
|
||||
SET HEADER "MS-Contract-Version" AS "v1"
|
||||
SET HEADER "MS-CorrelationId" AS uuid()
|
||||
SET HEADER "MS-RequestId" AS uuid()
|
||||
SET HEADER "MS-PartnerCenter-Application" AS "General Bots"
|
||||
SET HEADER "X-Locale" AS "en-US"
|
||||
|
||||
REM Sincroniza Customers e Subscriptions
|
||||
|
||||
SET PAGE MODE "none"
|
||||
list = GET host + "/v1/customers?size=20000"
|
||||
MERGE "Customers" WITH list.items BY "Id"
|
||||
|
||||
FOR EACH item IN list
|
||||
subs = GET host + "/v1/customers/" + item.id + "/subscriptions"
|
||||
MERGE "Subscriptions" WITH subs.items BY "Id"
|
||||
END FOR
|
||||
|
||||
|
||||
REM Determina período.
|
||||
|
||||
IF today = dueDay THEN
|
||||
IF period = "previous" AND NOT CONTINUATION TOKEN THEN
|
||||
period = "current"
|
||||
ELSE
|
||||
period = "previous"
|
||||
END IF
|
||||
ELSE
|
||||
period = "current"
|
||||
END IF
|
||||
|
||||
|
||||
|
||||
REM Perform the call and loop through the billing items.
|
||||
|
||||
SET PAGE MODE "auto"
|
||||
list = GET host + "/v1/invoices/unbilled/lineitems?provider=onetime&invoicelineitemtype=usagelineitems¤cycode=" + currency + "&period=previous&idparceiro=" + idparceiro
|
||||
FOR EACH item IN list
|
||||
SAVE "Billing", item.alternateId, item.availabilityId, item.billableQuantity, item.billingFrequency, item.chargeEndDate, item.chargeStartDate, item.chargeType, item.currency, item.customerCountry, item.customerDomainName, item.customerId, item.customerName, item.effectiveUnitPrice, item.invoiceNumber, item.meterDescription, item.mpnId, item.orderDate, item.orderId, item.partnerId, item.pCToBCExchangeRate, item.pCToBCExchangeRateDate, item.priceAdjustmentDescription, item.pricingCurrency, item.productId, item.productName, item.publisherId, item.publisherName, item.quantity, item.resellerMpnId, item.reservationOrderId, item.skuId, item.skuName, item.subscriptionDescription, item.subscriptionId, item.subtotal, item.taxTotal, item.termAndBillingCycle, item.totalForCustomer, item.unitPrice, item.unitType
|
||||
END FOR
|
||||
|
||||
|
||||
TABLE Billing
|
||||
CustomerId Customers
|
||||
ResourceGroup string(200)
|
||||
ResourceUri string(1000)
|
||||
Tags string(max)
|
||||
AdditionalInfo string(max)
|
||||
ServiceInfo1 string(max)
|
||||
ServiceInfo2 string(max)
|
||||
CustomerCountry string(6)
|
||||
MpnId string(50)
|
||||
ResellerMpnId string(50)
|
||||
ChargeType string(200)
|
||||
UnitPrice* double
|
||||
Quantity* double
|
||||
UnitType string(max)
|
||||
BillingPreTaxTotal double
|
||||
BillingCurrency string(6)
|
||||
PricingPreTaxTotal double
|
||||
PricingCurrency string(6)
|
||||
EntitlementId string(50)
|
||||
EntitlementDescription string(400)
|
||||
PCToBCExchangeRate double
|
||||
PCToBCExchangeRateDate date
|
||||
EffectiveUnitPrice* double
|
||||
RateOfPartnerEarnedCredit double
|
||||
ConsumedService string(200)
|
||||
ResourceLocation string(100)
|
||||
MeterRegion string(100)
|
||||
PartnerId string(50)
|
||||
PartnerName string(400)
|
||||
CustomerName string(400)
|
||||
CustomerDomainName string(400)
|
||||
InvoiceNumber string(400)
|
||||
ProductId string(50)
|
||||
SkuId string(50)
|
||||
AvailabilityId string(50)
|
||||
SkuName string(200)
|
||||
ProductName string(400)
|
||||
PublisherName string(200)
|
||||
PublisherId string(200)
|
||||
SubscriptionId string(50)
|
||||
SubscriptionDescription string(400)
|
||||
ChargeStartDate* date
|
||||
ChargeEndDate* date
|
||||
UsageDate date
|
||||
MeterType string(400)
|
||||
MeterCategory string(100)
|
||||
MeterId string(50)
|
||||
MeterSubCategory string(100)
|
||||
MeterName string(200)
|
||||
UnitOfMeasure string(100)
|
||||
Reprocess boolean
|
||||
END TABLE
|
||||
|
||||
|
||||
TABLE Customers
|
||||
TenantId guid
|
||||
CompanyName string(100)
|
||||
Id guid
|
||||
END TABLE
|
||||
|
||||
|
||||
TABLE Subscriptions
|
||||
CustomerId Customers
|
||||
Id guid
|
||||
OfferName string(50)
|
||||
END TABLE
|
||||
|
79
templates/api-server.gbai/api.gbdialog/start.bas
Normal file
|
@ -0,0 +1,79 @@
|
|||
REM 302 / 1234
|
||||
PARAM barraca AS number LIKE Código da barraca
|
||||
PARAM operador AS number LIKE Código do operador
|
||||
DESCRIPTION Esta função (tool) nunca é chamada pelo GPT. É um WebService do GB.
|
||||
|
||||
REM Login como Garçom
|
||||
data = NEW OBJECT
|
||||
data.IdentificadorOperador = operador
|
||||
data.BarracaId = barraca
|
||||
login = POST "https://api.server.com.br/api/Operadores/Login", data
|
||||
SET HEADER "Authorization" AS login.accessToken
|
||||
|
||||
REM Obter o cardápio da Barraca - Utilizar o token recuperado acima.
|
||||
data = GET "https://api.server.com.br/api/Item/Barraca/${barraca}/Cliente"
|
||||
produtos = NEW ARRAY
|
||||
|
||||
FOR EACH item IN data[0].itens
|
||||
IF item.statusItem = "Ativo" THEN
|
||||
produto = NEW OBJECT
|
||||
produto.id = item.id
|
||||
produto.valor = item.valor
|
||||
produto.nome = item.produto.nome
|
||||
produto.detalhe = item.detalhe
|
||||
produto.acompanhamentos = item.gruposAcompanhamento
|
||||
|
||||
produtos.push(produto)
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
Você deve atuar como um chatbot que irá auxiliar o atendente de uma loja respeitando as seguintes regras:
|
||||
Sempre que o atendente fizer um pedido e deve incluir a mesa e o nome do cliente. Exemplo: Uma caipirinha de 400ml de Abacaxi para Rafael na mesa 10.
|
||||
Os pedidos são feitos com base nos produtos e acompanhamentos deste cardápio de produtos:
|
||||
${JSON.stringify (produtos)}.
|
||||
A cada pedido realizado, retorne JSON contendo o nome do produto, a mesa e uma lista de acompanhamentos com seus respectivos ids.
|
||||
Mantenha itensPedido com apenas um item e mantenha itemsAcompanhamento apenas com os acompanhamentos que foram especificados.
|
||||
ItensAcompanhamento deve conter a coleção de itens de acompanhamento do pedido, que é solicitado quando o pedido é feito, por exemplo: Caipirinha de Morango com Gelo, Açúcar e Limão, gerariam três elementos neste nó.
|
||||
|
||||
Segue o exemplo do JSON do Pedido, apague os itens e mande um com o pedido feito pela pessoa, é apenas um exemplo:
|
||||
{
|
||||
itensPedido: [
|
||||
{
|
||||
item: {
|
||||
id: 23872,
|
||||
valor: 20,
|
||||
nome: Caipirinha Limão
|
||||
},
|
||||
itensAcompanhamento: [
|
||||
{
|
||||
id: 0,
|
||||
valor: 0,
|
||||
quantidade: 1
|
||||
}
|
||||
],
|
||||
quantidade: 1,
|
||||
observacao: a
|
||||
},
|
||||
{
|
||||
item: {
|
||||
id: 25510,
|
||||
valor: 12,
|
||||
nome: Heineken Lata 350ml
|
||||
},
|
||||
itensAcompanhamento: [],
|
||||
quantidade: 1,
|
||||
observacao: nenhuma
|
||||
}
|
||||
],
|
||||
barracaId: ${barraca},
|
||||
usuarioId: ${operador},
|
||||
identificadorConta: Rafael,
|
||||
tipoEntregaId: 2,
|
||||
camposTipoEntrega: {
|
||||
Mesa: 5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END SYSTEM PROMPT
|
|
@ -0,0 +1,19 @@
|
|||
list = DIR "QCARobot.gbdrive"
|
||||
|
||||
FOR EACH item IN list
|
||||
TALK "Verificando: " + item.name
|
||||
DEBUG item
|
||||
oldDays = DATEDIFF date, item.modified, "day"
|
||||
|
||||
IF oldDays > 3 THEN
|
||||
TALK "O arquivo ${item.name} será arquivado, pois está expirado."
|
||||
blob = UPLOAD item
|
||||
TALK Upload para o Azure realizado.
|
||||
|
||||
SAVE "log.xlsx", "archived",today,now, item.path, item.name, item.size, item.modified, blob.md5
|
||||
REM DELETE item
|
||||
REM TALK Arquivo removido do SharePoint.
|
||||
ELSE
|
||||
TALK "O arquivo ${item.name} não precisa de arquivamento."
|
||||
END IF
|
||||
NEXT
|