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

1
.gitignore vendored
View file

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

47
.test-init.ts Normal file
View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
@ -39,6 +37,7 @@ import SwaggerClient from 'swagger-client';
import { spawn } from 'child_process';
import { CodeServices } from '../../gpt.gblib/services/CodeServices.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
/**
* Web Automation services of conversation to be called by BASIC.
@ -156,12 +155,8 @@ export class DebuggerService {
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
GBServer.globals.debuggers[botId].client = client;
const response = await client.apis.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
@ -429,7 +427,7 @@ export class DialogKeywords {
* @example TALK TOLIST (array,member)
*
*/
public async getToLst(pid, array, member) {
public async getToLst({pid, array, member}) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
if (!array) {
@ -1350,12 +1348,7 @@ export class DialogKeywords {
const conversation = min['apiConversations'][pid];
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
conversation.client = client;
const response = await client.apis.Conversations.Conversations_StartConversation();
conversation.conversationId = response.obj.conversationId;
@ -1368,7 +1361,7 @@ export class DialogKeywords {
const step = proc.step;
const min = GBServer.globals.minInstances.filter(p => p.instance.instanceId == proc.instanceId)[0];
const sec = new SecService();
const user = await sec.getUserFromId(min.instance.instanceId, proc.userId);
const user = GBServer.globals.users [proc.userId];
const params = user ? JSON.parse(user.params) : {};
return {
min,

View file

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

View file

@ -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;
@ -370,8 +361,9 @@ export class GBVMService extends GBService {
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';
@ -1169,7 +1151,6 @@ export class GBVMService extends GBService {
const s = new VMScript(code, { filename: scriptPath });
result = vm1.run(s);
});
})();
} else {
const runnerPath = urlJoin(
@ -1200,10 +1181,15 @@ export class GBVMService extends GBService {
} catch (error) {
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
}
}
public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any, executable: string, step = null) {
public static createProcessInfo(
user: GuaribasUser,
min: GBMinInstance,
channel: any,
executable: string,
step = null
) {
const pid = GBAdminService.getNumberIdentifier();
GBServer.globals.processes[pid] = {
pid: pid,

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
@ -35,13 +33,13 @@
import Path from 'path';
import { GBLog, GBMinInstance } from 'botlib';
import { DialogKeywords } from './DialogKeywords.js';
import sharp from 'sharp';
import joinImages from 'join-images-updated';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import urlJoin from 'url-join';
import { GBServer } from '../../../src/app.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import sharp from 'sharp';
/**
* Image processing services of conversation to be called by BASIC.

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ __ __ _ _ | ,_)(_) __ __ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(___/(_) (_)`\__/' |
| | | ( )_) | |
| (_) \__/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
@ -30,6 +28,7 @@
| |
\*****************************************************************************/
'use strict';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';

View file

@ -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}...`);
}

View file

@ -92,6 +92,7 @@ export class SystemKeywords {
}
public async append({ pid, args }) {
if (!args) return [];
let array = [].concat(...args);
return array.filter(function (item, pos) {
return item;

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |

View file

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

View file

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

View file

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

View file

@ -36,11 +36,11 @@
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
import { GBMinInstance, IGBDialog } from 'botlib';
import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings.js';
import { GBLogEx } from '../services/GBLogEx.js';
import { GBConfigService } from '../services/GBConfigService.js';
/**
* Dialog for Welcoming people.
@ -65,7 +65,7 @@ export class WelcomeDialog extends IGBDialog {
async step => {
if (
GBServer.globals.entryPointDialog !== null &&
min.instance.botId === process.env.BOT_ID &&
min.instance.botId === GBConfigService.get('BOT_ID') &&
step.context.activity.channelId === 'webchat'
) {
return step.replaceDialog(GBServer.globals.entryPointDialog);

View file

@ -42,7 +42,7 @@ import * as en from 'dotenv-extended';
*/
export class GBConfigService {
public static getBoolean(value: string): boolean {
return (this.get(value) as unknown) as boolean;
return this.get(value) as unknown as boolean;
}
public static getServerPort(): string {
if (process.env.PORT) {
@ -79,13 +79,19 @@ export class GBConfigService {
public static get(key: string): string | undefined {
let value = GBConfigService.tryGet(key);
if (value === undefined) {
if (!value) {
switch (key) {
case 'STORAGE_NAME':
value = null;
break;
case 'CLOUD_USERNAME':
value = undefined;
break;
case 'STORAGE_LIBRARY':
value = `${process.env.HOME}/gbpackages`;
break;
case 'BOT_ID':
value = undefined;
value = 'default';
break;
case 'CLOUD_PASSWORD':
value = undefined;
@ -103,10 +109,10 @@ export class GBConfigService {
value = undefined;
break;
case 'STORAGE_DIALECT':
value = undefined;
value = 'sqlite';
break;
case 'STORAGE_FILE':
value = './guaribas.sqlite';
value = './data.db';
break;
case 'GBKB_AUTO_DEPLOY':
value = false;
@ -160,7 +166,7 @@ export class GBConfigService {
value = true;
break;
case 'BOT_URL':
value = undefined;
value = 'http://localhost:4242';
break;
case 'STORAGE_SERVER':
value = undefined;

View file

@ -341,6 +341,7 @@ export class GBConversationalService {
}
public async sendEvent(min: GBMinInstance, step: GBDialogStep, name: string, value: Object): Promise<any> {
if (step.context.activity.channelId !== 'msteams' && step.context.activity.channelId !== 'omnichannel') {
GBLogEx.info(
min,

View file

@ -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';
@ -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,7 +672,6 @@ 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}'...`);
@ -676,19 +679,19 @@ ENDPOINT_UPDATE=true
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,14 +750,11 @@ 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);
params = typeof instance.params === 'object' ? instance.params : JSON.parse(instance.params);
params = GBUtil.caseInsensitive(params);
value = params ? params[name] : defaultValue;
}
@ -769,7 +767,6 @@ ENDPOINT_UPDATE=true
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
});
}
}
}
}

View file

@ -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 = [];

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ _ _ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/ \ /`\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| |*| |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |

View file

@ -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.
@ -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 = {
@ -1699,16 +1723,8 @@ export class GBMinService {
}
};
GBServer.globals.server.apiServer = createKoaHttpServer(
GBVMService.API_PORT,
getRemoteId, { prefix: `api/v3` });
createRpcServer(
proxies,
GBServer.globals.server.apiServer,
opts
);
GBServer.globals.server.apiServer = createKoaHttpServer(GBVMService.API_PORT, getRemoteId, { prefix: `api/v3` });
createRpcServer(proxies, GBServer.globals.server.apiServer, opts);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,7 +0,0 @@
{
"version": "1.0.0",
"description": "Default General Bots theme.",
"authors": "pragmatismo.cloud",
"license": "AGPL-3.0"
}

View file

@ -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>

File diff suppressed because one or more lines are too long

View 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 => {

View file

@ -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" }}

View file

@ -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>

View file

@ -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(

View file

@ -1,11 +1,9 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__,\| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
@ -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'

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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`;

View file

@ -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;
}

View file

@ -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,11 +122,10 @@ 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) {
@ -131,7 +135,7 @@ export class GBServer {
}
if (!bypass) {
GBLogEx.error(0,`GBREJECTION: ${GBUtil.toYAML(err)} ${GBUtil.toYAML(p)}`);
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) {

View file

@ -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
View 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);

View 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&currencycode=" + 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

View 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 .
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

View file

@ -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

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